From ede8dfa7bae6b3aeeca5acbed0fae08528890bce Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Thu, 9 Jul 2026 10:51:39 +0800 Subject: [PATCH] feat: define skill release manifest --- docs/research/pre_work_error_ledger.md | 1 + scripts/skill_release_manifest.py | 103 +++++++++++++++++++++++++ tests/test_skill_release_manifest.py | 30 +++++++ 3 files changed, 134 insertions(+) create mode 100644 scripts/skill_release_manifest.py create mode 100644 tests/test_skill_release_manifest.py diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index 9640ca07..52c63584 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -59,6 +59,7 @@ For large architecture or release work, also read: | ERR-026 | VedAstro service adapter can obtain an official full-snapshot raw response while the user entrypoint drops it, leaving gateway official closure permanently blocked. | mitigated 2026-07-09 | `vedastro_user_entrypoint` must expose `vedastro_official_full_snapshot.raw_response_available` and root `official_raw_response` when explicitly requested; gateway tests must prove raw propagation reaches `official_verified`. | | ERR-027 | External engine readiness diagnostics can be mistaken for a completed same-chart parity comparison. | mitigated 2026-07-09 | `diagnose_external_engine_adapters.py` must expose `same_chart_parity_contract.required_outputs`, per-engine expected oracle fields, and `tested=false` until a real same-chart comparison runs. | | ERR-028 | Active birth-time rectification can stop at question generation and never narrow candidate clusters from user answers. | mitigated 2026-07-09 | `active_rectification_questions.score_answers()` must turn A/B/C/D answers into cluster rankings, next-round questions, and an explicit boundary that final rectification still needs candidate chart differences. | +| ERR-029 | Basic git and premium cloud-drive skill packages can blur contents, privacy exclusions, and external-engine promises. | mitigated 2026-07-09 | `scripts/skill_release_manifest.py` must define edition contents, excluded private material, acceptance commands, and external-engine runtime boundaries before packaging. | ## Fragment Sweep Command Set diff --git a/scripts/skill_release_manifest.py b/scripts/skill_release_manifest.py new file mode 100644 index 00000000..54cee616 --- /dev/null +++ b/scripts/skill_release_manifest.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Build the public/basic vs premium/cloud skill release manifest.""" + +from __future__ import annotations + +import json +from typing import Any + +try: + from scripts.diagnose_external_engine_adapters import build_report as external_engine_report +except ModuleNotFoundError: # pragma: no cover - direct script execution + from diagnose_external_engine_adapters import build_report as external_engine_report + + +REPO_URL = "https://github.com/732642856/yinduzhanxing" + + +def _external_boundary() -> dict[str, Any]: + diagnostics = external_engine_report() + engines = diagnostics.get("engines", {}) + return { + "VedAstro": { + "status": engines.get("VedAstro", {}).get("status"), + "runtime_dependency": False, + "completion_gate": "official_raw_response required before claiming official cloud closure", + }, + "PyJHora/JHora": { + "status": engines.get("PyJHora/JHora", {}).get("status"), + "runtime_dependency": False, + "license_boundary": engines.get("PyJHora/JHora", {}).get("license_boundary"), + }, + "JHora desktop": { + "status": "manual_oracle_only", + "runtime_dependency": False, + "boundary": "Desktop screenshots/exports can be used as external oracle evidence; do not vendor JHora.", + }, + "jyotishganit": { + "status": engines.get("jyotishganit", {}).get("status"), + "runtime_dependency": False, + "license": engines.get("jyotishganit", {}).get("license"), + }, + } + + +def build_report() -> dict[str, Any]: + return { + "scope": "skill_release_manifest", + "schema_version": 1, + "editions": { + "basic_git": { + "distribution": "public_git_repository", + "source": REPO_URL, + "included": [ + "SKILL.md", + ".codex-plugin/plugin.json", + "mcp_server.py", + "scripts/", + "tests/", + "README.md", + ], + "excluded": ["private local reports", "cloud-drive premium notes", "manual oracle screenshots"], + }, + "premium_cloud_drive": { + "distribution": "cloud_drive_zip", + "source": "operator-built package from the same cleaned repo revision", + "included": [ + "basic_git contents", + "guided user prompts", + "release checklist", + "offline install notes", + "optional .env.example templates", + ], + "excluded": ["personal birth data", "private PDFs", "API keys", "JHora/PyJHora binaries"], + }, + }, + "privacy_boundary": { + "private_birth_data_allowed": False, + "excluded_material": [ + "personal_case_reports", + "private_birth_records", + "private_pdf_exports", + "api_keys_or_env_local", + "manual_oracle_screenshots_with_identity", + ], + }, + "external_engine_boundary": _external_boundary(), + "acceptance_commands": [ + "python3 scripts/public_release_privacy_scan.py", + "python3 scripts/user_invocation_acceptance_check.py", + "python3 scripts/diagnose_external_engine_adapters.py --json", + "python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45", + ], + "boundary": "This manifest defines package contents and guards; it does not build or upload a zip.", + } + + +def main() -> int: + print(json.dumps(build_report(), ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_skill_release_manifest.py b/tests/test_skill_release_manifest.py new file mode 100644 index 00000000..e3943e37 --- /dev/null +++ b/tests/test_skill_release_manifest.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import json + +from scripts.skill_release_manifest import build_report + + +def test_skill_release_manifest_defines_basic_and_premium_boundaries() -> None: + report = build_report() + editions = report["editions"] + + assert report["scope"] == "skill_release_manifest" + assert set(editions) == {"basic_git", "premium_cloud_drive"} + assert editions["basic_git"]["distribution"] == "public_git_repository" + assert editions["premium_cloud_drive"]["distribution"] == "cloud_drive_zip" + assert "https://github.com/732642856/yinduzhanxing" in editions["basic_git"]["source"] + assert any("scripts/user_invocation_acceptance_check.py" in command for command in report["acceptance_commands"]) + assert any("scripts/public_release_privacy_scan.py" in command for command in report["acceptance_commands"]) + assert report["privacy_boundary"]["private_birth_data_allowed"] is False + assert "personal_case_reports" in report["privacy_boundary"]["excluded_material"] + assert report["external_engine_boundary"]["PyJHora/JHora"]["runtime_dependency"] is False + assert report["external_engine_boundary"]["JHora desktop"]["runtime_dependency"] is False + assert "official_raw_response" in report["external_engine_boundary"]["VedAstro"]["completion_gate"] + + +def test_skill_release_manifest_contains_no_private_birth_data() -> None: + text = json.dumps(build_report(), ensure_ascii=False) + + for forbidden in ("REDACTED_DATE", "REDACTED_TIME", "REDACTED_HOSPITAL"): + assert forbidden not in text