From 5ba1c93a09a0b0eb26caddd024b3845206f2e702 Mon Sep 17 00:00:00 2001 From: yaojingang Date: Sat, 13 Jun 2026 19:01:44 +0800 Subject: [PATCH] chore: modularize meta skill report surfaces --- AGENTS.md | 87 ++++ registry/index.json | 2 +- registry/packages/yao-meta-skill.json | 6 +- reports/adoption_drift_report.json | 2 +- reports/compiled_targets.json | 64 ++- reports/context_budget.json | 6 +- reports/install_simulation.json | 2 +- reports/output_execution_runs.json | 20 +- reports/output_execution_runs.md | 20 +- reports/package_verification.json | 4 +- reports/package_verification.md | 2 +- reports/registry_audit.json | 8 +- reports/registry_audit.md | 4 +- reports/review-studio.html | 55 ++- reports/review-studio.json | 234 ++++++--- reports/security_trust_report.json | 84 +++- reports/security_trust_report.md | 14 +- reports/skill-overview.html | 6 +- reports/skill-overview.json | 34 +- reports/skill_atlas.json | 4 + reports/upgrade_check.json | 4 +- scripts/render_review_studio.py | 336 +++++-------- scripts/render_skill_overview.py | 625 +----------------------- scripts/review_studio_formatting.py | 133 ++++++ scripts/review_studio_layout.py | 259 ++++++++++ scripts/skill_report_layout.py | 653 ++++++++++++++++++++++++++ scripts/yao.py | 239 +--------- scripts/yao_cli_config.py | 235 +++++++++ skill-ir/examples/yao-meta-skill.json | 6 +- skill_atlas/catalog.json | 4 + tests/verify_review_studio.py | 23 + tests/verify_skill_overview.py | 28 ++ tests/verify_trust_check.py | 8 + tests/verify_yao_cli.py | 7 + 34 files changed, 1960 insertions(+), 1258 deletions(-) create mode 100644 AGENTS.md create mode 100644 scripts/review_studio_formatting.py create mode 100644 scripts/review_studio_layout.py create mode 100644 scripts/skill_report_layout.py create mode 100644 scripts/yao_cli_config.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..47d8fee --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# 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` +- 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 --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_skill_overview.py . +python3 scripts/render_review_studio.py . --output-html reports/review-studio.html --output-json reports/review-studio.json +``` + +Clean test-only scratch directories after verification with `rm -rf tests/tmp_*`. 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/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`: Review Studio static layout and CSS contract. +- `scripts/review_studio_formatting.py`: Review Studio dictionary-to-panel formatting and Chinese metric labels. +- `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. + +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/registry/index.json b/registry/index.json index 46e108d..e4bc9b5 100644 --- a/registry/index.json +++ b/registry/index.json @@ -15,7 +15,7 @@ "agent-skills-compatible" ], "package_metadata": "registry/packages/yao-meta-skill.json", - "package_sha256": "9f1d53dbaeb1c90c80d7ffbbc1d10473e50b8032acda68332fbae551bb246abe" + "package_sha256": "742dc905b0d221c4f336e741d168861edaf7b75e97fe1e89f7d454c1cabf928f" } ] } diff --git a/registry/packages/yao-meta-skill.json b/registry/packages/yao-meta-skill.json index 72a338c..4fa9e8c 100644 --- a/registry/packages/yao-meta-skill.json +++ b/registry/packages/yao-meta-skill.json @@ -15,8 +15,8 @@ "trust_level": "local", "license": "MIT", "checksums": { - "package_sha256": "9f1d53dbaeb1c90c80d7ffbbc1d10473e50b8032acda68332fbae551bb246abe", - "archive_sha256": "4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06" + "package_sha256": "742dc905b0d221c4f336e741d168861edaf7b75e97fe1e89f7d454c1cabf928f", + "archive_sha256": "7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9" }, "compatibility": { "openai": "pass", @@ -47,7 +47,7 @@ }, "distribution": { "archive_verified": true, - "archive_sha256": "4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06", + "archive_sha256": "7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9", "package_verification": "reports/package_verification.json", "install_simulated": true, "install_simulation": "reports/install_simulation.json" diff --git a/reports/adoption_drift_report.json b/reports/adoption_drift_report.json index 8575418..7f931d0 100644 --- a/reports/adoption_drift_report.json +++ b/reports/adoption_drift_report.json @@ -1,7 +1,7 @@ { "ok": true, "schema_version": "2.0", - "generated_at": "2026-06-13T09:57:51Z", + "generated_at": "2026-06-13T10:57:52Z", "skill_dir": ".", "privacy_contract": { "storage": "local-first", diff --git a/reports/compiled_targets.json b/reports/compiled_targets.json index 0f32d4d..fc07765 100644 --- a/reports/compiled_targets.json +++ b/reports/compiled_targets.json @@ -190,6 +190,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -197,6 +199,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -205,7 +208,8 @@ "scripts/upgrade_check.py", "scripts/validate_skill.py", "scripts/verify_package.py", - "scripts/yao.py" + "scripts/yao.py", + "scripts/yao_cli_config.py" ], "assets": [ "templates/basic_skill.md.j2", @@ -256,7 +260,7 @@ ], "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 } @@ -494,7 +498,7 @@ "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -505,7 +509,7 @@ }, "scripts": { "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -717,7 +721,7 @@ "strategy": "Ship the neutral source tree and expose OpenAI-facing interface metadata as a generated companion file.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -728,7 +732,7 @@ }, "scripts": { "strategy": "Keep scripts as local package resources; expose help-smoke and permission metadata for reviewer approval before execution.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -975,6 +979,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -982,6 +988,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -990,7 +997,8 @@ "scripts/upgrade_check.py", "scripts/validate_skill.py", "scripts/verify_package.py", - "scripts/yao.py" + "scripts/yao.py", + "scripts/yao_cli_config.py" ], "assets": [ "templates/basic_skill.md.j2", @@ -1041,7 +1049,7 @@ ], "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 } @@ -1279,7 +1287,7 @@ "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -1290,7 +1298,7 @@ }, "scripts": { "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -1502,7 +1510,7 @@ "strategy": "Preserve the source tree directly; write target notes in targets/claude/README.md.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -1513,7 +1521,7 @@ }, "scripts": { "strategy": "Scripts remain local package resources and must be reviewed through trust and permission reports before use.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -1760,6 +1768,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -1767,6 +1777,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -1775,7 +1786,8 @@ "scripts/upgrade_check.py", "scripts/validate_skill.py", "scripts/verify_package.py", - "scripts/yao.py" + "scripts/yao.py", + "scripts/yao_cli_config.py" ], "assets": [ "templates/basic_skill.md.j2", @@ -1826,7 +1838,7 @@ ], "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 } @@ -2064,7 +2076,7 @@ "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -2074,7 +2086,7 @@ }, "scripts": { "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -2280,7 +2292,7 @@ "strategy": "Preserve references, scripts, assets, evals, reports, and adapter metadata as relative package resources.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -2290,7 +2302,7 @@ }, "scripts": { "strategy": "Expose script and permission metadata for downstream clients or installers to enforce.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -2529,6 +2541,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -2536,6 +2550,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -2544,7 +2559,8 @@ "scripts/upgrade_check.py", "scripts/validate_skill.py", "scripts/verify_package.py", - "scripts/yao.py" + "scripts/yao.py", + "scripts/yao_cli_config.py" ], "assets": [ "templates/basic_skill.md.j2", @@ -2595,7 +2611,7 @@ ], "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 } @@ -2833,7 +2849,7 @@ "strategy": "Keep optional directories as relative resources next to SKILL.md.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -2844,7 +2860,7 @@ }, "scripts": { "strategy": "Scripts remain local optional resources and should advertise --help when executable.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { @@ -3049,7 +3065,7 @@ "strategy": "Keep optional directories as relative resources next to SKILL.md.", "counts": { "references": 32, - "scripts": 62, + "scripts": 66, "assets": 2, "reports": 41 }, @@ -3060,7 +3076,7 @@ }, "scripts": { "strategy": "Scripts remain local optional resources and should advertise --help when executable.", - "script_count": 62, + "script_count": 66, "help_smoke_failed_count": 0 }, "permissions": { diff --git a/reports/context_budget.json b/reports/context_budget.json index 4da126e..49e96a0 100644 --- a/reports/context_budget.json +++ b/reports/context_budget.json @@ -6,10 +6,10 @@ "context_budget_tier": "production", "context_budget_limit": 1000, "skill_body_tokens": 811, - "other_text_tokens": 864534, + "other_text_tokens": 869504, "estimated_initial_load_tokens": 987, - "estimated_total_text_tokens": 865345, - "relevant_file_count": 316, + "estimated_total_text_tokens": 870315, + "relevant_file_count": 327, "unused_resource_dirs": [], "quality_signal_points": 130, "quality_density": 131.7 diff --git a/reports/install_simulation.json b/reports/install_simulation.json index facee86..1d67799 100644 --- a/reports/install_simulation.json +++ b/reports/install_simulation.json @@ -8,7 +8,7 @@ "installed_skill_dir": "dist/install-simulation/simulate-yao-meta-skill/yao-meta-skill", "summary": { "archive_present": true, - "archive_entry_count": 483, + "archive_entry_count": 488, "archive_extracted": true, "entrypoint_loaded": true, "manifest_loaded": true, diff --git a/reports/output_execution_runs.json b/reports/output_execution_runs.json index 32b0660..6cb2055 100644 --- a/reports/output_execution_runs.json +++ b/reports/output_execution_runs.json @@ -34,7 +34,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.22, + "duration_ms": 23.11, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -62,7 +62,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.12, + "duration_ms": 23.56, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -85,7 +85,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.12, + "duration_ms": 24.17, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -113,7 +113,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 30.03, + "duration_ms": 23.84, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -136,7 +136,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.11, + "duration_ms": 22.51, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -164,7 +164,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 26.68, + "duration_ms": 26.03, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -187,7 +187,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.07, + "duration_ms": 28.48, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -214,7 +214,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 27.57, + "duration_ms": 23.17, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -237,7 +237,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 29.2, + "duration_ms": 23.94, "provider": "local-output-eval-runner", "model": "", "usage": { @@ -266,7 +266,7 @@ "execution_mode": "command", "model_executed": false, "command_executed": true, - "duration_ms": 26.94, + "duration_ms": 22.69, "provider": "local-output-eval-runner", "model": "", "usage": { diff --git a/reports/output_execution_runs.md b/reports/output_execution_runs.md index ac9d4c6..d2e3be4 100644 --- a/reports/output_execution_runs.md +++ b/reports/output_execution_runs.md @@ -23,16 +23,16 @@ Command runner evidence is present. This proves the eval harness executed an ext | Case | Variant | Mode | Model | Duration ms | Tokens | Score | Status | | --- | --- | --- | --- | ---: | ---: | ---: | --- | -| skill-package-contract | baseline | command | local-output-eval-runner | 27.22 | 33 | 0.0 | pass | -| skill-package-contract | with_skill | command | local-output-eval-runner | 27.12 | 73 | 100.0 | pass | -| output-eval-expectation | baseline | command | local-output-eval-runner | 27.12 | 36 | 0.0 | pass | -| output-eval-expectation | with_skill | command | local-output-eval-runner | 30.03 | 80 | 100.0 | pass | -| ir-before-packaging | baseline | command | local-output-eval-runner | 27.11 | 33 | 0.0 | pass | -| ir-before-packaging | with_skill | command | local-output-eval-runner | 26.68 | 80 | 100.0 | pass | -| near-neighbor-boundary | baseline | command | local-output-eval-runner | 27.07 | 36 | 0.0 | pass | -| near-neighbor-boundary | with_skill | command | local-output-eval-runner | 27.57 | 65 | 100.0 | pass | -| file-backed-governed-package | baseline | command | local-output-eval-runner | 29.2 | 37 | 0.0 | pass | -| file-backed-governed-package | with_skill | command | local-output-eval-runner | 26.94 | 98 | 100.0 | pass | +| skill-package-contract | baseline | command | local-output-eval-runner | 23.11 | 33 | 0.0 | pass | +| skill-package-contract | with_skill | command | local-output-eval-runner | 23.56 | 73 | 100.0 | pass | +| output-eval-expectation | baseline | command | local-output-eval-runner | 24.17 | 36 | 0.0 | pass | +| output-eval-expectation | with_skill | command | local-output-eval-runner | 23.84 | 80 | 100.0 | pass | +| ir-before-packaging | baseline | command | local-output-eval-runner | 22.51 | 33 | 0.0 | pass | +| ir-before-packaging | with_skill | command | local-output-eval-runner | 26.03 | 80 | 100.0 | pass | +| near-neighbor-boundary | baseline | command | local-output-eval-runner | 28.48 | 36 | 0.0 | pass | +| near-neighbor-boundary | with_skill | command | local-output-eval-runner | 23.17 | 65 | 100.0 | pass | +| file-backed-governed-package | baseline | command | local-output-eval-runner | 23.94 | 37 | 0.0 | pass | +| file-backed-governed-package | with_skill | command | local-output-eval-runner | 22.69 | 98 | 100.0 | pass | ## Next Fixes diff --git a/reports/package_verification.json b/reports/package_verification.json index 17be2de..8c66a4e 100644 --- a/reports/package_verification.json +++ b/reports/package_verification.json @@ -8,8 +8,8 @@ "target_count": 3, "adapter_count": 3, "archive_present": true, - "archive_sha256": "4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06", - "archive_entry_count": 483, + "archive_sha256": "7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9", + "archive_entry_count": 488, "failure_count": 0, "warning_count": 0 }, diff --git a/reports/package_verification.md b/reports/package_verification.md index c17f69c..71cfac0 100644 --- a/reports/package_verification.md +++ b/reports/package_verification.md @@ -4,7 +4,7 @@ - Package directory: `dist` - Targets: `3 / 3` adapters present - Archive present: `True` -- Archive SHA256: `4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06` +- Archive SHA256: `7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9` - Failures: `0` - Warnings: `0` diff --git a/reports/registry_audit.json b/reports/registry_audit.json index 6f6525d..c8736dd 100644 --- a/reports/registry_audit.json +++ b/reports/registry_audit.json @@ -20,8 +20,8 @@ "trust_level": "local", "license": "MIT", "checksums": { - "package_sha256": "9f1d53dbaeb1c90c80d7ffbbc1d10473e50b8032acda68332fbae551bb246abe", - "archive_sha256": "4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06" + "package_sha256": "742dc905b0d221c4f336e741d168861edaf7b75e97fe1e89f7d454c1cabf928f", + "archive_sha256": "7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9" }, "compatibility": { "openai": "pass", @@ -52,7 +52,7 @@ }, "distribution": { "archive_verified": true, - "archive_sha256": "4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06", + "archive_sha256": "7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9", "package_verification": "reports/package_verification.json", "install_simulated": true, "install_simulation": "reports/install_simulation.json" @@ -76,7 +76,7 @@ "agent-skills-compatible" ], "package_metadata": "registry/packages/yao-meta-skill.json", - "package_sha256": "9f1d53dbaeb1c90c80d7ffbbc1d10473e50b8032acda68332fbae551bb246abe" + "package_sha256": "742dc905b0d221c4f336e741d168861edaf7b75e97fe1e89f7d454c1cabf928f" } ] }, diff --git a/reports/registry_audit.md b/reports/registry_audit.md index 6468aa9..eee4c66 100644 --- a/reports/registry_audit.md +++ b/reports/registry_audit.md @@ -6,8 +6,8 @@ - Maturity: `governed` - Owner: `Yao Team` - License: `MIT` -- Package SHA256: `9f1d53dbaeb1c90c80d7ffbbc1d10473e50b8032acda68332fbae551bb246abe` -- Archive SHA256: `4d1227dc0996a97aa4987761d46081391fe15b36362e19765e1e7aa8e5d41c06` +- Package SHA256: `742dc905b0d221c4f336e741d168861edaf7b75e97fe1e89f7d454c1cabf928f` +- Archive SHA256: `7bc83805501d24cf8e5932ef76e0454100398d5a73652e7440be47984d0cf4a9` - Install simulated: `True` ## Compatibility diff --git a/reports/review-studio.html b/reports/review-studio.html index 15be5d2..5382095 100644 --- a/reports/review-studio.html +++ b/reports/review-studio.html @@ -5,7 +5,7 @@ Yao Meta Skill Review Studio 2.0 @@ -1383,17 +1261,17 @@ def render_html(report: dict[str, Any]) -> str:

触发实验

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

-

组合治理

{html.escape(str(atlas_summary))}

+

组合治理

{atlas_panel}
-

输出实验

{html.escape(str(output_summary))}

-

执行证据

{html.escape(str(output_execution_summary or 'output execution report missing'))}

+

输出实验

{output_panel}
+

执行证据

{execution_panel}
-

盲评包

{html.escape(str(output_blind_summary or 'blind A/B review pack missing'))}

-

审定报告

{html.escape(str(output_review_summary or 'review adjudication report missing'))}

+

盲评包

{blind_panel}
+

审定报告

{review_panel}
@@ -1407,8 +1285,8 @@ def render_html(report: dict[str, Any]) -> str:
-

运行矩阵

{html.escape(str(conformance_summary))}

-

目标编译

{html.escape(str(compiled_summary or 'compiled target report missing'))}

+

运行矩阵

{conformance_panel}
+

目标编译

{compiled_panel}
@@ -1417,7 +1295,7 @@ def render_html(report: dict[str, Any]) -> str:
-

信任报告

{html.escape(str(trust_summary))}

+

信任报告

{trust_panel}

安全边界

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

@@ -1428,7 +1306,7 @@ def render_html(report: dict[str, Any]) -> str:

权限探针

{html.escape(gate_details.get('permission-runtime', 'runtime permission probes missing'))}

-

运行边界

{html.escape(str(runtime_permissions_summary or 'runtime permission probe summary missing'))}

+

运行边界

{runtime_boundary_panel}
@@ -1438,22 +1316,22 @@ def render_html(report: dict[str, Any]) -> str:

运营回路

{html.escape(gate_details.get('operations-loop', 'adoption drift report missing'))}

-

漂移信号

{html.escape(str(adoption_summary or 'no adoption drift summary'))}

+

漂移信号

{adoption_panel}

人工批准

{html.escape(gate_details.get('review-waivers', 'review waiver ledger missing'))}

-

批准台账

{html.escape(str(waiver_summary or 'no review waiver summary'))}

+

批准台账

{waiver_panel}

注册审计

{html.escape(gate_details.get('registry-audit', 'registry audit missing'))}

-

包体元数据

{html.escape(str(registry_package))}

+

包体元数据

{registry_panel}

发布路线

{html.escape(gate_details.get('release-notes', 'release notes missing'))}

-

包体验证

{html.escape(str(package_summary or 'package verification missing'))}

+

包体验证

{package_panel}
diff --git a/scripts/render_skill_overview.py b/scripts/render_skill_overview.py index b0f5341..ddde13a 100644 --- a/scripts/render_skill_overview.py +++ b/scripts/render_skill_overview.py @@ -5,6 +5,7 @@ import json from pathlib import Path from skill_report_charts import render_chart_set +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 @@ -279,15 +280,8 @@ def render_roadmap(items: list[dict]) -> str: def render_html(summary: dict) -> str: charts = render_chart_set(summary) - nav_html = "".join( - f"{bi_span(item['label'], item['label_en'])}" for item in REPORT_NAV_V2 - ) - language_switch = ( - '
' - '' - '' - "
" - ) + 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", {}) @@ -359,580 +353,7 @@ def render_html(summary: dict) -> str: {html.escape(summary['name'])} Skill 生成审计报告 @@ -1148,43 +569,7 @@ def render_html(summary: dict) -> str:
diff --git a/scripts/review_studio_formatting.py b/scripts/review_studio_formatting.py new file mode 100644 index 0000000..f7195fe --- /dev/null +++ b/scripts/review_studio_formatting.py @@ -0,0 +1,133 @@ +#!/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": "归档哈希", + "baseline_pass_rate": "Baseline", + "breaking_change_count": "破坏变更", + "case_count": "案例数", + "command_executed_count": "命令执行", + "compatibility_pass_count": "兼容通过", + "covered_gate_count": "覆盖 Gate", + "declared_bump": "声明版本", + "delta": "增益", + "event_count": "事件数", + "failure_count": "失败数", + "gate_pass": "Gate", + "help_smoke_failed_count": "Help 失败", + "install_simulated": "安装模拟", + "license": "License", + "metadata_fallback_count": "Metadata fallback", + "missed_trigger_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": "待审", + "recorded_fixture_count": "记录样本", + "recommended_bump": "建议版本", + "residual_risk_count": "残余风险", + "risk_band": "风险带", + "route_collision_count": "路由冲突", + "script_count": "脚本数", + "secret_findings": "Secret", + "skill_count": "Skill 数", + "target_count": "目标数", + "targets": "目标平台", + "timing_observed_count": "计时样本", + "token_estimated_count": "估算 Token", + "token_observed_count": "真实 Token", + "trust_level": "信任级别", + "variant_run_count": "运行数", + "version": "版本", + "warning_count": "警告数", + "with_skill_pass_rate": "With Skill", +} + + +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 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_layout.py b/scripts/review_studio_layout.py new file mode 100644 index 0000000..08e560e --- /dev/null +++ b/scripts/review_studio_layout.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +"""Static layout contract for Review Studio HTML.""" + +import html + + +SCRIPT_INTERFACE = "internal-module" +SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep Review Studio layout and CSS out of gate logic." + + +REVIEW_STUDIO_NAV = [ + ("#overview", "审查总览"), + ("#intent", "意图画布"), + ("#trigger", "触发实验"), + ("#output", "输出实验"), + ("#actions", "修复动作"), + ("#annotations", "审查批注"), + ("#runtime", "运行矩阵"), + ("#trust", "信任报告"), + ("#permissions", "权限批准"), + ("#permission-probes", "权限探针"), + ("#atlas", "组合治理"), + ("#telemetry", "运营回路"), + ("#waivers", "人工批准"), + ("#registry", "注册审计"), + ("#release", "发布路线"), +] + + +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 """ + :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); } + .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; } + .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); + } + code { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 13px; + } + @media (max-width: 980px) { + .metrics, .gates, .twocol, .actions-grid, .annotations-grid, .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; } + } + """.strip() diff --git a/scripts/skill_report_layout.py b/scripts/skill_report_layout.py new file mode 100644 index 0000000..9f60320 --- /dev/null +++ b/scripts/skill_report_layout.py @@ -0,0 +1,653 @@ +#!/usr/bin/env python3 +"""Static layout contract for skill overview report HTML.""" + +import html +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." + + +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 """ + :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, 246px) minmax(0, 1fr); + gap: 36px; + 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 p { + margin: 12px 0 0; + color: var(--muted); + max-width: 43ch; + text-wrap: pretty; + } + .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-stack { + display: grid; + gap: 18px; + } + .metrics-lead { + display: grid; + grid-template-columns: minmax(420px, 1.15fr) minmax(300px, 0.85fr); + gap: 18px; + align-items: stretch; + } + .metrics-note { + display: grid; + align-content: start; + gap: 16px; + } + .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; + } + .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-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 2px; + } + .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: 13px; + color: var(--muted); + } + .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); + } + .metric-card { + display: grid; + grid-template-columns: 92px minmax(0, 1fr); + gap: 18px; + align-content: start; + min-height: 0; + } + .metric-card-head { + display: grid; + align-content: start; + } + .metric-card strong { + display: block; + margin: 8px 0 10px; + 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; + } + .metric-card-body { + min-width: 0; + } + .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; } + .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; } + .hero-grid { gap: 24px; } + .score-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .metrics-lead, .two-col, .metric-grid, .chart-grid, .roadmap { grid-template-columns: 1fr; } + .metric-card { grid-template-columns: 86px minmax(0, 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; } + .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; } + } + """.strip() + + +def skill_overview_script() -> str: + return """ + (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(); + })(); + """.strip() diff --git a/scripts/yao.py b/scripts/yao.py index 9fe8604..013ed5a 100644 --- a/scripts/yao.py +++ b/scripts/yao.py @@ -7,65 +7,29 @@ from pathlib import Path from github_benchmark_scan import build_query from render_intent_confidence import assess_intent_confidence +from yao_cli_config import ( + ARCHETYPE_MODE, + archetype_guidance, + baseline_compare_args, + diagnose_skill_candidates, + diagnosis_note, + discovery_summary, + infer_archetype, + local_output_runner_command, + recommendation_from_synthesis, + reference_visibility, + resolve_promotion_target, + resolve_target, +) ROOT = Path(__file__).resolve().parent.parent SCRIPTS = ROOT / "scripts" -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", -} - - def script_path(name: str) -> str: return str(SCRIPTS / name) -def local_output_runner_command() -> str: - return json.dumps(["python3", "scripts/local_output_eval_runner.py"]) - - def load_json_maybe(text: str) -> dict | None: text = text.strip() if not text: @@ -94,33 +58,6 @@ def run_script(name: str, args: list[str], cwd: Path | None = None) -> dict: } -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 prompt_with_default(label: str, default: str) -> str: sys.stderr.write(f"{label} [{default}]: ") sys.stderr.flush() @@ -165,154 +102,6 @@ def intent_confidence_note(summary: dict) -> str: return "\n".join(lines) + "\n" -ARCHETYPE_MODE = { - "scaffold": "scaffold", - "production": "production", - "library": "library", - "governed": "governed", -} - - -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"], - } - - def maybe_emit_update_notice(args: argparse.Namespace) -> None: if getattr(args, "no_update_check", False): return diff --git a/scripts/yao_cli_config.py b/scripts/yao_cli_config.py new file mode 100644 index 0000000..f8bf5bb --- /dev/null +++ b/scripts/yao_cli_config.py @@ -0,0 +1,235 @@ +#!/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 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/skill-ir/examples/yao-meta-skill.json b/skill-ir/examples/yao-meta-skill.json index bc03202..8875667 100644 --- a/skill-ir/examples/yao-meta-skill.json +++ b/skill-ir/examples/yao-meta-skill.json @@ -154,6 +154,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -161,6 +163,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -169,7 +172,8 @@ "scripts/upgrade_check.py", "scripts/validate_skill.py", "scripts/verify_package.py", - "scripts/yao.py" + "scripts/yao.py", + "scripts/yao_cli_config.py" ], "assets": [ "templates/basic_skill.md.j2", diff --git a/skill_atlas/catalog.json b/skill_atlas/catalog.json index e1a7105..cfa9108 100644 --- a/skill_atlas/catalog.json +++ b/skill_atlas/catalog.json @@ -65,6 +65,8 @@ "scripts/render_social_preview.py", "scripts/render_system_model.py", "scripts/resource_boundary_check.py", + "scripts/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/run_conformance_suite.py", "scripts/run_description_optimization_suite.py", "scripts/run_eval_suite.py", @@ -72,6 +74,7 @@ "scripts/run_output_execution.py", "scripts/simulate_install.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", "scripts/sync_local_install.py", @@ -81,6 +84,7 @@ "scripts/validate_skill.py", "scripts/verify_package.py", "scripts/yao.py", + "scripts/yao_cli_config.py", "references/artifact-design-doctrine.md", "references/authoring-discipline.md", "references/distribution-registry-method.md", diff --git a/tests/verify_review_studio.py b/tests/verify_review_studio.py index 591fb6c..23cab6f 100644 --- a/tests/verify_review_studio.py +++ b/tests/verify_review_studio.py @@ -10,6 +10,8 @@ ROOT = Path(__file__).resolve().parent.parent SCRIPT = ROOT / "scripts" / "render_review_studio.py" 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_layout as review_layout # noqa: E402 def main() -> None: @@ -353,9 +355,30 @@ def main() -> None: assert "人工批准" in html, html[:8200] assert "权限批准" in html, html[:9000] assert "权限探针" in html, html[:9500] + 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 len(review_layout.REVIEW_STUDIO_NAV) == 15, review_layout.REVIEW_STUDIO_NAV + assert "position: sticky" in review_layout.review_studio_css(), review_layout.review_studio_css()[:400] + 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 review_layout.render_review_nav([]) == "" print(json.dumps({"ok": True}, ensure_ascii=False, indent=2)) diff --git a/tests/verify_skill_overview.py b/tests/verify_skill_overview.py index 6c63efb..1cb3f7e 100644 --- a/tests/verify_skill_overview.py +++ b/tests/verify_skill_overview.py @@ -7,6 +7,10 @@ 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_model import REPORT_NAV_V2 def run(*args: str) -> dict: @@ -26,6 +30,30 @@ def run(*args: str) -> dict: 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 "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 ".metrics-lead" in css_contract, css_contract[:7000] + assert "@media (max-width: 980px)" in css_contract, css_contract[-2200:] + + script_contract = skill_overview_script() + 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 + tmp_root = ROOT / "tests" / "tmp_skill_overview" if tmp_root.exists(): subprocess.run(["rm", "-rf", str(tmp_root)], check=True) diff --git a/tests/verify_trust_check.py b/tests/verify_trust_check.py index ead57fc..49ace96 100644 --- a/tests/verify_trust_check.py +++ b/tests/verify_trust_check.py @@ -87,16 +87,24 @@ def main() -> None: assert "scripts/github_benchmark_scan.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/review_studio_formatting.py", + "scripts/review_studio_layout.py", "scripts/skill_report_charts.py", + "scripts/skill_report_layout.py", "scripts/skill_report_metrics.py", "scripts/skill_report_model.py", + "scripts/yao_cli_config.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 "review_studio_formatting.py" not in warning_text, payload["warnings"] + assert "review_studio_layout.py" not in warning_text, payload["warnings"] assert "skill_report_charts.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 "yao_cli_config.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"] diff --git a/tests/verify_yao_cli.py b/tests/verify_yao_cli.py index 6ad15ed..7b4aaff 100644 --- a/tests/verify_yao_cli.py +++ b/tests/verify_yao_cli.py @@ -9,6 +9,8 @@ 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_cli_config # noqa: E402 def run(*args: str, input_text: str | None = None) -> dict: @@ -36,6 +38,11 @@ def main() -> None: 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 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() init_result = run("init", "cli-demo-skill", "--description", "CLI demo skill.", "--output-dir", str(tmp_root)) assert init_result["ok"], init_result