feat: sync astrology provenance gates

This commit is contained in:
732642856
2026-07-19 08:59:33 +08:00
parent 5298b49a89
commit 82e7b0a95a
12 changed files with 623 additions and 1 deletions
@@ -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.
@@ -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.
+3 -1
View File
@@ -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:
<!--AYANAM_SUGGESTIONS:["问题一","问题二","问题三"]-->
@@ -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/);
});
@@ -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"
}
@@ -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"
}
@@ -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())
+95
View File
@@ -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())
@@ -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())
@@ -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
@@ -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
@@ -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("<Project />\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]