Merge remote-tracking branch 'origin/main' into codex/optimize-runtime-ux

# Conflicts:
#	frontend/src/mastra/index.ts
This commit is contained in:
732642856
2026-07-19 09:01:27 +08:00
29 changed files with 2924 additions and 1 deletions
@@ -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.
@@ -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":"..."}'
```
## 结论边界
没有真实独立负样本前:
- 可以输出候选日期排序;
- 可以说明触发信号;
- 不能说“精确日期预测已验证”。
@@ -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.
@@ -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.
@@ -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.
@@ -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.
+3 -1
View File
@@ -120,6 +120,7 @@ export function toAgentConsultationContext(data: JsonRecord) {
houses: chart.houses,
dasha: chart.dasha,
shadbala: chart.shadbala,
ashtakavarga: chart.ashtakavarga,
yogas: chart.yogas,
},
local_layers: {
@@ -172,9 +173,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.
- Treat evidence_contract.answer_policy as a hard output contract. When can_answer_precise_timing is false, provide only direction or structure and do not state a month, date, or guaranteed timing outcome.
- Treat rectification.boundary=not_auto_rectified as final: a candidate time or score is not a verified birth time and must not be presented as one.
Usually answer in 2-5 short paragraphs. Ask one clarifying question only when the user's intent is genuinely unclear.
@@ -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,6 +17,11 @@ 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/);
});
test("keeps strength, Ashtakavarga, and timing evidence available to the answer model", () => {
@@ -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",
@@ -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"
}
File diff suppressed because it is too large Load Diff
@@ -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"
}
@@ -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"
}
+60
View File
@@ -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())
+3
View File
@@ -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")
@@ -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())
+71
View File
@@ -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())
@@ -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())
+8
View File
@@ -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"]
+48
View File
@@ -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"
@@ -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,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
@@ -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
+193
View File
@@ -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"
@@ -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
@@ -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]