From a16a157f0a01df27ef963b102d6e891c7e6e3dfd Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sat, 27 Jun 2026 00:08:10 +0800 Subject: [PATCH] feat(skill): add gap truth audit --- SKILL.md | 20 ++ references/skill_gap_truth_registry.json | 164 +++++++++++++++ scripts/skill_gap_truth_audit.py | 245 +++++++++++++++++++++++ tests/test_skill_gap_truth_audit.py | 89 ++++++++ 4 files changed, 518 insertions(+) create mode 100644 references/skill_gap_truth_registry.json create mode 100644 scripts/skill_gap_truth_audit.py create mode 100644 tests/test_skill_gap_truth_audit.py diff --git a/SKILL.md b/SKILL.md index 6bb6b5a7..924219ea 100644 --- a/SKILL.md +++ b/SKILL.md @@ -277,6 +277,26 @@ python3 scripts/validate_interpretation_templates.py --format markdown 完整排序见 `/Users/wuyongnaren/Documents/印度占星/docs/research/current_skill_core_gap_rerank_2026_06_26.md`。 +### Skill Gap Truth Audit(严禁过度声明) + +当用户问“是否已经全球第一”“是否包含所有印度占星技法”“过去案例哪里错了”“还差什么硬任务”时,必须先运行: + +```bash +python3 scripts/skill_gap_truth_audit.py --format markdown +``` + +真源文件: + +`references/skill_gap_truth_registry.json` + +此审计的结论优先级高于口头记忆: + +- 若 `can_claim_global_first: false`,不得宣称全球无争议第一。 +- 若 `can_claim_all_skills_complete: false`,不得宣称所有技法已完全封顶。 +- 若 `can_claim_perfect_accuracy: false`,不得宣称排盘、Dasha、Shadbala、年运等已达到完美精度。 +- 若某技法为 `covered`,只能说“有稳定入口或可用层”,不能自动说成 `complete`。 +- 过去案例分析若触及 `past_case_analysis_corrections` 中的误判类型,必须主动修正并降低置信度。 + ## 全球开源定位 **当前还不能诚实地说这是全球开源印度占星 / 吠陀占星项目里的无争议第一。** diff --git a/references/skill_gap_truth_registry.json b/references/skill_gap_truth_registry.json new file mode 100644 index 00000000..2ee85ede --- /dev/null +++ b/references/skill_gap_truth_registry.json @@ -0,0 +1,164 @@ +{ + "schema_version": 1, + "scope": "jyotish_skill_gap_truth_registry", + "purpose": "Machine-readable truth boundary for the Jyotish skill: what remains unfinished, what must not be overclaimed, and which past analysis claims have been corrected.", + "public_claim_rules": { + "can_claim_global_first": false, + "can_claim_all_skills_complete": false, + "can_claim_perfect_accuracy": false, + "reason": "The skill has strong breadth, but external oracle closure, long-term benchmark history and several traditional judgment layers remain unfinished." + }, + "hard_fronts": { + "dasha_external_oracle": { + "title": "Dasha external oracle", + "priority": "P0", + "status": "blocked_external_evidence", + "current_truth": "Dasha engines are usable, but exact start dates, balance periods and sub-period boundaries cannot be called externally closed until real JHora/PyJHora/book-example packets pass validation.", + "completion_standard": [ + "At least one real external_verified packet is accepted for the first Dasha target.", + "Multiple Dasha families have versioned boundary comparisons.", + "Differences between local output and external oracle output are documented without production tuning from local data." + ], + "forbidden_claims": [ + "Dasha external oracle alone is complete", + "Exact Dasha dates are perfectly calibrated", + "One local chart output proves software-grade timing accuracy" + ], + "next_actions": [ + "Use scripts/dasha_oracle_closure_status.py to fill the first external packet.", + "Collect redacted JHora/PyJHora/book evidence under references/oracle/artifacts.", + "Re-run scripts/oracle_evidence_validator.py after packet application." + ] + }, + "shadbala_external_absolute_values": { + "title": "Shadbala external absolute values", + "priority": "P0", + "status": "blocked_external_evidence", + "current_truth": "Internal six-component Rupa/Virupa aggregation is usable, but external absolute-value closure is not complete.", + "completion_standard": [ + "All seven visible planets have external Sthana, Dig, Kala, Chesta, Naisargika, Drik and total Rupa evidence.", + "Component sums match validated packet structure.", + "Any differences are explained component-by-component rather than hidden with a global multiplier." + ], + "forbidden_claims": [ + "Shadbala absolute values alone are fully JHora-calibrated", + "Relative planetary ranking proves absolute Rupa correctness", + "Global scaling alone fixes Shadbala" + ], + "next_actions": [ + "Use scripts/shadbala_oracle_closure_status.py for the first absolute-value packet.", + "Fill the REDACTED_PLACE/Raman or equivalent component table from an external oracle.", + "Reject production constant changes until component-level evidence is accepted." + ] + }, + "tajika_sahams_annual_closure": { + "title": "Tajika / Sahams annual closure", + "priority": "P0", + "status": "active_gap", + "current_truth": "Varshaphala, Muntha, Year Lord, Mudda Dasha and Sahams have local structure, but annual judgment depth and external sample closure remain incomplete.", + "completion_standard": [ + "External annual packets include solar return time, Varsha Lagna, Muntha, Year Lord, Mudda Dasha, key Sahams and Tajika Yogas.", + "Annual interpretation templates distinguish calculation evidence from judgment confidence.", + "At least several public cases are benchmarked over multiple years." + ], + "forbidden_claims": [ + "Tajika or Sahams alone are fully closed", + "A calculated Saham alone predicts an annual event", + "Local Varshaphala output alone is external oracle evidence" + ], + "next_actions": [ + "Use scripts/tajika_annual_closure_status.py to fill the first annual packet.", + "Freeze the annual judgment template around Muntha, Year Lord, Mudda Dasha and Sahams.", + "Add public benchmark rows only after external annual evidence exists." + ] + }, + "article_template_industrialization": { + "title": "Article-level interpretation template industrialization", + "priority": "P1", + "status": "active_gap", + "current_truth": "Several high-granularity templates are frozen, but article-derived rules must keep being distilled into guarded templates with sources, cross-checks and forbidden claims.", + "completion_standard": [ + "High-value topics have reusable templates in references/interpretation_template_registry.json.", + "Each template states required cross-checks, confidence ceiling and forbidden single-factor claims.", + "User-supplied article claims are treated as B/C-level clues unless supported by stronger sources." + ], + "forbidden_claims": [ + "Article claims alone are authoritative", + "A screenshot or online article alone proves a Jyotish rule", + "A high-granularity template replaces D1, Varga, Dasha or Transit" + ], + "next_actions": [ + "Keep extending references/interpretation_template_registry.json instead of scattering rules.", + "Run scripts/validate_interpretation_templates.py after each template addition.", + "Promote only source-graded, cross-checked templates into SKILL.md." + ] + }, + "long_term_public_benchmark": { + "title": "Long-term public benchmark", + "priority": "P0", + "status": "active_gap", + "current_truth": "Benchmark scripts exist, but the skill still lacks a long-running public history comparable to the strongest global projects.", + "completion_standard": [ + "Capability, oracle readiness and public benchmark reports are versioned after every sample batch.", + "Benchmark rows separate internal consistency, external oracle evidence and interpretation maturity.", + "Global-first claims remain false until public evidence supports them." + ], + "forbidden_claims": [ + "Long-term benchmark alone is complete", + "A green local test suite alone proves global first status", + "Many covered techniques equal public benchmark leadership" + ], + "next_actions": [ + "Run scripts/public_benchmark_dashboard.py and scripts/oracle_closure_master_dashboard.py after evidence changes.", + "Publish markdown reports only after validation commands pass.", + "Track global-first gap as a durable public claim boundary." + ] + } + }, + "past_case_analysis_corrections": [ + { + "id": "ashtakoot_not_all_zero", + "wrong_pattern": "Treating Ashtakoot as if it returned all zero scores.", + "corrected_truth": "Ashtakoot matrices and non-zero scoring exist; the remaining issue is external validation and finer-grained comparison, not total absence.", + "source_ref": "docs/research/antigravity_round25_ashtakoot_round24_claim_correction_2026_06_25.md" + }, + { + "id": "panchanga_not_empty", + "wrong_pattern": "Treating Panchanga/Muhurta as completely blank.", + "corrected_truth": "Panchanga and Muhurta engines are deep; remaining work is UI, external oracle comparison and festival/v vrata breadth.", + "source_ref": "docs/research/antigravity_round26_panchang_round25_claim_correction_2026_06_25.md" + }, + { + "id": "covered_is_not_complete", + "wrong_pattern": "Using covered and complete as if they meant the same thing.", + "corrected_truth": "Covered means there is a stable entry or usable layer; complete requires mature workflow, tests, boundary text and, where needed, external oracle closure.", + "source_ref": "docs/research/current_skill_core_gap_rerank_2026_06_26.md" + }, + { + "id": "single_factor_reading_risk", + "wrong_pattern": "Drawing case conclusions from one planet, one yoga, one transit, one Dasha or one article rule.", + "corrected_truth": "Use D1, relevant Varga, Dasha, Transit, strength and historical verification where applicable. Single-factor readings should be capped at low confidence.", + "source_ref": "references/practitioner-wisdom-anti-dogma.md" + }, + { + "id": "enemy_sign_not_debilitation", + "wrong_pattern": "Confusing enemy sign with debilitation.", + "corrected_truth": "Enemy sign and debilitation are separate dignity states. Do not label a planet as debilitated unless it is in the classical debilitation sign/degree zone.", + "source_ref": "references/common-misconceptions.md" + }, + { + "id": "jaimini_kp_tajika_not_absent", + "wrong_pattern": "Describing Chara Dasha, KP/Prashna or Tajika/Sahams as absent.", + "corrected_truth": "These fronts have usable structures; the hard work is tail-difference closure, traditional workflow deepening and external sample validation.", + "source_ref": "docs/research/three_fronts_skill_depth_audit_2026_06_26.md" + } + ], + "must_not_overclaim": [ + "Dasha exact-date boundaries are externally closed.", + "Shadbala absolute Rupa values are fully calibrated against JHora or book examples.", + "Tajika/Sahams annual prediction is traditional-software-grade closed.", + "All Indian astrology techniques are complete.", + "The skill is already the undisputed global open-source number one.", + "A single chart factor, article rule, screenshot or template can override D1/Varga/Dasha/Transit convergence." + ] +} diff --git a/scripts/skill_gap_truth_audit.py b/scripts/skill_gap_truth_audit.py new file mode 100644 index 00000000..3448b605 --- /dev/null +++ b/scripts/skill_gap_truth_audit.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +"""Audit the current Jyotish skill truth boundary.""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +PYTHON = sys.executable +DEFAULT_REGISTRY = ROOT / "references" / "skill_gap_truth_registry.json" + + +def _resolve(path: str | None) -> Path: + if not path: + return DEFAULT_REGISTRY + candidate = Path(path) + return candidate if candidate.is_absolute() else ROOT / candidate + + +def _load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as fh: + return json.load(fh) + + +def _run_json(command: list[str]) -> dict[str, Any]: + completed = subprocess.run( + command, + cwd=ROOT, + text=True, + capture_output=True, + timeout=120, + check=False, + ) + if completed.returncode != 0: + raise RuntimeError(completed.stderr.strip() or completed.stdout.strip()) + return json.loads(completed.stdout) + + +def _validate_registry(registry: dict[str, Any]) -> list[str]: + problems: list[str] = [] + if registry.get("scope") != "jyotish_skill_gap_truth_registry": + problems.append("invalid_scope") + hard_fronts = registry.get("hard_fronts") + if not isinstance(hard_fronts, dict) or not hard_fronts: + problems.append("missing_hard_fronts") + hard_fronts = {} + for front_id, front in hard_fronts.items(): + for field in [ + "title", + "priority", + "status", + "current_truth", + "completion_standard", + "forbidden_claims", + "next_actions", + ]: + if front.get(field) in (None, "", [], {}): + problems.append(f"{front_id}:missing_{field}") + if "alone" not in " ".join(front.get("forbidden_claims", [])).lower(): + problems.append(f"{front_id}:forbidden_claims_should_block_single_factor_or_overclaim") + corrections = registry.get("past_case_analysis_corrections") + if not isinstance(corrections, list) or not corrections: + problems.append("missing_past_case_analysis_corrections") + corrections = [] + for correction in corrections: + for field in ["id", "wrong_pattern", "corrected_truth", "source_ref"]: + if correction.get(field) in (None, "", [], {}): + problems.append(f"correction:missing_{field}") + source_ref = correction.get("source_ref") + if source_ref and not (ROOT / source_ref).exists(): + problems.append(f"correction:missing_source_ref:{source_ref}") + return problems + + +def build_report(registry_path: Path) -> dict[str, Any]: + registry = _load_json(registry_path) + problems = _validate_registry(registry) + capability = _run_json([PYTHON, "scripts/audit_capabilities.py", "--mode", "validate"]) + oracle = _run_json([ + PYTHON, + "scripts/oracle_closure_master_dashboard.py", + "--format", + "json", + ]) + hard_fronts = registry.get("hard_fronts", {}) + remaining = [ + { + "id": front_id, + "title": front["title"], + "priority": front["priority"], + "status": front["status"], + "current_truth": front["current_truth"], + "completion_standard": front["completion_standard"], + "next_actions": front["next_actions"], + } + for front_id, front in hard_fronts.items() + ] + priority_rank = {"P0": 0, "P1": 1, "P2": 2} + remaining.sort(key=lambda item: (priority_rank.get(item["priority"], 99), item["id"])) + claim_rules = registry.get("public_claim_rules", {}) + can_claim_global_oracle = oracle["summary"]["can_claim_global_oracle_closure"] + can_claim_global_first = bool( + claim_rules.get("can_claim_global_first") + and can_claim_global_oracle + and capability.get("valid") + ) + can_claim_all_skills_complete = bool( + claim_rules.get("can_claim_all_skills_complete") + and can_claim_global_oracle + and capability.get("status_counts", {}).get("covered", 0) == 0 + ) + can_claim_perfect_accuracy = bool( + claim_rules.get("can_claim_perfect_accuracy") + and can_claim_global_oracle + and oracle["summary"].get("production_tuning_allowed") + ) + corrections = registry.get("past_case_analysis_corrections", []) + return { + "scope": "jyotish_skill_gap_truth_audit", + "schema_version": 1, + "generated_at": datetime.now(timezone.utc).isoformat(), + "registry": str(registry_path.relative_to(ROOT) if registry_path.is_relative_to(ROOT) else registry_path), + "valid": not problems, + "summary": { + "technique_count": capability["technique_count"], + "capability_valid": capability["valid"], + "capability_problem_count": capability["problem_count"], + "status_counts": capability["status_counts"], + "hard_front_count": len(hard_fronts), + "past_correction_count": len(corrections), + "registry_problem_count": len(problems), + }, + "public_claim": { + "can_claim_global_first": can_claim_global_first, + "can_claim_all_skills_complete": can_claim_all_skills_complete, + "can_claim_perfect_accuracy": can_claim_perfect_accuracy, + "reason": claim_rules.get("reason", ""), + }, + "oracle_closure": { + "summary": oracle["summary"], + "next_action_order": oracle.get("next_action_order", []), + }, + "remaining_hard_fronts": remaining, + "past_correction_ids": [item["id"] for item in corrections], + "past_case_analysis_corrections": corrections, + "must_not_overclaim": registry.get("must_not_overclaim", []), + "problems": problems, + "boundary": ( + "This audit answers the skill-level truth question. A covered technique is not the same as a " + "complete externally closed technique; single-factor case readings must remain confidence-capped." + ), + } + + +def render_markdown(report: dict[str, Any]) -> str: + claim = report["public_claim"] + summary = report["summary"] + oracle = report["oracle_closure"]["summary"] + lines = [ + "# Jyotish Skill Gap Truth Audit", + "", + f"Generated: `{report['generated_at']}`", + "", + "## Public Claim Boundary", + "", + f"- can_claim_global_first: `{str(claim['can_claim_global_first']).lower()}`", + f"- can_claim_all_skills_complete: `{str(claim['can_claim_all_skills_complete']).lower()}`", + f"- can_claim_perfect_accuracy: `{str(claim['can_claim_perfect_accuracy']).lower()}`", + f"- reason: {claim['reason']}", + "", + "## Capability Snapshot", + "", + f"- technique_count: `{summary['technique_count']}`", + f"- capability_valid: `{str(summary['capability_valid']).lower()}`", + f"- hard_front_count: `{summary['hard_front_count']}`", + f"- past_correction_count: `{summary['past_correction_count']}`", + "", + "## External Oracle Closure", + "", + f"- total_tasks: `{oracle['total_tasks']}`", + f"- external_verified_tasks: `{oracle['external_verified_tasks']}`", + f"- open_tasks: `{oracle['open_tasks']}`", + f"- can_claim_global_oracle_closure: `{str(oracle['can_claim_global_oracle_closure']).lower()}`", + "", + "## Remaining Hard Fronts", + "", + ] + for front in report["remaining_hard_fronts"]: + lines.extend( + [ + f"### {front['title']}", + "", + f"- id: `{front['id']}`", + f"- priority: `{front['priority']}`", + f"- status: `{front['status']}`", + f"- current_truth: {front['current_truth']}", + "", + ] + ) + lines.extend(["## Past Corrections", ""]) + for correction in report["past_case_analysis_corrections"]: + lines.extend( + [ + f"- `{correction['id']}`: {correction['corrected_truth']}", + ] + ) + lines.extend(["", "## Must Not Overclaim", ""]) + lines.extend(f"- {item}" for item in report["must_not_overclaim"]) + lines.extend(["", "## Boundary", "", report["boundary"], ""]) + if report["problems"]: + lines.extend(["## Problems", ""]) + lines.extend(f"- {problem}" for problem in report["problems"]) + lines.append("") + return "\n".join(lines) + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Audit Jyotish skill gap truth boundary") + parser.add_argument("--registry", help="Path to skill_gap_truth_registry.json") + parser.add_argument("--format", choices=["json", "markdown"], default="json") + parser.add_argument("--output", help="Optional output path") + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv) + report = build_report(_resolve(args.registry)) + text = json.dumps(report, ensure_ascii=False, indent=2) if args.format == "json" else render_markdown(report) + if args.output: + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(text, encoding="utf-8") + print(text) + return 0 if report["valid"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_skill_gap_truth_audit.py b/tests/test_skill_gap_truth_audit.py new file mode 100644 index 00000000..09d2152e --- /dev/null +++ b/tests/test_skill_gap_truth_audit.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Tests for the skill-level gap truth audit.""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REGISTRY = ROOT / "references" / "skill_gap_truth_registry.json" + + +def test_skill_gap_truth_registry_lists_hard_fronts_and_past_corrections() -> None: + data = json.loads(REGISTRY.read_text(encoding="utf-8")) + + assert data["scope"] == "jyotish_skill_gap_truth_registry" + assert data["public_claim_rules"]["can_claim_global_first"] is False + assert data["public_claim_rules"]["can_claim_all_skills_complete"] is False + + hard_fronts = data["hard_fronts"] + required_fronts = { + "dasha_external_oracle", + "shadbala_external_absolute_values", + "tajika_sahams_annual_closure", + "article_template_industrialization", + "long_term_public_benchmark", + } + assert required_fronts <= set(hard_fronts) + for front_id in required_fronts: + front = hard_fronts[front_id] + assert front["status"] in {"blocked_external_evidence", "active_gap"} + assert front["priority"] in {"P0", "P1", "P2"} + assert front["completion_standard"] + assert front["forbidden_claims"] + assert front["next_actions"] + + corrections = data["past_case_analysis_corrections"] + correction_ids = {item["id"] for item in corrections} + assert "ashtakoot_not_all_zero" in correction_ids + assert "panchanga_not_empty" in correction_ids + assert "covered_is_not_complete" in correction_ids + assert "single_factor_reading_risk" in correction_ids + + +def test_skill_gap_truth_audit_outputs_current_truth_boundary() -> None: + completed = subprocess.run( + [sys.executable, "scripts/skill_gap_truth_audit.py", "--format", "json"], + cwd=ROOT, + text=True, + capture_output=True, + timeout=120, + check=False, + ) + + assert completed.returncode == 0, completed.stderr or completed.stdout + report = json.loads(completed.stdout) + assert report["scope"] == "jyotish_skill_gap_truth_audit" + assert report["valid"] is True + assert report["summary"]["technique_count"] >= 79 + assert report["summary"]["capability_valid"] is True + assert report["summary"]["hard_front_count"] >= 5 + assert report["public_claim"]["can_claim_global_first"] is False + assert report["public_claim"]["can_claim_all_skills_complete"] is False + assert report["public_claim"]["can_claim_perfect_accuracy"] is False + assert "Dasha" in report["must_not_overclaim"][0] + assert report["oracle_closure"]["summary"]["can_claim_global_oracle_closure"] is False + assert report["remaining_hard_fronts"][0]["id"] == "dasha_external_oracle" + assert "covered_is_not_complete" in report["past_correction_ids"] + + +def test_skill_gap_truth_audit_markdown_is_human_readable() -> None: + completed = subprocess.run( + [sys.executable, "scripts/skill_gap_truth_audit.py", "--format", "markdown"], + cwd=ROOT, + text=True, + capture_output=True, + timeout=120, + check=False, + ) + + assert completed.returncode == 0, completed.stderr or completed.stdout + markdown = completed.stdout + assert "# Jyotish Skill Gap Truth Audit" in markdown + assert "can_claim_global_first: `false`" in markdown + assert "Dasha external oracle" in markdown + assert "Past Corrections" in markdown