From 51decd5003df1a33f49e71d6469e5a0cd382e7dc Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 19 Jul 2026 06:56:59 +0800 Subject: [PATCH 1/4] docs: align cross-project sync boundary --- ...ercial_web_backport_boundary_2026_07_19.md | 42 +++++++++++++++++++ .../sync_policy.v1.json | 8 ++++ tests/test_cross_project_sync_status.py | 8 ++++ 3 files changed, 58 insertions(+) create mode 100644 docs/research/commercial_web_backport_boundary_2026_07_19.md diff --git a/docs/research/commercial_web_backport_boundary_2026_07_19.md b/docs/research/commercial_web_backport_boundary_2026_07_19.md new file mode 100644 index 00000000..6da7234e --- /dev/null +++ b/docs/research/commercial_web_backport_boundary_2026_07_19.md @@ -0,0 +1,42 @@ +# Research-to-commercial capability boundary — 2026-07-19 + +Purpose: keep the asset flow honest. Research repo is the personal core asset and source of astrological capability. Commercial repo productizes stable research contracts for users. + +## Research capabilities that commercial may productize + +| Capability | Research web status | Evidence | +|---|---|---| +| Daily starlanguage entry | local research UX exists | `jyotish-app/index.html#daily-guidance-card`; guarded by `tests/test_frontend_productization.py` | +| Birth-time rectification entry | local research UX exists | `jyotish-app/index.html#entry-rectification`; guarded by `tests/test_frontend_productization.py` | +| Local chart library | local research UX exists | `jyotish-app/index.html#saved-chart-panel`; `jyotish-app/main.js#saveCurrentChartToLibrary`; guarded by `tests/test_frontend_productization.py` | +| Display-name field | local research profile exists | `jyotish-app/index.html#profile-display-name`; `PROFILE_DISPLAY_NAME_KEY`; guarded by `tests/test_frontend_productization.py` | +| Chat history actions | local research sessions exist | rename/share/archive/delete in `jyotish-app/main.js`; guarded by `tests/test_frontend_productization.py` | + +## Not equivalent by design + +| Commercial capability | Research boundary | +|---|---| +| Supabase `/api/account` profile save | Research web is static/local-first; it must not claim cloud profile persistence. | +| `profiles` service-role upsert grants | Commercial database migration only; research repo may document it but should not require Supabase for local research web. | +| Cookie-authenticated account route | Commercial runtime only; research web stores display name locally. | +| Credits, billing, subscriptions | Commercial-only business layer; never part of research repo capability. | + +## Claim rule + +Research web may say: +- “research capability has a local UX/reference implementation” +- “profile display name and sessions persist in localStorage” +- “stable research contracts can be synced outward to commercial” + +Research web must not say: +- “cloud profile persistence is equivalent to commercial” +- “Supabase account/profile upsert is available in the static research site” +- “research web and commercial web are 100% identical” +- “commercial credits, billing, subscriptions, or account entitlements are research capabilities” + +## Current optimization priority + +1. Improve real research capability first; sync stable contracts outward to commercial. +2. Keep timing claims exploratory until independent negative holdout labels exist. +3. Keep external oracle mismatch reports as attribution, not majority-vote truth. +4. Keep commercial account/payment/runtime details out of research repo except as deployment contract notes. diff --git a/references/cross_project_contract/sync_policy.v1.json b/references/cross_project_contract/sync_policy.v1.json index 0f5f468b..08dc1d96 100644 --- a/references/cross_project_contract/sync_policy.v1.json +++ b/references/cross_project_contract/sync_policy.v1.json @@ -24,6 +24,14 @@ ] } }, + "research_repo_exclusions": [ + "commercial_credits", + "billing", + "subscriptions", + "payment", + "account_entitlements", + "service_role_runtime" + ], "shared_files": [ "references/cross_project_contract/fixture_manifest.v1.json", "references/cross_project_contract/sync_ledger.json", diff --git a/tests/test_cross_project_sync_status.py b/tests/test_cross_project_sync_status.py index aabfed3f..6da125a6 100644 --- a/tests/test_cross_project_sync_status.py +++ b/tests/test_cross_project_sync_status.py @@ -36,6 +36,14 @@ def test_sync_policy_encodes_research_first_commercial_mature_rule() -> None: assert policy["sync_model"] == "research_validates_commercial_receives_mature" assert policy["directional_gates"]["research_to_commercial"]["source_required"] == "validated_in_research" assert policy["directional_gates"]["research_to_commercial"]["target_required"] == "commercial_safe" + assert set(policy["research_repo_exclusions"]) >= { + "commercial_credits", + "billing", + "subscriptions", + "payment", + "account_entitlements", + "service_role_runtime", + } assert "references/cross_project_contract/fixture_manifest.v1.json" in policy["shared_files"] From 05948cde7f418b57bc6100aeb9a13049c5416e9f Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 19 Jul 2026 08:02:44 +0800 Subject: [PATCH 2/4] feat: sync blind timing holdout gate --- .../day_level_holdout_labeling_guide_zh.md | 76 +++++++ scripts/day_level_holdout_template.py | 60 ++++++ scripts/day_level_holdout_validator.py | 3 + scripts/timing_ranker_blind_eval.py | 71 +++++++ tests/test_day_level_holdout_template.py | 48 +++++ tests/test_timing_ranker_blind_eval.py | 193 ++++++++++++++++++ 6 files changed, 451 insertions(+) create mode 100644 docs/research/day_level_holdout_labeling_guide_zh.md create mode 100644 scripts/day_level_holdout_template.py create mode 100644 scripts/timing_ranker_blind_eval.py create mode 100644 tests/test_day_level_holdout_template.py create mode 100644 tests/test_timing_ranker_blind_eval.py diff --git a/docs/research/day_level_holdout_labeling_guide_zh.md b/docs/research/day_level_holdout_labeling_guide_zh.md new file mode 100644 index 00000000..b397012b --- /dev/null +++ b/docs/research/day_level_holdout_labeling_guide_zh.md @@ -0,0 +1,76 @@ +# 日级应期 holdout 人工标签指南 + +目标:给研究仓提供真实、独立、可复验的正/负样本。你不需要会编程,只需要提供来源清楚的事实材料。 + +## 需要收集什么 + +每条标签只回答一个问题: + +某人在某个日期区间,某类事件是否发生? + +可用标签: + +- `target_event`:事件发生了。 +- `no_target_event`:有公开资料支持该区间没有发生这个目标事件。 + +## 优先领域 + +1. 事业:任命、创办公司、上市、获奖、重大作品发布。 +2. 婚恋:结婚、离婚、订婚、公开伴侣关系变化。 +3. 财富:上市、重大融资、破产、重大资产事件。 + +暂不优先健康/死亡,噪音和伦理风险高。 + +## 合格来源 + +优先: + +- 官方 biography / timeline +- Britannica / Nobel / company official timeline +- 出版传记中可核对页码或章节的时间线 +- IMDb / MusicBrainz / company history 等结构化公开资料 + +不合格: + +- “没搜到新闻所以没发生” +- ChatGPT 生成内容 +- 无来源论坛故事 +- 已被本项目观察过的旧控制日期 +- 模糊说法:“那一年很平静” + +## 最小可用规模 + +pilot 阶段: + +- 3 个公开人物 +- 每人 1 个领域 +- 每人 1 个正样本窗口 +- 每人 2 个负样本窗口 + +正式升级门槛: + +- 至少 20 个独立案例 +- 至少 80 个独立负样本区间 +- 标签冻结后才允许评分 + +## 填写方式 + +生成空模板: + +```bash +python3 scripts/day_level_holdout_template.py --output /tmp/holdout_annotation_template.json +``` + +把公开来源、日期区间、事件说明填进去,再交给 intake: + +```bash +python3 scripts/day_level_negative_holdout_intake.py references/real_case_calibration/day_level_holdout_v3_preregistration.json --row-json '{"case_id":"..."}' +``` + +## 结论边界 + +没有真实独立负样本前: + +- 可以输出候选日期排序; +- 可以说明触发信号; +- 不能说“精确日期预测已验证”。 diff --git a/scripts/day_level_holdout_template.py b/scripts/day_level_holdout_template.py new file mode 100644 index 00000000..6c77749c --- /dev/null +++ b/scripts/day_level_holdout_template.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Create a blank independent day-level timing holdout annotation template.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +TEMPLATE = { + "case_id": "", + "subject": { + "name": "", + "public_profile_url": "", + "birth_time_rating": "AA/A only preferred", + }, + "domain": "career|marriage|wealth|health", + "label": "target_event|no_target_event", + "start": "YYYY-MM-DD", + "end": "YYYY-MM-DD", + "event_description": "", + "event_absent_assertion": "", + "source_url": "https://", + "source_quote_or_summary": "", + "adjudicator": "", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + "frozen_before_scoring": True, + "source_path": "", + "notes": "", +} + + +def build_template() -> dict: + return { + "template_type": "day_level_holdout_annotation_v3", + "instructions": [ + "Use target_event for known dated events.", + "Use no_target_event only when a public source supports that the target event did not occur in the interval.", + "Do not use old control dates or rows observed before preregistration for tuning.", + "Freeze labels before running timing_ranker_blind_eval.py.", + ], + "annotation": TEMPLATE, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + text = json.dumps(build_template(), ensure_ascii=False, indent=2) + "\n" + if args.output: + args.output.write_text(text, encoding="utf-8") + else: + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/day_level_holdout_validator.py b/scripts/day_level_holdout_validator.py index 788fbfb6..2063404e 100644 --- a/scripts/day_level_holdout_validator.py +++ b/scripts/day_level_holdout_validator.py @@ -11,11 +11,14 @@ REQUIRED={"case_id","domain","label","start","end","source_url","adjudicator","t def validate(path: Path) -> dict: data=json.loads(path.read_text(encoding="utf-8")); rows=data.get("annotations") or []; errors=[] mode=data.get("validation_mode", "independent") + prohibited=set(data.get("prohibited_tuning_data") or []) allowed_labels={"target_event", "no_target_event"} if mode == "independent" else {"target_event", "observational_non_target_date"} for i,row in enumerate(rows): for key in sorted(REQUIRED-set(row)): errors.append({"row":i,"field":key,"error":"missing"}) if row.get("label") not in allowed_labels: errors.append({"row":i,"field":"label","error":"invalid"}) if not str(row.get("source_url") or "").startswith(("https://","http://")): errors.append({"row":i,"field":"source_url","error":"not_public_url"}) + if mode == "independent" and row.get("independent_human_reviewed") is not True: errors.append({"row":i,"field":"independent_human_reviewed","error":"not_independently_human_reviewed"}) + if row.get("source_path") in prohibited: errors.append({"row":i,"field":"source_path","error":"prohibited_tuning_source"}) positives=sum(r.get("label")=="target_event" for r in rows); negatives=sum(r.get("label") in {"no_target_event", "observational_non_target_date"} for r in rows) gate=data.get("frozen_gate") or {}; ready=not errors and positives>=gate.get("minimum_independent_cases",20) and negatives>=gate.get("minimum_independent_negative_intervals",80) status=("observational_ready_not_independent" if ready else "awaiting_observational_labels") if mode == "observational" else ("ready_for_blind_replay" if ready else "awaiting_independent_labels") diff --git a/scripts/timing_ranker_blind_eval.py b/scripts/timing_ranker_blind_eval.py new file mode 100644 index 00000000..aa97a0d9 --- /dev/null +++ b/scripts/timing_ranker_blind_eval.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Evaluate frozen day/month timing candidates against independent holdout labels.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +from scripts.day_level_holdout_validator import validate + + +def _load(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def _key(row: dict) -> tuple[str, str, str]: + return (str(row.get("case_id") or ""), str(row.get("start") or ""), str(row.get("end") or "")) + + +def evaluate(manifest_path: Path, candidates_path: Path) -> dict: + validation = validate(manifest_path) + manifest = _load(manifest_path) + candidates = sorted( + (_load(candidates_path).get("candidate_windows") or []), + key=lambda row: float(row.get("score") or 0), + reverse=True, + ) + labels = {_key(row): row.get("label") for row in manifest.get("annotations") or []} + ranked = [{**row, "rank": index + 1, "label": labels.get(_key(row))} for index, row in enumerate(candidates)] + positives = [row for row in ranked if row.get("label") == "target_event"] + negatives = [row for row in ranked if row.get("label") == "no_target_event"] + top_3_positive = sum(1 for row in positives if row["rank"] <= 3) + positive_top_3_rate = top_3_positive / len(positives) if positives else 0.0 + min_positive_score = min((float(row.get("score") or 0) for row in positives), default=0.0) + false_positive_negatives = sum(1 for row in negatives if float(row.get("score") or 0) >= min_positive_score) + specificity = 1 - (false_positive_negatives / len(negatives)) if negatives else 0.0 + gate = manifest.get("frozen_gate") or {} + blockers = [] + if validation["status"] != "ready_for_blind_replay": + blockers.append("holdout_not_ready") + if positive_top_3_rate < gate.get("minimum_positive_top_3_rate", 1): + blockers.append("positive_top_3_rate_below_gate") + if specificity < gate.get("minimum_specificity", 1): + blockers.append("specificity_below_gate") + passed = not blockers + return { + "scope": "timing_ranker_blind_eval", + "status": "pass" if passed else "blocked", + "claim_status": "calibrated_day_level" if passed else "exploratory_unvalidated", + "production_tuning_allowed": bool(passed), + "positive_count": len(positives), + "negative_count": len(negatives), + "positive_top_3_rate": positive_top_3_rate, + "specificity": specificity, + "blockers": blockers, + "validation": validation, + "ranked_windows": ranked, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("manifest", type=Path) + parser.add_argument("candidates", type=Path) + args = parser.parse_args() + print(json.dumps(evaluate(args.manifest, args.candidates), ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_day_level_holdout_template.py b/tests/test_day_level_holdout_template.py new file mode 100644 index 00000000..aad25713 --- /dev/null +++ b/tests/test_day_level_holdout_template.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +from scripts.day_level_holdout_template import build_template + +ROOT = Path(__file__).resolve().parents[1] + + +def test_holdout_template_is_nontechnical_and_validator_compatible_shape() -> None: + template = build_template() + annotation = template["annotation"] + + for token in [ + "case_id", + "subject", + "domain", + "label", + "start", + "end", + "event_absent_assertion", + "source_url", + "adjudicator", + "independent_human_reviewed", + "frozen_before_scoring", + ]: + assert token in annotation + + assert "Do not use old control dates" in " ".join(template["instructions"]) + assert annotation["source_url"] == "https://" + assert annotation["independent_human_reviewed"] is True + + +def test_holdout_template_cli_writes_json(tmp_path: Path) -> None: + output = tmp_path / "template.json" + + subprocess.run( + [sys.executable, str(ROOT / "scripts" / "day_level_holdout_template.py"), "--output", str(output)], + check=True, + text=True, + ) + + data = json.loads(output.read_text(encoding="utf-8")) + assert data["template_type"] == "day_level_holdout_annotation_v3" + assert data["annotation"]["label"] == "target_event|no_target_event" diff --git a/tests/test_timing_ranker_blind_eval.py b/tests/test_timing_ranker_blind_eval.py new file mode 100644 index 00000000..e9dcef0c --- /dev/null +++ b/tests/test_timing_ranker_blind_eval.py @@ -0,0 +1,193 @@ +from __future__ import annotations + +import json +from pathlib import Path + +from scripts.day_level_holdout_validator import validate +from scripts.day_level_negative_holdout_intake import append_annotation +from scripts.timing_ranker_blind_eval import evaluate + + +def test_holdout_validator_rejects_non_independent_and_prohibited_rows(tmp_path: Path) -> None: + manifest = { + "prohibited_tuning_data": ["old_controls.json"], + "frozen_gate": {"minimum_independent_cases": 1, "minimum_independent_negative_intervals": 1}, + "annotations": [ + { + "case_id": "case-1", + "domain": "career", + "label": "no_target_event", + "start": "2020-01-01", + "end": "2020-01-07", + "source_url": "https://example.org/timeline", + "adjudicator": "same_person", + "time_uncertainty_days": 0, + "independent_human_reviewed": False, + "source_path": "old_controls.json", + } + ], + } + path = tmp_path / "holdout.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + + report = validate(path) + + assert report["status"] == "awaiting_independent_labels" + assert report["production_tuning_allowed"] is False + assert {error["error"] for error in report["errors"]} >= { + "not_independently_human_reviewed", + "prohibited_tuning_source", + } + + +def test_blind_eval_requires_positive_windows_to_rank_above_negative_windows(tmp_path: Path) -> None: + manifest = { + "frozen_gate": { + "minimum_positive_top_3_rate": 0.6, + "minimum_specificity": 0.6, + "minimum_independent_cases": 1, + "minimum_independent_negative_intervals": 1, + }, + "annotations": [ + { + "case_id": "case-1", + "domain": "career", + "label": "target_event", + "start": "2020-01-01", + "end": "2020-01-07", + "source_url": "https://example.org/event", + "adjudicator": "reviewer-a", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + }, + { + "case_id": "case-1-neg", + "domain": "career", + "label": "no_target_event", + "start": "2020-02-01", + "end": "2020-02-07", + "source_url": "https://example.org/non-event", + "adjudicator": "reviewer-b", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + }, + ], + } + candidates = { + "candidate_windows": [ + {"case_id": "case-1", "start": "2020-01-01", "end": "2020-01-07", "score": 0.90}, + {"case_id": "case-1-neg", "start": "2020-02-01", "end": "2020-02-07", "score": 0.20}, + ] + } + manifest_path = tmp_path / "holdout.json" + candidates_path = tmp_path / "candidates.json" + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + candidates_path.write_text(json.dumps(candidates), encoding="utf-8") + + report = evaluate(manifest_path, candidates_path) + + assert report["status"] == "pass" + assert report["claim_status"] == "calibrated_day_level" + assert report["production_tuning_allowed"] is True + assert report["positive_top_3_rate"] == 1.0 + assert report["specificity"] == 1.0 + + +def test_blind_eval_blocks_when_negative_scores_outrank_positive_scores(tmp_path: Path) -> None: + manifest = { + "frozen_gate": { + "minimum_positive_top_3_rate": 0.6, + "minimum_specificity": 0.6, + "minimum_independent_cases": 1, + "minimum_independent_negative_intervals": 1, + }, + "annotations": [ + { + "case_id": "positive", + "domain": "marriage", + "label": "target_event", + "start": "2020-01-01", + "end": "2020-01-07", + "source_url": "https://example.org/event", + "adjudicator": "reviewer-a", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + }, + { + "case_id": "negative", + "domain": "marriage", + "label": "no_target_event", + "start": "2020-02-01", + "end": "2020-02-07", + "source_url": "https://example.org/non-event", + "adjudicator": "reviewer-b", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + }, + ], + } + candidates = { + "candidate_windows": [ + {"case_id": "positive", "start": "2020-01-01", "end": "2020-01-07", "score": 0.10}, + {"case_id": "negative", "start": "2020-02-01", "end": "2020-02-07", "score": 0.95}, + ] + } + manifest_path = tmp_path / "holdout.json" + candidates_path = tmp_path / "candidates.json" + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + candidates_path.write_text(json.dumps(candidates), encoding="utf-8") + + report = evaluate(manifest_path, candidates_path) + + assert report["status"] == "blocked" + assert report["claim_status"] == "exploratory_unvalidated" + assert report["production_tuning_allowed"] is False + assert "specificity_below_gate" in report["blockers"] + + +def test_negative_holdout_intake_appends_valid_independent_label(tmp_path: Path) -> None: + path = tmp_path / "holdout.json" + path.write_text(json.dumps({"annotations": [], "prohibited_tuning_data": []}), encoding="utf-8") + + report = append_annotation( + path, + { + "case_id": "case-2-neg", + "domain": "career", + "label": "no_target_event", + "start": "2020-03-01", + "end": "2020-03-31", + "source_url": "https://example.org/biography", + "adjudicator": "reviewer-c", + "time_uncertainty_days": 0, + "independent_human_reviewed": True, + }, + ) + + saved = json.loads(path.read_text(encoding="utf-8")) + assert report["appended"] is True + assert saved["annotations"][0]["case_id"] == "case-2-neg" + assert saved["annotations"][0]["frozen_before_scoring"] is True + + +def test_negative_holdout_intake_rejects_non_independent_label(tmp_path: Path) -> None: + path = tmp_path / "holdout.json" + path.write_text(json.dumps({"annotations": []}), encoding="utf-8") + + report = append_annotation( + path, + { + "case_id": "bad", + "domain": "career", + "label": "no_target_event", + "start": "2020-03-01", + "end": "2020-03-31", + "source_url": "https://example.org/biography", + "adjudicator": "reviewer-c", + "time_uncertainty_days": 0, + "independent_human_reviewed": False, + }, + ) + + assert report["appended"] is False + assert report["errors"][0]["error"] == "not_independently_human_reviewed" From 5298b49a89f8167757bf4f0dd1817807b0aadfae Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 19 Jul 2026 08:37:24 +0800 Subject: [PATCH 3/4] docs: sync oracle claim boundaries --- ..._av_normative_benchmark_plan_2026_07_19.md | 75 + ..._engine_mismatch_arbitration_2026_07_19.md | 27 + ...dastro_identity_closure_plan_2026_07_19.md | 61 + ...ngine_mismatch_arbitration_2026_07_19.json | 1464 +++++++++++++++++ .../vedastro_identity_archive_2026_07_19.json | 76 + ...st_shadbala_av_normative_benchmark_plan.py | 42 + .../test_three_engine_mismatch_arbitrator.py | 12 + tests/test_vedastro_identity_closure_plan.py | 35 + 8 files changed, 1792 insertions(+) create mode 100644 docs/research/shadbala_av_normative_benchmark_plan_2026_07_19.md create mode 100644 docs/research/three_engine_mismatch_arbitration_2026_07_19.md create mode 100644 docs/research/vedastro_identity_closure_plan_2026_07_19.md create mode 100644 references/oracle/three_engine_mismatch_arbitration_2026_07_19.json create mode 100644 references/oracle/vedastro_identity_archive_2026_07_19.json create mode 100644 tests/test_shadbala_av_normative_benchmark_plan.py create mode 100644 tests/test_vedastro_identity_closure_plan.py diff --git a/docs/research/shadbala_av_normative_benchmark_plan_2026_07_19.md b/docs/research/shadbala_av_normative_benchmark_plan_2026_07_19.md new file mode 100644 index 00000000..2c3c73a4 --- /dev/null +++ b/docs/research/shadbala_av_normative_benchmark_plan_2026_07_19.md @@ -0,0 +1,75 @@ +# Shadbala / Ashtakavarga normative benchmark plan — 2026-07-19 + +## Current status + +Current Shadbala target set is closed, but global tuning is not allowed. + +- current target set: `external_verified` +- `can_claim_shadbala_absolute_closure = true` +- `production_tuning_allowed = false` +- three-engine mismatch report: 60 mismatches, 60 classified, 0 unclassified +- Shadbala formula/component variants: 35 component rows + 7 derived-total rows +- Ashtakavarga table/contributor variants: 8 rows + +## What this means + +The research repo can claim closure for the current curated Shadbala target package. It must not claim universal Shadbala or AV truth across all schools/software. + +## Required benchmark ladder + +1. Source provenance + - VP Jain: page/edition/source hash or explicit retrieval gap. + - Xalen: source commit, package hash, executable hash if available, method list. + - PyJHora/JHora: isolated AGPL oracle only; no copied implementation. + - jyotishganit: permissive observation adapter where license permits. + +2. Component normalization + - `sthana` + - `dig` + - `kala` + - `chesta` + - `naisargika` + - `drik` + - `total_rupa` + +3. Unit contract + - Virupa vs Rupa must be explicit. + - Totals cannot be arbitrated before components. + - Component caps/floors must be named. + +4. Formula variant registry + - legitimate school variants become `method_variant`; + - unresolved variants stay `classified_unresolved`; + - no majority vote. + +5. Independent ephemeris mode + - same raw input mode isolates formula layer; + - independent ephemeris mode checks longitude/ayanamsa layer; + - both must be reported separately. + +## Commercial sync rule + +Commercial may receive: + +- readiness status; +- user-safe confidence cap; +- claim boundary; +- supported component list. + +Commercial must not receive: + +- unresolved raw research debt; +- forced absolute Virupa truth; +- AGPL implementation code; +- production tuning flag while `production_tuning_allowed = false`. + +## Next implementation task + +Create a component provenance registry that maps each Shadbala/AV mismatch category to: + +- source artifact; +- component; +- unit; +- likely reason; +- allowed claim; +- next evidence required. diff --git a/docs/research/three_engine_mismatch_arbitration_2026_07_19.md b/docs/research/three_engine_mismatch_arbitration_2026_07_19.md new file mode 100644 index 00000000..be8807cb --- /dev/null +++ b/docs/research/three_engine_mismatch_arbitration_2026_07_19.md @@ -0,0 +1,27 @@ +# Three-engine mismatch arbitration + +manifest: `references/oracle/three_engine_parity_replay_manifest.json` +status: `classified_unresolved` +truth_policy: `no_majority_vote` +commercial_sync: `status_and_claim_boundary_only` +mismatch_count: `60` +classified_count: `60` +unclassified_count: `0` + +Do not copy raw research debt into commercial runtime. Commercial receives readiness, claim boundary, and user-safe status only. + +## Category counts + +| category | count | +|---|---:| +| `ashtakavarga_table_or_contributor_variant` | 8 | +| `derived_total_from_component_variants` | 7 | +| `endpoint_or_varga_semantics` | 10 | +| `shadbala_formula_variant` | 35 | + +## Closure requirements + +- `endpoint_or_varga_semantics`: Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract. +- `ashtakavarga_table_or_contributor_variant`: Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics. +- `derived_total_from_component_variants`: Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned. +- `shadbala_formula_variant`: Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals. diff --git a/docs/research/vedastro_identity_closure_plan_2026_07_19.md b/docs/research/vedastro_identity_closure_plan_2026_07_19.md new file mode 100644 index 00000000..55ebf819 --- /dev/null +++ b/docs/research/vedastro_identity_closure_plan_2026_07_19.md @@ -0,0 +1,61 @@ +# VedAstro identity closure plan — 2026-07-19 + +## Current status + +VedAstro hosted API remains observation-only. + +Reason: hosted output can be replayed, but build identity, method semantics, and deployment version are not archived. Stable mismatch replay proves the conflict is not random transport noise; it does not prove which side is true. + +## Acceptable closure paths + +### Path A — hosted metadata + +Required from upstream: + +- hosted build version; +- source commit or release tag; +- method semantic contract for each endpoint used; +- ayanamsa/node/timezone interpretation; +- deployment timestamp or immutable build ID. + +### Path B — pinned self-hosted version + +Required archive: + +- source commit; +- NuGet package hash; +- DLL SHA-256; +- assembly version; +- public method inventory; +- container image digest or reproducible local runner hash. + +`scripts/vedastro_identity_archive.py` now records the NuGet identity and a `required_self_host_evidence` checklist. Missing fields keep truth upgrade blocked. + +## Claim boundary + +Allowed: + +- use VedAstro as external observation; +- report stable conflicts; +- compare raw response hashes and normalized fields; +- use pinned self-hosted evidence if all required identity fields are present. + +Forbidden: + +- tune production predictions from hosted output with unknown build identity; +- call hosted mismatch arbitration a global truth decision; +- silently prefer VedAstro or local output by majority vote. + +## Commercial sync rule + +Commercial may receive: + +- `VedAstro: observation_only`; +- endpoint status; +- claim boundary text. + +Commercial must not receive: + +- raw hosted credentials; +- hosted output as truth; +- production tuning permission while `truth_upgrade_gate` is blocked. diff --git a/references/oracle/three_engine_mismatch_arbitration_2026_07_19.json b/references/oracle/three_engine_mismatch_arbitration_2026_07_19.json new file mode 100644 index 00000000..3a90137d --- /dev/null +++ b/references/oracle/three_engine_mismatch_arbitration_2026_07_19.json @@ -0,0 +1,1464 @@ +{ + "category_counts": { + "ashtakavarga_table_or_contributor_variant": 8, + "derived_total_from_component_variants": 7, + "endpoint_or_varga_semantics": 10, + "shadbala_formula_variant": 35 + }, + "classified_count": 60, + "manifest_path": "references/oracle/three_engine_parity_replay_manifest.json", + "mismatch_count": 60, + "rows": [ + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Sun.sign", + "local_value": "Leo", + "oracle_values": { + "PyJHora_JHora": "Leo", + "VedAstro": "Gemini", + "jyotishganit": "Leo" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Moon.sign", + "local_value": "Cancer", + "oracle_values": { + "PyJHora_JHora": "Cancer", + "VedAstro": "Pisces", + "jyotishganit": "Cancer" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Mars.sign", + "local_value": "Leo", + "oracle_values": { + "PyJHora_JHora": "Leo", + "VedAstro": "Aries", + "jyotishganit": "Leo" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Mercury.sign", + "local_value": "Leo", + "oracle_values": { + "PyJHora_JHora": "Leo", + "VedAstro": "Virgo", + "jyotishganit": "Leo" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Jupiter.sign", + "local_value": "Cancer", + "oracle_values": { + "PyJHora_JHora": "Cancer", + "VedAstro": "Aquarius", + "jyotishganit": "Cancer" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Venus.sign", + "local_value": "Cancer", + "oracle_values": { + "PyJHora_JHora": "Cancer", + "VedAstro": "Leo", + "jyotishganit": "Cancer" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Saturn.sign", + "local_value": "Cancer", + "oracle_values": { + "PyJHora_JHora": "Cancer", + "VedAstro": "Gemini", + "jyotishganit": "Cancer" + }, + "section": "D2", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Moon.sign", + "local_value": "Gemini", + "oracle_values": { + "PyJHora_JHora": "Gemini", + "VedAstro": "Pisces", + "jyotishganit": "Gemini" + }, + "section": "D4", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Moon.sign", + "local_value": "Scorpio", + "oracle_values": { + "PyJHora_JHora": "Scorpio", + "VedAstro": "Leo", + "jyotishganit": "Scorpio" + }, + "section": "D9", + "truth_status": "unresolved" + }, + { + "category": "endpoint_or_varga_semantics", + "closure_requirement": "Confirm VedAstro endpoint returns the requested varga under the same ayanamsa/node/method contract.", + "differing_engines": [ + "VedAstro" + ], + "field": "Moon.sign", + "local_value": "Pisces", + "oracle_values": { + "PyJHora_JHora": "Pisces", + "VedAstro": "Sagittarius", + "jyotishganit": "Pisces" + }, + "section": "D10", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro" + ], + "field": "Sun", + "local_value": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ], + "oracle_values": { + "PyJHora_JHora": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ], + "VedAstro": [ + 3, + 7, + 3, + 2, + 3, + 3, + 6, + 7, + 4, + 3, + 4, + 3 + ], + "jyotishganit": [ + 3, + 7, + 4, + 3, + 3, + 2, + 6, + 7, + 4, + 4, + 3, + 2 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Moon", + "local_value": [ + 4, + 4, + 4, + 3, + 6, + 5, + 3, + 3, + 4, + 5, + 3, + 5 + ], + "oracle_values": { + "PyJHora_JHora": [ + 4, + 4, + 4, + 3, + 6, + 5, + 3, + 3, + 4, + 5, + 3, + 5 + ], + "VedAstro": [ + 4, + 5, + 3, + 2, + 6, + 6, + 3, + 2, + 5, + 4, + 4, + 5 + ], + "jyotishganit": [ + 4, + 5, + 4, + 2, + 6, + 5, + 3, + 2, + 5, + 5, + 3, + 5 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro" + ], + "field": "Mars", + "local_value": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ], + "oracle_values": { + "PyJHora_JHora": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ], + "VedAstro": [ + 4, + 7, + 3, + 4, + 2, + 1, + 4, + 5, + 2, + 3, + 2, + 2 + ], + "jyotishganit": [ + 4, + 7, + 4, + 4, + 3, + 0, + 4, + 5, + 1, + 4, + 1, + 2 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro" + ], + "field": "Mercury", + "local_value": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ], + "oracle_values": { + "PyJHora_JHora": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ], + "VedAstro": [ + 5, + 5, + 4, + 5, + 3, + 2, + 7, + 4, + 6, + 8, + 2, + 3 + ], + "jyotishganit": [ + 5, + 5, + 5, + 4, + 4, + 2, + 6, + 5, + 5, + 8, + 2, + 3 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro" + ], + "field": "Jupiter", + "local_value": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ], + "oracle_values": { + "PyJHora_JHora": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ], + "VedAstro": [ + 7, + 5, + 3, + 3, + 4, + 7, + 5, + 4, + 4, + 6, + 4, + 4 + ], + "jyotishganit": [ + 7, + 5, + 3, + 3, + 4, + 7, + 4, + 5, + 4, + 6, + 5, + 3 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Venus", + "local_value": [ + 4, + 3, + 5, + 4, + 3, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ], + "oracle_values": { + "PyJHora_JHora": [ + 4, + 3, + 5, + 4, + 3, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ], + "VedAstro": [ + 4, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 6, + 6, + 6 + ], + "jyotishganit": [ + 4, + 3, + 5, + 3, + 4, + 5, + 4, + 3, + 5, + 5, + 5, + 6 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro" + ], + "field": "Saturn", + "local_value": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ], + "oracle_values": { + "PyJHora_JHora": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ], + "VedAstro": [ + 1, + 5, + 2, + 0, + 5, + 4, + 4, + 4, + 4, + 2, + 4, + 4 + ], + "jyotishganit": [ + 1, + 5, + 3, + 0, + 6, + 3, + 4, + 5, + 3, + 3, + 3, + 3 + ] + }, + "section": "ashtakavarga_bav", + "truth_status": "unresolved" + }, + { + "category": "ashtakavarga_table_or_contributor_variant", + "closure_requirement": "Compare contributor tables, Lagna inclusion, shodhana state, and BAV/SAV row semantics.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "12_sign_scores", + "local_value": [ + 28, + 36, + 28, + 21, + 29, + 24, + 31, + 33, + 26, + 35, + 22, + 24 + ], + "oracle_values": { + "PyJHora_JHora": [ + 28, + 36, + 28, + 21, + 29, + 24, + 31, + 33, + 26, + 35, + 22, + 24 + ], + "VedAstro": [ + 24, + 28, + 37, + 28, + 19, + 30, + 24, + 31, + 32, + 27, + 35, + 22 + ], + "jyotishganit": [ + 28, + 37, + 28, + 19, + 30, + 24, + 31, + 32, + 27, + 35, + 22, + 24 + ] + }, + "section": "ashtakavarga_sav", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Sun", + "local_value": 480.62, + "oracle_values": { + "PyJHora_JHora": 444.63, + "VedAstro": 395.06, + "jyotishganit": 422.007 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Sun.sthana", + "local_value": 217.09, + "oracle_values": { + "PyJHora_JHora": 217.09, + "VedAstro": 202.0874074074074, + "jyotishganit": 153.341 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Sun.kala", + "local_value": 140.98, + "oracle_values": { + "PyJHora_JHora": 140.99, + "VedAstro": 128.0507461419753, + "jyotishganit": 148.893 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Sun.dig", + "local_value": 24.81, + "oracle_values": { + "PyJHora_JHora": 24.81, + "VedAstro": 24.810925925925925, + "jyotishganit": 29.175 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Sun.chesta", + "local_value": 36.0, + "oracle_values": { + "PyJHora_JHora": 0.0, + "VedAstro": 0.0, + "jyotishganit": 37.06 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Sun.drik", + "local_value": 1.74, + "oracle_values": { + "PyJHora_JHora": 1.74, + "VedAstro": -19.891041666666666, + "jyotishganit": -6.462 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Moon", + "local_value": 584.26, + "oracle_values": { + "PyJHora_JHora": 562.89, + "VedAstro": 421.8, + "jyotishganit": 420.655 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Moon.sthana", + "local_value": 246.34, + "oracle_values": { + "PyJHora_JHora": 246.34, + "VedAstro": 236.69703703703703, + "jyotishganit": 141.341 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Moon.kala", + "local_value": 149.61, + "oracle_values": { + "PyJHora_JHora": 149.58, + "VedAstro": 159.53537962962963, + "jyotishganit": 183.112 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Moon.dig", + "local_value": 95.48, + "oracle_values": { + "PyJHora_JHora": 95.48, + "VedAstro": 52.26824074074074, + "jyotishganit": 20.159 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Moon.chesta", + "local_value": 21.34, + "oracle_values": { + "PyJHora_JHora": 0.0, + "VedAstro": 0.0, + "jyotishganit": 10.667 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Moon.drik", + "local_value": 20.06, + "oracle_values": { + "PyJHora_JHora": 20.06, + "VedAstro": -18.131319444444447, + "jyotishganit": 13.946 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mars", + "local_value": 456.19, + "oracle_values": { + "PyJHora_JHora": 533.18, + "VedAstro": 397.89, + "jyotishganit": 438.227 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Mars.sthana", + "local_value": 198.63, + "oracle_values": { + "PyJHora_JHora": 198.63, + "VedAstro": 221.28777777777776, + "jyotishganit": 232.378 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mars.kala", + "local_value": 127.92, + "oracle_values": { + "PyJHora_JHora": 127.93, + "VedAstro": 148.2544363425926, + "jyotishganit": 106.407 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Mars.dig", + "local_value": 77.41, + "oracle_values": { + "PyJHora_JHora": 77.41, + "VedAstro": 2.3666666666666667, + "jyotishganit": 46.955 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mars.chesta", + "local_value": 21.6, + "oracle_values": { + "PyJHora_JHora": 98.58, + "VedAstro": 21.737741162570256, + "jyotishganit": 22.864 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Mars.drik", + "local_value": 13.49, + "oracle_values": { + "PyJHora_JHora": 13.49, + "VedAstro": -12.978854166666672, + "jyotishganit": 12.483 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mercury", + "local_value": 365.28, + "oracle_values": { + "PyJHora_JHora": 363.24, + "VedAstro": 376.04, + "jyotishganit": 241.84 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Mercury.sthana", + "local_value": 167.96, + "oracle_values": { + "PyJHora_JHora": 167.96, + "VedAstro": 182.95805555555555, + "jyotishganit": 111.704 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mercury.kala", + "local_value": 121.79, + "oracle_values": { + "PyJHora_JHora": 121.78, + "VedAstro": 126.59104166666665, + "jyotishganit": 72.089 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Mercury.dig", + "local_value": 12.64, + "oracle_values": { + "PyJHora_JHora": 12.64, + "VedAstro": 12.643148148148148, + "jyotishganit": 7.954 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mercury.chesta", + "local_value": 47.8, + "oracle_values": { + "PyJHora_JHora": 45.78, + "VedAstro": 47.51070370816793, + "jyotishganit": 35.368 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Mercury.drik", + "local_value": -10.62, + "oracle_values": { + "PyJHora_JHora": -10.63, + "VedAstro": -19.363888888888884, + "jyotishganit": -10.985 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter", + "local_value": 569.4, + "oracle_values": { + "PyJHora_JHora": 596.89, + "VedAstro": 427.22, + "jyotishganit": 596.043 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter.sthana", + "local_value": 201.81, + "oracle_values": { + "PyJHora_JHora": 201.8, + "VedAstro": 213.06314814814814, + "jyotishganit": 226.178 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter.kala", + "local_value": 199.32, + "oracle_values": { + "PyJHora_JHora": 199.31, + "VedAstro": 141.64764506172838, + "jyotishganit": 250.984 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter.dig", + "local_value": 80.59, + "oracle_values": { + "PyJHora_JHora": 80.6, + "VedAstro": 6.773333333333333, + "jyotishganit": 44.095 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter.chesta", + "local_value": 46.49, + "oracle_values": { + "PyJHora_JHora": 74.0, + "VedAstro": 46.05491068568356, + "jyotishganit": 36.424 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Jupiter.drik", + "local_value": 6.9, + "oracle_values": { + "PyJHora_JHora": 6.89, + "VedAstro": -14.818125000000006, + "jyotishganit": 4.072 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Venus", + "local_value": 406.93, + "oracle_values": { + "PyJHora_JHora": 408.64, + "VedAstro": 402.56, + "jyotishganit": 306.501 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Venus.sthana", + "local_value": 154.06, + "oracle_values": { + "PyJHora_JHora": 154.06, + "VedAstro": 210.05953703703705, + "jyotishganit": 112.816 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Venus.kala", + "local_value": 113.45, + "oracle_values": { + "PyJHora_JHora": 113.44, + "VedAstro": 97.43954166666667, + "jyotishganit": 63.186 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Venus.dig", + "local_value": 69.95, + "oracle_values": { + "PyJHora_JHora": 69.95, + "VedAstro": 29.630740740740737, + "jyotishganit": 45.684 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Venus.chesta", + "local_value": 29.36, + "oracle_values": { + "PyJHora_JHora": 31.08, + "VedAstro": 30.84685510809705, + "jyotishganit": 46.466 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Venus.drik", + "local_value": -2.75, + "oracle_values": { + "PyJHora_JHora": -2.75, + "VedAstro": -8.51038194444445, + "jyotishganit": -4.511 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "derived_total_from_component_variants", + "closure_requirement": "Do not arbitrate totals until all six component variants and Virupa/Rupa units are aligned.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Saturn", + "local_value": 430.05, + "oracle_values": { + "PyJHora_JHora": 429.42, + "VedAstro": 448.58, + "jyotishganit": 322.113 + }, + "section": "shadbala_total", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Saturn.sthana", + "local_value": 218.61, + "oracle_values": { + "PyJHora_JHora": 218.61, + "VedAstro": 239.23268518518518, + "jyotishganit": 151.104 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Saturn.kala", + "local_value": 136.76, + "oracle_values": { + "PyJHora_JHora": 136.77, + "VedAstro": 140.7160763888889, + "jyotishganit": 71.929 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Saturn.dig", + "local_value": 19.63, + "oracle_values": { + "PyJHora_JHora": 19.62, + "VedAstro": 19.623796296296295, + "jyotishganit": 24.313 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "PyJHora_JHora", + "VedAstro", + "jyotishganit" + ], + "field": "Saturn.chesta", + "local_value": 36.99, + "oracle_values": { + "PyJHora_JHora": 36.36, + "VedAstro": 39.438983295527784, + "jyotishganit": 58.137 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + }, + { + "category": "shadbala_formula_variant", + "closure_requirement": "Compare component formula, units, local solar context, aspect model, and Chesta lineage before totals.", + "differing_engines": [ + "VedAstro", + "jyotishganit" + ], + "field": "Saturn.drik", + "local_value": 9.49, + "oracle_values": { + "PyJHora_JHora": 9.49, + "VedAstro": 0.9979513888888878, + "jyotishganit": 8.06 + }, + "section": "shadbala_components", + "truth_status": "unresolved" + } + ], + "scope": "three_engine_field_level_mismatch_arbitration", + "status": "classified_unresolved", + "truth_policy": "no_majority_vote", + "unclassified_count": 0 +} diff --git a/references/oracle/vedastro_identity_archive_2026_07_19.json b/references/oracle/vedastro_identity_archive_2026_07_19.json new file mode 100644 index 00000000..548a9f42 --- /dev/null +++ b/references/oracle/vedastro_identity_archive_2026_07_19.json @@ -0,0 +1,76 @@ +{ + "boundary": "This archive fixes a NuGet self-host candidate identity only; it does not prove the hosted API is running this package or the same method semantics.", + "catalog_commit_id": "c707690b-f7bd-4813-a2cb-876e943e9667", + "catalog_commit_timestamp": "2023-03-22T22:24:11.811524Z", + "dependencies": [ + { + "id": "ByteSize", + "range": "[2.1.1, )", + "target_framework": "net7.0" + }, + { + "id": "Google.Apis", + "range": "[1.51.0, )", + "target_framework": "net7.0" + }, + { + "id": "Google.Apis.Auth", + "range": "[1.51.0, )", + "target_framework": "net7.0" + }, + { + "id": "Google.Apis.Calendar.v3", + "range": "[1.51.0.2237, )", + "target_framework": "net7.0" + }, + { + "id": "Google.Apis.Core", + "range": "[1.51.0, )", + "target_framework": "net7.0" + }, + { + "id": "Microsoft.AspNetCore.Components.WebAssembly", + "range": "[6.0.11, )", + "target_framework": "net7.0" + }, + { + "id": "Microsoft.Extensions.Caching.Memory", + "range": "[6.0.1, )", + "target_framework": "net7.0" + }, + { + "id": "Microsoft.JSInterop", + "range": "[6.0.11, )", + "target_framework": "net7.0" + }, + { + "id": "SwissEphNet", + "range": "[2.8.0.2, )", + "target_framework": "net7.0" + } + ], + "hosted_api_blocker": "api.vedastro.org does not expose a verified build commit, package hash, DLL hash, assembly version, container digest, or method-semantics contract.", + "hosted_api_status": "blocked", + "license": "MIT", + "nuget_catalog_url": "https://api.nuget.org/v3/catalog0/data/2023.03.22.22.24.11/vedastro.library.1.2.0.json", + "nuget_registration_url": "https://api.nuget.org/v3/registration5-semver1/vedastro.library/1.2.0.json", + "package": "VedAstro.Library", + "package_content_url": "https://api.nuget.org/v3-flatcontainer/vedastro.library/1.2.0/vedastro.library.1.2.0.nupkg", + "package_hash": "36gq1hggII7Ho27TSL2B85P0BQD1zsAE1xHoJgUrM9V5rnUJGXUZibDlutkXR9gJ+aMr8UCQa2771MfD6BAscA==", + "package_hash_algorithm": "SHA512", + "package_size": 230836, + "project_url": "https://vedastro.org/", + "published": "2023-03-22T22:22:01.81Z", + "required_self_host_evidence": { + "assembly_version": null, + "container_image_digest": null, + "dll_sha256": null, + "nuget_package_hash": "36gq1hggII7Ho27TSL2B85P0BQD1zsAE1xHoJgUrM9V5rnUJGXUZibDlutkXR9gJ+aMr8UCQa2771MfD6BAscA==", + "public_method_inventory": null, + "source_commit": null + }, + "scope": "vedastro_reproducible_identity_archive", + "self_host_candidate_status": "reproducible_package_identity_archived", + "truth_upgrade_gate": "blocked_until_source_commit_dll_hash_assembly_version_method_inventory_and_image_digest_are_archived", + "version": "1.2.0" +} diff --git a/tests/test_shadbala_av_normative_benchmark_plan.py b/tests/test_shadbala_av_normative_benchmark_plan.py new file mode 100644 index 00000000..2d830996 --- /dev/null +++ b/tests/test_shadbala_av_normative_benchmark_plan.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +PLAN = ROOT / "docs" / "research" / "shadbala_av_normative_benchmark_plan_2026_07_19.md" + + +def test_shadbala_av_plan_keeps_current_closure_separate_from_global_truth() -> None: + text = PLAN.read_text(encoding="utf-8") + + for token in [ + "current target set: `external_verified`", + "`can_claim_shadbala_absolute_closure = true`", + "`production_tuning_allowed = false`", + "must not claim universal Shadbala or AV truth", + "60 mismatches, 60 classified, 0 unclassified", + "no majority vote", + ]: + assert token in text + + +def test_shadbala_av_plan_requires_component_units_and_license_boundaries() -> None: + text = PLAN.read_text(encoding="utf-8") + + for token in [ + "VP Jain", + "Xalen", + "PyJHora/JHora", + "jyotishganit", + "Virupa vs Rupa must be explicit", + "`sthana`", + "`dig`", + "`kala`", + "`chesta`", + "`naisargika`", + "`drik`", + "`total_rupa`", + "AGPL implementation code", + "Independent ephemeris mode", + ]: + assert token in text diff --git a/tests/test_three_engine_mismatch_arbitrator.py b/tests/test_three_engine_mismatch_arbitrator.py index 2d697631..be3ac9d0 100644 --- a/tests/test_three_engine_mismatch_arbitrator.py +++ b/tests/test_three_engine_mismatch_arbitrator.py @@ -33,3 +33,15 @@ def test_vedastro_only_d2_difference_is_endpoint_semantics(tmp_path: Path) -> No row = arbitrate_manifest(path)["rows"][0] assert row["category"] == "endpoint_or_varga_semantics" assert row["differing_engines"] == ["VedAstro"] + + +def test_commercial_receives_mismatch_status_not_raw_truth_upgrade() -> None: + report = json.loads((ROOT / "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json").read_text(encoding="utf-8")) + markdown = (ROOT / "docs/research/three_engine_mismatch_arbitration_2026_07_19.md").read_text(encoding="utf-8") + + assert report["mismatch_count"] == 60 + assert report["classified_count"] == 60 + assert report["unclassified_count"] == 0 + assert report["truth_policy"] == "no_majority_vote" + assert "commercial_sync: `status_and_claim_boundary_only`" in markdown + assert "Do not copy raw research debt into commercial runtime" in markdown diff --git a/tests/test_vedastro_identity_closure_plan.py b/tests/test_vedastro_identity_closure_plan.py new file mode 100644 index 00000000..ecbbe476 --- /dev/null +++ b/tests/test_vedastro_identity_closure_plan.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DOC = ROOT / "docs" / "research" / "vedastro_identity_closure_plan_2026_07_19.md" + + +def test_vedastro_identity_plan_keeps_hosted_output_observation_only() -> None: + text = DOC.read_text(encoding="utf-8") + + for token in [ + "VedAstro hosted API remains observation-only", + "build identity, method semantics, and deployment version are not archived", + "does not prove which side is true", + "tune production predictions from hosted output with unknown build identity", + "silently prefer VedAstro or local output by majority vote", + ]: + assert token in text + + +def test_vedastro_identity_plan_requires_self_host_supply_chain_fields() -> None: + text = DOC.read_text(encoding="utf-8") + + for token in [ + "source commit", + "NuGet package hash", + "DLL SHA-256", + "assembly version", + "public method inventory", + "container image digest", + "required_self_host_evidence", + "truth_upgrade_gate", + ]: + assert token in text From 82e7b0a95a3e2d3b6250abac0bec51128aab45e2 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Sun, 19 Jul 2026 08:59:33 +0800 Subject: [PATCH 4/4] feat: sync astrology provenance gates --- ...se_timing_optimization_audit_2026_07_19.md | 47 ++++++++ ..._component_provenance_report_2026_07_19.md | 48 ++++++++ frontend/src/mastra/index.ts | 4 +- frontend/tests/consultation-context.test.ts | 6 + ...ponent_provenance_registry_2026_07_19.json | 110 ++++++++++++++++++ ...eproducible_build_identity_2026_07_17.json | 39 +++++++ scripts/day_level_negative_holdout_intake.py | 54 +++++++++ scripts/shadbala_av_component_registry.py | 95 +++++++++++++++ scripts/vedastro_reproducible_build_probe.py | 101 ++++++++++++++++ ...est_real_case_timing_optimization_audit.py | 24 ++++ tests/test_shadbala_av_component_registry.py | 63 ++++++++++ .../test_vedastro_reproducible_build_probe.py | 33 ++++++ 12 files changed, 623 insertions(+), 1 deletion(-) create mode 100644 docs/research/real_case_timing_optimization_audit_2026_07_19.md create mode 100644 docs/research/shadbala_av_component_provenance_report_2026_07_19.md create mode 100644 references/oracle/shadbala_av_component_provenance_registry_2026_07_19.json create mode 100644 references/oracle/vedastro_reproducible_build_identity_2026_07_17.json create mode 100644 scripts/day_level_negative_holdout_intake.py create mode 100644 scripts/shadbala_av_component_registry.py create mode 100644 scripts/vedastro_reproducible_build_probe.py create mode 100644 tests/test_real_case_timing_optimization_audit.py create mode 100644 tests/test_shadbala_av_component_registry.py create mode 100644 tests/test_vedastro_reproducible_build_probe.py diff --git a/docs/research/real_case_timing_optimization_audit_2026_07_19.md b/docs/research/real_case_timing_optimization_audit_2026_07_19.md new file mode 100644 index 00000000..9fb4df51 --- /dev/null +++ b/docs/research/real_case_timing_optimization_audit_2026_07_19.md @@ -0,0 +1,47 @@ +# Real-case timing optimization audit — 2026-07-19 + +## Current verified layer + +Positive-event replay is healthy: + +| Manifest | Cases | Ready | Boundary | +|---|---:|---:|---| +| `references/real_case_calibration/replay_manifest.json` | 10 | 10 | known positive events only | +| `references/real_case_calibration/replay_manifest_holdout_v2.json` | 10 | 10 | blind positive holdout; not specificity proof | +| `references/real_case_calibration/replay_manifest_probe3_v2.json` | 3 | 3 | probe batch only | + +This verifies technical recall around known dated events. It does not verify day/month predictive specificity. + +## Current blocked layer + +Day-level negative holdout remains empty: + +- `references/real_case_calibration/day_level_holdout_v3_preregistration.json` +- `annotation_count = 0` +- `negative_count = 0` +- `positive_count = 0` +- `production_tuning_allowed = false` +- `status = awaiting_independent_labels` + +Existing 40 control dates remain diagnostic only because they were already observed before preregistration and are not independent human-reviewed labels. + +Pilot source queue exists at `references/real_case_calibration/day_level_holdout_v3_pilot_source_queue_2026_07_19.json`. + +Boundary: the queue contains public-source candidates for Steve Jobs, Barack Obama, and Albert Einstein. It is not a holdout manifest and must not be used for timing evaluation until independent adjudication converts rows into frozen annotations. + +## Optimization needed + +1. Collect independent human-labeled non-event intervals for the same subjects/domains. +2. Freeze labels before scoring. +3. Run candidate day/month ranking over positive and negative windows together. +4. Promote timing claims only if positive windows rank above negative windows under frozen rules. + +Until then, precise day/month output must stay: + +- `timing_precision = candidate_day_window` +- `claim_status = exploratory_unvalidated` +- `production_tuning_allowed = false` + +Allowed UX: ranked candidate windows w/ evidence and confidence caps. + +Forbidden UX: packaging candidate dates as verified event promises. diff --git a/docs/research/shadbala_av_component_provenance_report_2026_07_19.md b/docs/research/shadbala_av_component_provenance_report_2026_07_19.md new file mode 100644 index 00000000..3f691494 --- /dev/null +++ b/docs/research/shadbala_av_component_provenance_report_2026_07_19.md @@ -0,0 +1,48 @@ +# Shadbala / AV component provenance report — 2026-07-19 + +Source registry: `references/oracle/shadbala_av_component_provenance_registry_2026_07_19.json` + +Status: `classified_unresolved` + +Truth policy: `method_variant_not_majority_vote` + +Production tuning: `false` + +## Component families + +| Category | Rows | Component family | Allowed claim | +|---|---:|---|---| +| `shadbala_formula_variant` | 35 | Shadbala six-force components | `component_method_variant` | +| `derived_total_from_component_variants` | 7 | Shadbala total | `derived_total_blocked_until_components_close` | +| `ashtakavarga_table_or_contributor_variant` | 8 | Ashtakavarga tables | `table_variant` | +| `endpoint_or_varga_semantics` | 10 | Varga endpoint semantics | `current_target_observation_only` | + +## Unit / source obligations + +- Sthana, Dig, Kala, Chesta, Naisargika, and Drik Bala each require formula source, unit, cap/floor, and worked example. +- Total Rupa/Virupa cannot be arbitrated until component units close. +- Ashtakavarga BAV/SAV requires contributor table, shodhana state, Lagna inclusion, and row/column schema. +- VedAstro varga endpoint rows require explicit endpoint/method semantics before truth arbitration. + +## Current claim boundary + +Allowed: + +- current target observations; +- named method variants; +- component-level unresolved status; +- user-safe confidence caps. + +Forbidden: + +- global Shadbala/AV truth; +- production tuning; +- majority-vote truth; +- copying raw research debt into runtime. + +## Next source-evidence queue + +1. Map 35 Shadbala component rows to `sthana/dig/kala/chesta/naisargika/drik`. +2. Attach VP Jain / Xalen / jyotishganit / PyJHora observation source per component. +3. Mark each component as `source_explicit`, `method_variant`, or `insufficient_evidence`. +4. Recompute totals only after component status closes. diff --git a/frontend/src/mastra/index.ts b/frontend/src/mastra/index.ts index ff4214d5..32ffd360 100644 --- a/frontend/src/mastra/index.ts +++ b/frontend/src/mastra/index.ts @@ -77,6 +77,7 @@ export function toAgentConsultationContext(data: JsonRecord) { houses: chart.houses, dasha: chart.dasha, shadbala: chart.shadbala, + ashtakavarga: chart.ashtakavarga, yogas: chart.yogas, }, local_layers: { @@ -120,9 +121,10 @@ When reference_transparency is present: - Treat similarity.timing_state as authoritative: status=matched means Vimshottari MD and AD both match; partial_match means only Vimshottari MD matches. Read narayana_status and transit_status separately; never infer either from Vimshottari status. A transit_status match means only Jupiter and Saturn relative houses match, not that every transit matches. - When similar_public_cases.coverage.requested_uncovered_domains is non-empty, say the current public-case catalog does not yet cover those themes; do not infer that no comparable real-world case exists. - When method_variants applies, present parallel methods and their source paths rather than silently picking one result as the only truth. +- Treat Shadbala/Ashtakavarga component differences under production_tuning_allowed=false as method boundaries, not absolute calculation errors. Use no_majority_vote and method_variant_not_majority_vote: do not decide truth by engine count, and do not say one school is wrong unless a pinned authoritative worked example is present. - If should_lead_with_limitations is false, do not lead with limitations. If a limitation is relevant, put it in one short sentence at the end. - Only say the chart calculation failed when hard_blockers is non-empty. -- Never claim D9, D10, A10, UL, or Narayana Dasha is missing when it appears in available_layers or local_layers. +- Never claim D2, D11, D9, D10, A10, UL, or Narayana Dasha is missing when it appears in available_layers, chart, or local_layers. Usually answer in 2-5 short paragraphs. Ask one clarifying question only when the user's intent is genuinely unclear. After every substantive answer, append exactly two hidden blocks in this order and nothing after the second block: diff --git a/frontend/tests/consultation-context.test.ts b/frontend/tests/consultation-context.test.ts index 52605011..a89b07d2 100644 --- a/frontend/tests/consultation-context.test.ts +++ b/frontend/tests/consultation-context.test.ts @@ -7,6 +7,7 @@ test("passes transparent public-case references into the agent context", () => { assert.match(source, /reference_transparency:\s*record\(data\.reference_transparency\)/); assert.match(source, /vedastro_gateway:\s*record\(data\.vedastro_gateway\)/); + assert.match(source, /ashtakavarga:\s*chart\.ashtakavarga/); assert.match(source, /high_similarity_public_references_available/); assert.match(source, /requested_uncovered_domains/); assert.match(source, /public_context_only/); @@ -16,4 +17,9 @@ test("passes transparent public-case references into the agent context", () => { assert.match(source, /transit_status/); assert.match(source, /Jupiter and Saturn relative houses/); assert.match(source, /exact_triggers as technical trigger points/); + assert.match(source, /production_tuning_allowed=false/); + assert.match(source, /no_majority_vote/); + assert.match(source, /method_variant_not_majority_vote/); + assert.match(source, /Shadbala\/Ashtakavarga component differences/); + assert.match(source, /D2, D11/); }); diff --git a/references/oracle/shadbala_av_component_provenance_registry_2026_07_19.json b/references/oracle/shadbala_av_component_provenance_registry_2026_07_19.json new file mode 100644 index 00000000..ff844a05 --- /dev/null +++ b/references/oracle/shadbala_av_component_provenance_registry_2026_07_19.json @@ -0,0 +1,110 @@ +{ + "production_tuning_allowed": false, + "registry": [ + { + "allowed_claim": "table_variant", + "category": "ashtakavarga_table_or_contributor_variant", + "component_family": "ashtakavarga", + "next_evidence_required": "public worked table with same contributor semantics and row/column schema.", + "row_count": 8, + "sample_fields": [ + "Sun", + "Moon", + "Mars", + "Mercury", + "Jupiter", + "Venus", + "Saturn", + "12_sign_scores" + ], + "sections": [ + "ashtakavarga_bav", + "ashtakavarga_sav" + ], + "truth_status": "classified_unresolved", + "unit_contract": "BAV/SAV tables must name contributor set, shodhana state, and Lagna inclusion." + }, + { + "allowed_claim": "derived_total_blocked_until_components_close", + "category": "derived_total_from_component_variants", + "component_family": "shadbala_total", + "next_evidence_required": "close sthana/dig/kala/chesta/naisargika/drik first, then recompute totals.", + "row_count": 7, + "sample_fields": [ + "Sun", + "Moon", + "Mars", + "Mercury", + "Jupiter", + "Venus", + "Saturn" + ], + "sections": [ + "shadbala_total" + ], + "truth_status": "classified_unresolved", + "unit_contract": "Total Rupa/Virupa cannot be arbitrated before component units close." + }, + { + "allowed_claim": "current_target_observation_only", + "category": "endpoint_or_varga_semantics", + "component_family": "varga_endpoint", + "next_evidence_required": "identified endpoint contract for D2/D4/D9/D10 ayanamsa/node/method.", + "row_count": 10, + "sample_fields": [ + "Sun.sign", + "Moon.sign", + "Mars.sign", + "Mercury.sign", + "Jupiter.sign", + "Venus.sign", + "Saturn.sign", + "Moon.sign" + ], + "sections": [ + "D10", + "D2", + "D4", + "D9" + ], + "truth_status": "classified_unresolved", + "unit_contract": "Sign values only; endpoint must prove requested varga/method semantics." + }, + { + "allowed_claim": "component_method_variant", + "category": "shadbala_formula_variant", + "component_family": "shadbala_components", + "next_evidence_required": "worked example or source text for each six-force component formula and unit.", + "row_count": 35, + "sample_fields": [ + "Sun.sthana", + "Sun.kala", + "Sun.dig", + "Sun.chesta", + "Sun.drik", + "Moon.sthana", + "Moon.kala", + "Moon.dig" + ], + "sections": [ + "shadbala_components" + ], + "truth_status": "classified_unresolved", + "unit_contract": "Virupa/Rupa component unit must be explicit before parity claims." + } + ], + "scope": "shadbala_av_component_provenance_registry", + "source_arbitration": "references/oracle/three_engine_mismatch_arbitration_2026_07_19.json", + "status": "classified_unresolved", + "summary": { + "category_counts": { + "ashtakavarga_table_or_contributor_variant": 8, + "derived_total_from_component_variants": 7, + "endpoint_or_varga_semantics": 10, + "shadbala_formula_variant": 35 + }, + "registry_count": 4, + "source_mismatch_count": 60 + }, + "truth_policy": "method_variant_not_majority_vote" +} diff --git a/references/oracle/vedastro_reproducible_build_identity_2026_07_17.json b/references/oracle/vedastro_reproducible_build_identity_2026_07_17.json new file mode 100644 index 00000000..1004a0c1 --- /dev/null +++ b/references/oracle/vedastro_reproducible_build_identity_2026_07_17.json @@ -0,0 +1,39 @@ +{ + "base_images": [ + "mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated7.0", + "mcr.microsoft.com/dotnet/sdk:7.0" + ], + "boundary": "Identifies the pinned local candidate only; it does not identify api.vedastro.org.", + "dockerfile_path": "API/Dockerfile", + "dockerfile_sha256": "5a22647f6402e22e583c03eedde1a2cee0661baa262e07efe36c4fcab90203b2", + "image_id": "", + "project_file_hashes": { + "API/API.csproj": "320c08b14d3b89f453c855ba08e4bb4cc03c379f540e7db0ffc07035b2fda173", + "APITester/APITester.csproj": "677e6b436b830fcd4495643b03f3a7a74f06601eac8140aa27c07f9377cecd7c", + "Console/Console.csproj": "c93f66eed9bb2ef4da31642802e00876f7eb733d0c4b06802774e678ddff4a78", + "Desktop/APILauncher/APILauncher.csproj": "600f96b7cb38786451438265fc9db06f6a8a4e3efdb8effb1d8fa4f69d9630b2", + "Desktop/Desktop.csproj": "bf4f16228da6708be85955349557810dcff2b550473e2cc504c0432e96d11347", + "Desktop/Windows/Desktop_Windows.csproj": "b118be0e8025862c27e5bfb4beb4584db19009695b7f6995ee2c1ad0e073c12f", + "DocToEmbeddings/DocToEmbeddings.csproj": "a3b92db8e84a5cad287fc3d769c7b4f7ad4992ff30b18cf2b47a246fcabc2559", + "LLMCoder/LLMCoder.csproj": "1d3905a1059e2dd5b677c1e10b6d9b8e256a7a8189dc013e5c1147d1cc2106e6", + "Library/Library.csproj": "d74bc184a225c77247c29ee74542f3ba4daf67912d32b587da7a30a3aa78a2b2", + "LibraryTests/LibraryTests.csproj": "2fe83ecbb72455dcd0df2e4744a6407acba526b61b747a835bc5944164626f35", + "MatchMLPipeline/MatchMLPipeline.csproj": "610a80be75226cdfa4ad70ea278d1bd14782856445c0dac03bcf47ccd62065fb", + "MigrateGeoLocationData/MigrateGeoLocationData.csproj": "ded1da7b81c85fa44b61df66b376ae1fea94230283051f5f6f1adf0373d395e6", + "Others/ArchivedCode/APITests/APITests.csproj": "cf687a39e84fb91bee847e5544a41abb8c3f911c305aa6bf13f78c73f66d1c89", + "Others/ArchivedCode/Compatibility.Desktop/Compatibility.Desktop.csproj": "1102b834b79badf241671faeb799a3f9acc5fa93e6dfe6cddf4bc45bf21a6bda", + "Others/ArchivedCode/Horoscope.Desktop/Horoscope.Desktop.csproj": "1341b72198bf757da183656b02630cb6a416de808acdc1eb75ba82157f07e2a3", + "Others/ArchivedCode/Muhurtha.Desktop/Muhurtha.Desktop.csproj": "4ac800f5bfc468703f62dc2c0a997bf9e67d14d9a7be772f226177eae6caebfb", + "Others/ArchivedCode/Tests/Tests.csproj": "f84e998698fdf6e8f007b9a01be94b07bf8de8877c3ea6679f2c3a4b755e7b0f", + "Others/MLTableGenerator-MK3/MLTableGenerator.csproj": "7dcf5b42246077d47f383cf023fa33e7c5b485b9fd260c949ad579c67e0490ea", + "Publisher/Publisher.csproj": "64622175d2a669f80c788e684d49bfad26eb34c3f2343b4fed862941bc96ae98", + "StaticTableGenerator/StaticTableGenerator.csproj": "a2779e770268badcf8ebd92c20a3e927210429c4621d248dac406d7efff5f6c7", + "Website/Website.csproj": "81b8b712286839f15472afa4dc2b449f6616d421b51ba86ac7c8f21ad2acc257", + "Website_Mobile/Website_Mobile.csproj": "2e0e1232a24295326ea26cbb5c48cd1b01efd8e20bf6c9d9e41b798746219e48" + }, + "repo_digests": [], + "scope": "vedastro_reproducible_build_identity", + "source_commit": "1f3a46482ab98eb414ab03fa44496a37d35a3a96", + "source_root": "/private/tmp/VedAstro-official", + "status": "source_pinned_image_not_built" +} diff --git a/scripts/day_level_negative_holdout_intake.py b/scripts/day_level_negative_holdout_intake.py new file mode 100644 index 00000000..c1e39682 --- /dev/null +++ b/scripts/day_level_negative_holdout_intake.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Append independently sourced day-level timing holdout annotations.""" +from __future__ import annotations + +import argparse +import json +from datetime import datetime, timezone +from pathlib import Path + +from scripts.day_level_holdout_validator import REQUIRED, validate + + +def _row_errors(row: dict, prohibited: set[str]) -> list[dict]: + errors = [] + for key in sorted(REQUIRED - set(row)): + errors.append({"field": key, "error": "missing"}) + if row.get("label") not in {"target_event", "no_target_event"}: + errors.append({"field": "label", "error": "invalid"}) + if not str(row.get("source_url") or "").startswith(("https://", "http://")): + errors.append({"field": "source_url", "error": "not_public_url"}) + if row.get("independent_human_reviewed") is not True: + errors.append({"field": "independent_human_reviewed", "error": "not_independently_human_reviewed"}) + if row.get("source_path") in prohibited: + errors.append({"field": "source_path", "error": "prohibited_tuning_source"}) + return errors + + +def append_annotation(path: Path, row: dict) -> dict: + data = json.loads(path.read_text(encoding="utf-8")) + prohibited = set(data.get("prohibited_tuning_data") or []) + errors = _row_errors(row, prohibited) + if errors: + return {"appended": False, "errors": errors, "validation": validate(path)} + next_row = { + **row, + "frozen_before_scoring": True, + "ingested_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + } + data.setdefault("annotations", []).append(next_row) + path.write_text(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8") + return {"appended": True, "errors": [], "validation": validate(path)} + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("manifest", type=Path) + parser.add_argument("--row-json", required=True, help="One annotation JSON object.") + args = parser.parse_args() + print(json.dumps(append_annotation(args.manifest, json.loads(args.row_json)), ensure_ascii=False, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/shadbala_av_component_registry.py b/scripts/shadbala_av_component_registry.py new file mode 100644 index 00000000..0f924401 --- /dev/null +++ b/scripts/shadbala_av_component_registry.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Build Shadbala/Ashtakavarga component provenance registry from mismatch arbitration.""" +from __future__ import annotations + +import argparse +import json +from collections import Counter, defaultdict +from pathlib import Path +from typing import Any + + +CATEGORY_POLICY = { + "shadbala_formula_variant": { + "component_family": "shadbala_components", + "unit_contract": "Virupa/Rupa component unit must be explicit before parity claims.", + "allowed_claim": "component_method_variant", + "next_evidence_required": "worked example or source text for each six-force component formula and unit.", + }, + "derived_total_from_component_variants": { + "component_family": "shadbala_total", + "unit_contract": "Total Rupa/Virupa cannot be arbitrated before component units close.", + "allowed_claim": "derived_total_blocked_until_components_close", + "next_evidence_required": "close sthana/dig/kala/chesta/naisargika/drik first, then recompute totals.", + }, + "ashtakavarga_table_or_contributor_variant": { + "component_family": "ashtakavarga", + "unit_contract": "BAV/SAV tables must name contributor set, shodhana state, and Lagna inclusion.", + "allowed_claim": "table_variant", + "next_evidence_required": "public worked table with same contributor semantics and row/column schema.", + }, + "endpoint_or_varga_semantics": { + "component_family": "varga_endpoint", + "unit_contract": "Sign values only; endpoint must prove requested varga/method semantics.", + "allowed_claim": "current_target_observation_only", + "next_evidence_required": "identified endpoint contract for D2/D4/D9/D10 ayanamsa/node/method.", + }, +} + + +def build_registry(arbitration_path: str | Path) -> dict[str, Any]: + path = Path(arbitration_path) + arbitration = json.loads(path.read_text(encoding="utf-8")) + grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in arbitration.get("rows") or []: + grouped[row["category"]].append(row) + registry = [] + for category, rows in sorted(grouped.items()): + policy = CATEGORY_POLICY.get(category, { + "component_family": "unknown", + "unit_contract": "unknown", + "allowed_claim": "current_target_observation_only", + "next_evidence_required": "manual provenance review required.", + }) + registry.append({ + "category": category, + "component_family": policy["component_family"], + "row_count": len(rows), + "sections": sorted({str(row.get("section")) for row in rows}), + "sample_fields": [str(row.get("field")) for row in rows[:8]], + "unit_contract": policy["unit_contract"], + "allowed_claim": policy["allowed_claim"], + "next_evidence_required": policy["next_evidence_required"], + "truth_status": "classified_unresolved", + }) + return { + "scope": "shadbala_av_component_provenance_registry", + "source_arbitration": str(path), + "status": "classified_unresolved", + "truth_policy": "method_variant_not_majority_vote", + "production_tuning_allowed": False, + "summary": { + "source_mismatch_count": arbitration.get("mismatch_count", 0), + "registry_count": len(registry), + "category_counts": dict(Counter({row["category"]: row["row_count"] for row in registry})), + }, + "registry": registry, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("arbitration", nargs="?", default="references/oracle/three_engine_mismatch_arbitration_2026_07_19.json") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + registry = build_registry(args.arbitration) + text = json.dumps(registry, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text, encoding="utf-8") + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/vedastro_reproducible_build_probe.py b/scripts/vedastro_reproducible_build_probe.py new file mode 100644 index 00000000..14d3ccf3 --- /dev/null +++ b/scripts/vedastro_reproducible_build_probe.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""Create a secret-free identity contract for a pinned VedAstro container build.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +from pathlib import Path +from typing import Any + + +def _sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _git_commit(root: Path) -> str: + completed = subprocess.run( + ["git", "rev-parse", "HEAD"], cwd=root, text=True, capture_output=True, check=False + ) + return completed.stdout.strip() if completed.returncode == 0 else "" + + +def _inspect_image(tag: str) -> dict[str, Any] | None: + completed = subprocess.run( + ["docker", "image", "inspect", tag], text=True, capture_output=True, check=False + ) + if completed.returncode != 0: + return None + payload = json.loads(completed.stdout) + return payload[0] if isinstance(payload, list) and payload else None + + +def build_identity( + source_root: Path, + *, + source_commit: str | None = None, + image_inspect: dict[str, Any] | None = None, +) -> dict[str, Any]: + source_root = source_root.resolve() + dockerfile = source_root / "API/Dockerfile" + if not dockerfile.is_file(): + raise FileNotFoundError(dockerfile) + docker_text = dockerfile.read_text(encoding="utf-8") + base_images: list[str] = [] + stage_names: set[str] = set() + for match in re.finditer( + r"^FROM\s+([^\s]+)(?:\s+AS\s+([^\s]+))?", + docker_text, + flags=re.MULTILINE | re.IGNORECASE, + ): + image, stage = match.group(1), match.group(2) + if image not in stage_names: + base_images.append(image) + if stage: + stage_names.add(stage) + project_files = sorted(source_root.glob("**/*.csproj")) + project_hashes = { + str(path.relative_to(source_root)): _sha256(path) + for path in project_files + if "/bin/" not in path.as_posix() and "/obj/" not in path.as_posix() + } + image_id = (image_inspect or {}).get("Id", "") + repo_digests = (image_inspect or {}).get("RepoDigests") or [] + return { + "scope": "vedastro_reproducible_build_identity", + "source_root": str(source_root), + "source_commit": source_commit or _git_commit(source_root), + "dockerfile_path": "API/Dockerfile", + "dockerfile_sha256": _sha256(dockerfile), + "base_images": base_images, + "project_file_hashes": project_hashes, + "image_id": image_id, + "repo_digests": repo_digests, + "status": "reproducible_candidate_built" if image_id else "source_pinned_image_not_built", + "boundary": "Identifies the pinned local candidate only; it does not identify api.vedastro.org.", + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("source_root", type=Path) + parser.add_argument("--image-tag", default="") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + report = build_identity( + args.source_root, + image_inspect=_inspect_image(args.image_tag) if args.image_tag else None, + ) + text = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text, encoding="utf-8") + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_real_case_timing_optimization_audit.py b/tests/test_real_case_timing_optimization_audit.py new file mode 100644 index 00000000..2d8456df --- /dev/null +++ b/tests/test_real_case_timing_optimization_audit.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DOC = ROOT / "docs" / "research" / "real_case_timing_optimization_audit_2026_07_19.md" + + +def test_real_case_timing_audit_separates_positive_replay_from_specificity() -> None: + text = DOC.read_text(encoding="utf-8") + + for token in [ + "Positive-event replay is healthy", + "known positive events only", + "does not verify day/month predictive specificity", + "Day-level negative holdout remains empty", + "status = awaiting_independent_labels", + "day_level_holdout_v3_pilot_source_queue_2026_07_19.json", + "not a holdout manifest", + "production_tuning_allowed = false", + "claim_status = exploratory_unvalidated", + "Forbidden UX: packaging candidate dates as verified event promises", + ]: + assert token in text diff --git a/tests/test_shadbala_av_component_registry.py b/tests/test_shadbala_av_component_registry.py new file mode 100644 index 00000000..c090593e --- /dev/null +++ b/tests/test_shadbala_av_component_registry.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import json +from pathlib import Path + +from scripts.shadbala_av_component_registry import build_registry + +ROOT = Path(__file__).resolve().parents[1] +ARBITRATION = ROOT / "references" / "oracle" / "three_engine_mismatch_arbitration_2026_07_19.json" + + +def test_component_registry_groups_shadbala_and_av_mismatches_without_truth_upgrade() -> None: + registry = build_registry(ARBITRATION) + + assert registry["scope"] == "shadbala_av_component_provenance_registry" + assert registry["truth_policy"] == "method_variant_not_majority_vote" + assert registry["status"] == "classified_unresolved" + assert registry["production_tuning_allowed"] is False + assert registry["summary"]["source_mismatch_count"] == 60 + assert registry["summary"]["registry_count"] >= 4 + + categories = {row["category"] for row in registry["registry"]} + assert "shadbala_formula_variant" in categories + assert "derived_total_from_component_variants" in categories + assert "ashtakavarga_table_or_contributor_variant" in categories + + for row in registry["registry"]: + assert row["allowed_claim"] in { + "current_target_observation_only", + "component_method_variant", + "table_variant", + "derived_total_blocked_until_components_close", + } + assert row["unit_contract"] + assert row["next_evidence_required"] + + +def test_component_registry_json_artifact_matches_source_counts() -> None: + artifact = ROOT / "references" / "oracle" / "shadbala_av_component_provenance_registry_2026_07_19.json" + data = json.loads(artifact.read_text(encoding="utf-8")) + + assert data["summary"]["source_mismatch_count"] == 60 + assert data["summary"]["category_counts"]["shadbala_formula_variant"] == 35 + assert data["summary"]["category_counts"]["derived_total_from_component_variants"] == 7 + assert data["summary"]["category_counts"]["ashtakavarga_table_or_contributor_variant"] == 8 + assert data["production_tuning_allowed"] is False + + +def test_component_provenance_markdown_report_is_human_readable() -> None: + report = ROOT / "docs" / "research" / "shadbala_av_component_provenance_report_2026_07_19.md" + text = report.read_text(encoding="utf-8") + + for token in [ + "Shadbala / AV component provenance report", + "method_variant_not_majority_vote", + "Production tuning: `false`", + "`shadbala_formula_variant`", + "`derived_total_from_component_variants`", + "`ashtakavarga_table_or_contributor_variant`", + "Total Rupa/Virupa cannot be arbitrated until component units close", + "Next source-evidence queue", + ]: + assert token in text diff --git a/tests/test_vedastro_reproducible_build_probe.py b/tests/test_vedastro_reproducible_build_probe.py new file mode 100644 index 00000000..4f461196 --- /dev/null +++ b/tests/test_vedastro_reproducible_build_probe.py @@ -0,0 +1,33 @@ +from pathlib import Path + +from scripts.vedastro_reproducible_build_probe import build_identity + + +def test_build_identity_binds_source_and_container_inputs(tmp_path: Path) -> None: + (tmp_path / "API").mkdir() + (tmp_path / "API/Dockerfile").write_text( + "FROM example/sdk:7@sha256:" + "a" * 64 + " AS build\nFROM build AS final\n", + encoding="utf-8", + ) + (tmp_path / "API/API.csproj").write_text("\n", encoding="utf-8") + report = build_identity(tmp_path, source_commit="1f3a464", image_inspect=None) + + assert report["source_commit"] == "1f3a464" + assert len(report["dockerfile_sha256"]) == 64 + assert report["base_images"] == ["example/sdk:7@sha256:" + "a" * 64] + assert len(report["project_file_hashes"]["API/API.csproj"]) == 64 + assert report["status"] == "source_pinned_image_not_built" + + +def test_build_identity_records_built_image_digest(tmp_path: Path) -> None: + (tmp_path / "API").mkdir() + (tmp_path / "API/Dockerfile").write_text("FROM example/sdk:7\n", encoding="utf-8") + report = build_identity( + tmp_path, + source_commit="abc123", + image_inspect={"Id": "sha256:" + "b" * 64, "RepoDigests": ["repo@sha256:" + "c" * 64]}, + ) + + assert report["status"] == "reproducible_candidate_built" + assert report["image_id"] == "sha256:" + "b" * 64 + assert report["repo_digests"] == ["repo@sha256:" + "c" * 64]