feat: add release docs and replay schemas

This commit is contained in:
732642856
2026-07-09 13:43:58 +08:00
parent 88eff419a1
commit 52266a3b64
6 changed files with 329 additions and 0 deletions
+1
View File
@@ -61,6 +61,7 @@ For large architecture or release work, also read:
| 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. |
| ERR-030 | Release packaging can misread non-ASCII tracked filenames when parsing quoted `git ls-files` output. | mitigated 2026-07-09 | Package builders must use `git ls-files -z` and decode NUL-separated paths before writing zip archives. |
| ERR-031 | Premium skill zip can ship without user install prompts or replay schemas, leaving users and future oracle imports without a contract. | mitigated 2026-07-09 | `skill_release_package.py` must inject `INSTALL.md` and `USER_PROMPTS.md`; replay contracts must live in `references/real_case_calibration/` and `references/oracle/`. |
## Fragment Sweep Command Set
@@ -0,0 +1,115 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Three Engine Same-Chart Parity Replay",
"type": "object",
"required": [
"case_id",
"birth_data_policy",
"engines",
"comparison_rows",
"status"
],
"properties": {
"case_id": {
"type": "string"
},
"birth_data_policy": {
"type": "string",
"enum": [
"public_case_only",
"anonymized_private_case",
"operator_local_only"
]
},
"engines": {
"type": "object",
"properties": {
"VedAstro": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"official_raw_response_path": {
"type": "string"
},
"artifact_hash": {
"type": "string"
}
}
},
"PyJHora_JHora": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"raw_output_path": {
"type": "string"
},
"settings": {
"type": "object"
}
}
},
"jyotishganit": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"raw_output_path": {
"type": "string"
}
}
}
}
},
"comparison_rows": {
"type": "array",
"items": {
"type": "object",
"required": [
"section",
"field",
"local_value",
"oracle_values",
"status"
],
"properties": {
"section": {
"type": "string"
},
"field": {
"type": "string"
},
"local_value": {},
"oracle_values": {
"type": "object"
},
"status": {
"type": "string",
"enum": [
"match",
"mismatch",
"blocked",
"not_comparable"
]
}
}
}
},
"status": {
"type": "string",
"enum": [
"not_started",
"blocked",
"partial",
"complete"
]
},
"blocked_reason": {
"type": "string"
}
}
}
@@ -0,0 +1,140 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Real Case Calibration Catalog",
"type": "object",
"required": [
"case_id",
"source",
"chart_signature",
"event_outcomes",
"similarity",
"replay"
],
"properties": {
"case_id": {
"type": "string"
},
"source": {
"type": "object",
"required": [
"url",
"source_grade",
"license_or_quote_boundary"
],
"properties": {
"url": {
"type": "string"
},
"source_grade": {
"type": "string",
"enum": [
"primary",
"verified_secondary",
"forum_claim",
"unverified"
]
},
"license_or_quote_boundary": {
"type": "string"
}
}
},
"chart_signature": {
"type": "object",
"properties": {
"lagna": {
"type": "string"
},
"moon_sign": {
"type": "string"
},
"d9_lagna": {
"type": "string"
},
"ul": {
"type": "string"
},
"a7": {
"type": "string"
},
"a10": {
"type": "string"
},
"notable_yogas": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"event_outcomes": {
"type": "array",
"items": {
"type": "object",
"required": [
"event_type",
"event_date",
"outcome"
],
"properties": {
"event_type": {
"type": "string"
},
"event_date": {
"type": "string"
},
"outcome": {
"type": "string"
},
"source_excerpt_note": {
"type": "string"
}
}
}
},
"similarity": {
"type": "object",
"properties": {
"score": {
"type": "number"
},
"matching_factors": {
"type": "array",
"items": {
"type": "string"
}
},
"dissimilar_factors": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"replay": {
"type": "object",
"properties": {
"outcome_replay_status": {
"type": "string",
"enum": [
"not_started",
"blocked",
"partial",
"complete"
]
},
"conflict_notes": {
"type": "array",
"items": {
"type": "string"
}
},
"do_not_use_for_prediction": {
"type": "boolean"
}
}
}
}
}
+31
View File
@@ -21,6 +21,34 @@ except ModuleNotFoundError: # pragma: no cover - direct script execution
ROOT = Path(__file__).resolve().parents[1]
SKIP_PREFIXES = ("scratch/", "references/open_source_sources/", ".git/", "__pycache__/")
SKIP_NAMES = {".env", ".env.local"}
GENERATED_DOCS = {
"INSTALL.md": """# Jyotish Skill Install
1. Unzip this package into a local folder.
2. Run `python3 scripts/public_release_privacy_scan.py`.
3. Run `python3 scripts/user_invocation_acceptance_check.py`.
4. If the check passes, load this folder as a Codex skill/plugin or call `mcp_server.py`.
5. VedAstro cloud evidence is optional. Without `official_raw_response`, reports must say `official_blocked` or `local_fallback`.
Do not add private birth data, API keys, or desktop oracle screenshots to this package.
""",
"USER_PROMPTS.md": """# User Prompts
## User Has No Question
请根据我的出生信息先运行统一主链,生成 evidence_packet、guided_topics 和 Technique Audit Table。
不要反问我想看什么;请先给出 3-5 个最值得继续看的方向,并附可直接复制的问题。
## High-Rigor Reading
请使用 strict_workflow,并在输出中标明 VedAstro / PyJHora-JHora / jyotishganit / Real Case Calibration 的状态。
如果没有 VedAstro official_raw_response,请标记 official_blocked 或 local_fallback。
## Birth-Time Rectification
请使用主动问询式校时:先生成候选时间扫描和选择题,我只回答 A/B/C/D。
""",
}
def _git_files() -> list[str]:
@@ -59,6 +87,7 @@ def build_package_plan(edition: str = "premium_cloud_drive") -> dict[str, Any]:
"privacy_scan_status": privacy["status"],
"file_count": len(files),
"files": files,
"generated_files": sorted(GENERATED_DOCS),
"boundary": "Dry-run plan only; use --write-zip to create a local zip, then upload manually if desired.",
}
@@ -71,6 +100,8 @@ def write_zip(edition: str, output: Path) -> dict[str, Any]:
with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED) as archive:
for rel in plan["files"]:
archive.write(ROOT / rel, rel)
for rel, text in GENERATED_DOCS.items():
archive.writestr(rel, text)
return {**plan, "mode": "write_zip", "zip_path": str(output)}
+34
View File
@@ -0,0 +1,34 @@
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def _load(path: str) -> dict:
return json.loads((ROOT / path).read_text(encoding="utf-8"))
def test_real_case_calibration_schema_defines_replay_contract() -> None:
schema = _load("references/real_case_calibration/catalog.schema.json")
props = schema["properties"]
assert schema["title"] == "Real Case Calibration Catalog"
assert {"case_id", "source", "chart_signature", "event_outcomes", "similarity", "replay"} <= set(props)
assert props["source"]["properties"]["source_grade"]["enum"] == ["primary", "verified_secondary", "forum_claim", "unverified"]
assert "outcome_replay_status" in props["replay"]["properties"]
assert "do_not_use_for_prediction" in props["replay"]["properties"]
def test_three_engine_parity_replay_schema_defines_raw_oracle_slots() -> None:
schema = _load("references/oracle/three_engine_parity_replay.schema.json")
engines = schema["properties"]["engines"]["properties"]
assert schema["title"] == "Three Engine Same-Chart Parity Replay"
assert {"VedAstro", "PyJHora_JHora", "jyotishganit"} <= set(engines)
assert "official_raw_response_path" in engines["VedAstro"]["properties"]
assert "raw_output_path" in engines["PyJHora_JHora"]["properties"]
assert "comparison_rows" in schema["properties"]
assert "blocked_reason" in schema["properties"]
+8
View File
@@ -29,4 +29,12 @@ def test_skill_release_package_can_write_zip(tmp_path) -> None:
names = set(archive.namelist())
assert "SKILL.md" in names
assert "scripts/skill_release_manifest.py" in names
assert "INSTALL.md" in names
assert "USER_PROMPTS.md" in names
assert ".env.local" not in names
with zipfile.ZipFile(target) as archive:
install = archive.read("INSTALL.md").decode("utf-8")
prompts = archive.read("USER_PROMPTS.md").decode("utf-8")
assert "python3 scripts/user_invocation_acceptance_check.py" in install
assert "guided_topics" in prompts
assert "official_blocked" in prompts