diff --git a/scripts/skill_release_package.py b/scripts/skill_release_package.py index d803df43..e69065fd 100644 --- a/scripts/skill_release_package.py +++ b/scripts/skill_release_package.py @@ -55,6 +55,52 @@ Do not add private birth data, API keys, or desktop oracle screenshots to this p ## Birth-Time Rectification 请使用主动问询式校时:先生成候选时间扫描和选择题,我只回答 A/B/C/D。 +""", + "SALES_PACKAGE.md": """# Premium Skill Sales Package + +Edition: premium_cloud_drive + +## What This Package Includes + +- Unified Jyotish runtime entrypoints for skill, MCP, API, and local scripts. +- Evidence packet workflow for D1/D9/D10/D2/D4, dasha boundaries, audit rows, and external oracle status. +- Guided topic entrypoint for users who do not know what to ask. +- Strict workflow prompts for high-rigor readings. +- Release hygiene checks for privacy, clean unzip usage, and user invocation acceptance. + +## What This Package Does Not Include + +- Private birth data, private case reports, API keys, or desktop oracle screenshots. +- Guaranteed VedAstro cloud closure without `official_raw_response`. +- Completed PyJHora/JHora parity rows unless the operator imports external raw oracle outputs. +- Completed real case replay unless structured case outcomes are imported. + +Do not include private birth data in this package. Users provide their own birth data at runtime. +""", + "GUIDED_ENTRYPOINT.md": """# Guided Entrypoint + +Use this when the user loads the skill but does not know how to ask. + +## Default Flow + +1. Ask for birth date, exact or approximate birth time, birthplace, and question language. +2. Run `direct_chart` when birth time is reliable; run `rectification` when birth time is uncertain. +3. Generate `evidence_packet`, `guided_topics`, and `Technique Audit Table` before interpretation. +4. Use `blind=true` when prior personal feedback must not influence the reading. +5. Mark VedAstro as `official_verified`, `official_blocked`, or `local_fallback`. +6. Mark PyJHora/JHora, jyotishganit, Western oracle, and Real Case Calibration as `used`, `partial`, or `blocked`. + +## User-Facing Starter + +请提供出生日期、出生时间、出生地点和想看的语言。 +如果你不知道该问什么,我会先运行 evidence packet,然后给出 3-5 个最值得继续看的主题。 + +## Output Requirements + +- Start from evidence, not story. +- Show blocked or partial techniques. +- Never claim official cloud evidence without raw official evidence. +- Keep `guided_topics` available for the next turn. """, } diff --git a/scripts/vedastro_user_entrypoint.py b/scripts/vedastro_user_entrypoint.py index 9a740f50..6b19201b 100644 --- a/scripts/vedastro_user_entrypoint.py +++ b/scripts/vedastro_user_entrypoint.py @@ -96,22 +96,31 @@ def _case_from_args(args: argparse.Namespace, themes: list[str]) -> dict[str, An def _run_capability_catalog(case: dict[str, Any]) -> dict[str, Any]: - completed = subprocess.run( - [ - sys.executable, - str(RUNNER), - "--bundle", - "official_full_capability_catalog", - "--birth-json", - json.dumps(case, ensure_ascii=False), - ], - cwd=ROOT, - text=True, - capture_output=True, - timeout=max(5.0, float(os.environ.get("VEDASTRO_TIMEOUT_SECONDS", "5") or 5)), - check=False, - env=os.environ.copy(), - ) + try: + completed = subprocess.run( + [ + sys.executable, + str(RUNNER), + "--bundle", + "official_full_capability_catalog", + "--birth-json", + json.dumps(case, ensure_ascii=False), + ], + cwd=ROOT, + text=True, + capture_output=True, + timeout=max(5.0, float(os.environ.get("VEDASTRO_TIMEOUT_SECONDS", "5") or 5)), + check=False, + env=os.environ.copy(), + ) + except subprocess.TimeoutExpired: + return { + "available": False, + "status": "official_full_capability_catalog_timeout", + "summary": {}, + "domain_routing": {}, + "sample_outputs": {}, + } if completed.returncode != 0: return { "available": False, diff --git a/tests/test_skill_release_clean_trial.py b/tests/test_skill_release_clean_trial.py index 814fbf23..2cfc76d8 100644 --- a/tests/test_skill_release_clean_trial.py +++ b/tests/test_skill_release_clean_trial.py @@ -36,6 +36,8 @@ def test_premium_skill_zip_runs_from_clean_directory(tmp_path: Path) -> None: required = [ "INSTALL.md", "USER_PROMPTS.md", + "SALES_PACKAGE.md", + "GUIDED_ENTRYPOINT.md", "PACKAGE_ACCEPTANCE.json", "references/real_case_calibration/replay_manifest.json", "references/oracle/three_engine_parity_replay_manifest.json", diff --git a/tests/test_skill_release_package.py b/tests/test_skill_release_package.py index 76018767..2c413047 100644 --- a/tests/test_skill_release_package.py +++ b/tests/test_skill_release_package.py @@ -20,6 +20,8 @@ def test_skill_release_package_dry_run_uses_safe_tracked_files() -> None: assert not any("private" in path.lower() for path in plan["files"]) assert "RELEASE_MANIFEST.json" in plan["generated_files"] assert "PACKAGE_ACCEPTANCE.json" in plan["generated_files"] + assert "SALES_PACKAGE.md" in plan["generated_files"] + assert "GUIDED_ENTRYPOINT.md" in plan["generated_files"] assert plan["required_contracts"]["references/real_case_calibration/replay_manifest.json"] is True assert plan["required_contracts"]["references/oracle/three_engine_parity_replay_manifest.json"] is True assert "references/real_case_calibration/replay_manifest.json" in plan["files"] @@ -41,6 +43,8 @@ def test_skill_release_package_can_write_zip(tmp_path) -> None: assert "USER_PROMPTS.md" in names assert "RELEASE_MANIFEST.json" in names assert "PACKAGE_ACCEPTANCE.json" in names + assert "SALES_PACKAGE.md" in names + assert "GUIDED_ENTRYPOINT.md" in names assert "references/real_case_calibration/replay_manifest.json" in names assert "references/oracle/three_engine_parity_replay_manifest.json" in names assert "references/oracle/western_oracle_adapter_contract.md" in names @@ -48,11 +52,18 @@ def test_skill_release_package_can_write_zip(tmp_path) -> None: with zipfile.ZipFile(target) as archive: install = archive.read("INSTALL.md").decode("utf-8") prompts = archive.read("USER_PROMPTS.md").decode("utf-8") + sales = archive.read("SALES_PACKAGE.md").decode("utf-8") + guided = archive.read("GUIDED_ENTRYPOINT.md").decode("utf-8") acceptance = json.loads(archive.read("PACKAGE_ACCEPTANCE.json").decode("utf-8")) assert "python3 scripts/user_invocation_acceptance_check.py" in install assert "https://github.com/732642856/yinduzhanxing" in install assert "guided_topics" in prompts assert "official_blocked" in prompts assert "western_oracle_payload" in prompts + assert "premium_cloud_drive" in sales + assert "Do not include private birth data" in sales + assert "blind=true" in guided + assert "Technique Audit Table" in guided + assert "guided_topics" in guided assert acceptance["status"] in {"pass", "blocked"} assert acceptance["required_contracts"]["references/oracle/three_engine_parity_replay_manifest.json"] is True diff --git a/tests/test_vedastro_user_entrypoint.py b/tests/test_vedastro_user_entrypoint.py index 1a097354..ebe9366b 100644 --- a/tests/test_vedastro_user_entrypoint.py +++ b/tests/test_vedastro_user_entrypoint.py @@ -6,10 +6,25 @@ import subprocess import sys from pathlib import Path +from scripts import vedastro_user_entrypoint + ROOT = Path(__file__).resolve().parents[1] +def test_capability_catalog_timeout_degrades_to_blocked(monkeypatch) -> None: + def timeout_run(*args, **kwargs): + raise subprocess.TimeoutExpired(cmd=args[0], timeout=kwargs.get("timeout")) + + monkeypatch.setattr(vedastro_user_entrypoint.subprocess, "run", timeout_run) + + report = vedastro_user_entrypoint._run_capability_catalog({"year": 2000}) + + assert report["available"] is False + assert report["status"] == "official_full_capability_catalog_timeout" + assert report["summary"] == {} + + def test_user_entrypoint_runs_catalog_and_strict_workflow_contract() -> None: catalog_stub = { "available": True,