feat: sync astrology provenance gates
This commit is contained in:
@@ -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.
|
||||
@@ -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())
|
||||
@@ -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]
|
||||
Reference in New Issue
Block a user