diff --git a/docs/research/ACTIVE_FRONTS.md b/docs/research/ACTIVE_FRONTS.md index 8da071cc..b1c89744 100644 --- a/docs/research/ACTIVE_FRONTS.md +++ b/docs/research/ACTIVE_FRONTS.md @@ -39,8 +39,11 @@ This file is the small index for the current engineering fronts that still drive ## VedAstro Adapter MVP +- `/Users/wuyongnaren/Documents/印度占星/docs/research/vedastro_parity_matrix_latest.md` +- `/Users/wuyongnaren/Documents/印度占星/docs/research/vedastro_parity_matrix_latest.json` - `/Users/wuyongnaren/Documents/印度占星/scripts/vedastro_service_adapter.py` - follow only after the Jaimini marriage bridge v1 regression loop is closed +- Use the parity matrix before adding or claiming VedAstro-equivalent capability. ## Dignity / Role Guardrails diff --git a/docs/research/vedastro_parity_matrix_latest.json b/docs/research/vedastro_parity_matrix_latest.json new file mode 100644 index 00000000..0167dbaa --- /dev/null +++ b/docs/research/vedastro_parity_matrix_latest.json @@ -0,0 +1,243 @@ +{ + "scope": "vedastro_parity_matrix", + "schema_version": 1, + "generated_at": "2026-06-28T08:31:52.958980+00:00", + "summary": { + "row_count": 13, + "p0_count": 11, + "status_counts": { + "partial": 8, + "covered": 4, + "missing": 1 + }, + "recommended_path_counts": { + "hybrid_local_plus_vedastro": 7, + "local_native": 4, + "new_local_impl": 1, + "external_evidence_only": 1 + }, + "local_registry_technique_count": 89 + }, + "boundary": { + "not_a_clone_claim": true, + "external_outputs_are_adapter_evidence_until_promoted": true, + "local_adjudicator_remains_final_reasoning_layer": true + }, + "rows": [ + { + "vedastro_capability": "Tajika Annual", + "category": "annual_prediction", + "local_status": "partial", + "local_assets": [ + "tajika.py", + "varshaphala.py", + "sahams", + "solar_return" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "Annual modules exist; yearly career/wealth/month windows need stronger strict-workflow integration." + }, + { + "vedastro_capability": "Ayanamsa Selection", + "category": "ephemeris_policy", + "local_status": "partial", + "local_assets": [ + "ayanamsa_utils", + "ephemeris_adapter_contract" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_clean_room_only", + "adjudicator_use": "oracle_only", + "gap_notes": "Local Lahiri path is usable, but broad ayanamsa parity and public comparison artifacts remain incomplete." + }, + { + "vedastro_capability": "Prashna / Horary", + "category": "horary", + "local_status": "covered", + "local_assets": [ + "prashna.py", + "kp_system.py", + "upagraha_gulika_maandi", + "sphuta_trisphuta_family" + ], + "can_call_vedastro": true, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_or_clean_room", + "adjudicator_use": "secondary", + "gap_notes": "Horary modules exist but are not yet a first-class question adjudicator route." + }, + { + "vedastro_capability": "Jaimini / Chara Dasha", + "category": "jaimini", + "local_status": "covered", + "local_assets": [ + "jaimini.py", + "AK", + "DK", + "UL", + "Chara Dasha", + "Jaimini marriage bridge v1" + ], + "can_call_vedastro": true, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_mit_attribution_for_reused_parts", + "adjudicator_use": "secondary", + "gap_notes": "Core Jaimini exists; mission/career/marriage quality adjudicator folding is not yet exhaustive." + }, + { + "vedastro_capability": "Report Rendering", + "category": "presentation", + "local_status": "partial", + "local_assets": [ + "report_artifact API", + "report_builder.py", + "chart_renderer.py", + "jyotish-app export" + ], + "can_call_vedastro": false, + "recommended_path": "new_local_impl", + "priority": "P0", + "license_boundary": "local_native", + "adjudicator_use": "not_used", + "gap_notes": "HTML/PDF artifact path exists; polished SVG/PDF chart rendering and cloud-scale report production are not finished." + }, + { + "vedastro_capability": "Synastry / Ashtakoot", + "category": "relationship_matching", + "local_status": "covered", + "local_assets": [ + "synastry.py", + "ashtakoot.py", + "36-point Ashtakoot", + "16-factor compatibility" + ], + "can_call_vedastro": true, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_mit_attribution_for_reused_parts", + "adjudicator_use": "secondary", + "gap_notes": "Matching modules exist and API-backed; relationship adjudicator still needs a formal bridge." + }, + { + "vedastro_capability": "MCP / API Surface", + "category": "service_surface", + "local_status": "partial", + "local_assets": [ + "mcp_server.py", + "jyotish_api_server.py", + "strict workflows", + "vedastro_service_adapter.py" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_local_native", + "adjudicator_use": "primary", + "gap_notes": "Local API/MCP surfaces exist; VedAstro live adapter needs endpoint-backed smoke tests and provenance capture." + }, + { + "vedastro_capability": "Ashtakavarga", + "category": "strength", + "local_status": "partial", + "local_assets": [ + "ashtakavarga", + "ashtakavarga_pav", + "ashtakavarga_sodhita", + "finance_ashtakavarga_bridge" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "SAV/BAV is in production; PAV/Sodhita/Kakshya bridges need continued regression before being dominant labels." + }, + { + "vedastro_capability": "Shadbala", + "category": "strength", + "local_status": "partial", + "local_assets": [ + "shadbala", + "shadbala_advanced", + "shadbala_component_cap", + "oracle_shadbala_queue" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "Local component-aware cap exists; absolute external oracle closure is still incomplete." + }, + { + "vedastro_capability": "EventsAtRange / Life Event Graph", + "category": "timing_range_scan", + "local_status": "partial", + "local_assets": [ + "transit_trigger", + "dasha", + "narayana_dasha", + "vedastro_service_adapter.range_scan" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_clean_room_only", + "adjudicator_use": "oracle_only", + "gap_notes": "Local timing modules exist, but a high-frequency day/hour event graph is not yet a local productized radar." + }, + { + "vedastro_capability": "D1-D60 Divisional Charts", + "category": "varga", + "local_status": "covered", + "local_assets": [ + "varga", + "varga_full", + "shodasavarga", + "divisional_charts_extended" + ], + "can_call_vedastro": true, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_or_clean_room", + "adjudicator_use": "primary", + "gap_notes": "Local varga coverage is strong; keep VedAstro/PyJHora as benchmark evidence rather than replacing local math." + }, + { + "vedastro_capability": "Birth Time ML / Rectification Assistant", + "category": "birth_time_rectification", + "local_status": "partial", + "local_assets": [ + "birth_time_rectifier.py", + "rectification_gate", + "jyotish-app rectification" + ], + "can_call_vedastro": true, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P1", + "license_boundary": "external_service_or_local_native", + "adjudicator_use": "secondary", + "gap_notes": "Local rectification exists; ML parity with VedAstro-style service behavior is not established." + }, + { + "vedastro_capability": "Numerology / Non-Jyotish Tools", + "category": "adjacent_tools", + "local_status": "missing", + "local_assets": [], + "can_call_vedastro": true, + "recommended_path": "external_evidence_only", + "priority": "P2", + "license_boundary": "external_service_only", + "adjudicator_use": "not_used", + "gap_notes": "Adjacent product feature; not required for Jyotish adjudicator depth." + } + ] +} diff --git a/docs/research/vedastro_parity_matrix_latest.md b/docs/research/vedastro_parity_matrix_latest.md new file mode 100644 index 00000000..148742d3 --- /dev/null +++ b/docs/research/vedastro_parity_matrix_latest.md @@ -0,0 +1,41 @@ +# VedAstro Parity Matrix + +- Generated: `2026-06-28T08:31:52.958980+00:00` +- Rows: `13` +- P0 rows: `11` +- Local registry technique count: `89` + +## Honesty Boundary + +VedAstro calls are external adapter evidence until a capability is promoted by local tests, oracle artifacts, or strict workflow integration. This matrix does not claim clone-level parity. + +## Summary + +- Local status counts: `{"covered": 4, "missing": 1, "partial": 8}` +- Recommended path counts: `{"external_evidence_only": 1, "hybrid_local_plus_vedastro": 7, "local_native": 4, "new_local_impl": 1}` + +## Matrix + +| VedAstro capability | Category | Local status | Path | Priority | Adjudicator use | Local assets | Gap notes | +|---|---|---:|---|---:|---|---|---| +| Tajika Annual | annual_prediction | partial | hybrid_local_plus_vedastro | P0 | secondary | tajika.py, varshaphala.py, sahams, solar_return | Annual modules exist; yearly career/wealth/month windows need stronger strict-workflow integration. | +| Ayanamsa Selection | ephemeris_policy | partial | hybrid_local_plus_vedastro | P0 | oracle_only | ayanamsa_utils, ephemeris_adapter_contract | Local Lahiri path is usable, but broad ayanamsa parity and public comparison artifacts remain incomplete. | +| Prashna / Horary | horary | covered | local_native | P0 | secondary | prashna.py, kp_system.py, upagraha_gulika_maandi, sphuta_trisphuta_family | Horary modules exist but are not yet a first-class question adjudicator route. | +| Jaimini / Chara Dasha | jaimini | covered | local_native | P0 | secondary | jaimini.py, AK, DK, UL, Chara Dasha, Jaimini marriage bridge v1 | Core Jaimini exists; mission/career/marriage quality adjudicator folding is not yet exhaustive. | +| Report Rendering | presentation | partial | new_local_impl | P0 | not_used | report_artifact API, report_builder.py, chart_renderer.py, jyotish-app export | HTML/PDF artifact path exists; polished SVG/PDF chart rendering and cloud-scale report production are not finished. | +| Synastry / Ashtakoot | relationship_matching | covered | local_native | P0 | secondary | synastry.py, ashtakoot.py, 36-point Ashtakoot, 16-factor compatibility | Matching modules exist and API-backed; relationship adjudicator still needs a formal bridge. | +| MCP / API Surface | service_surface | partial | hybrid_local_plus_vedastro | P0 | primary | mcp_server.py, jyotish_api_server.py, strict workflows, vedastro_service_adapter.py | Local API/MCP surfaces exist; VedAstro live adapter needs endpoint-backed smoke tests and provenance capture. | +| Ashtakavarga | strength | partial | hybrid_local_plus_vedastro | P0 | secondary | ashtakavarga, ashtakavarga_pav, ashtakavarga_sodhita, finance_ashtakavarga_bridge | SAV/BAV is in production; PAV/Sodhita/Kakshya bridges need continued regression before being dominant labels. | +| Shadbala | strength | partial | hybrid_local_plus_vedastro | P0 | secondary | shadbala, shadbala_advanced, shadbala_component_cap, oracle_shadbala_queue | Local component-aware cap exists; absolute external oracle closure is still incomplete. | +| EventsAtRange / Life Event Graph | timing_range_scan | partial | hybrid_local_plus_vedastro | P0 | oracle_only | transit_trigger, dasha, narayana_dasha, vedastro_service_adapter.range_scan | Local timing modules exist, but a high-frequency day/hour event graph is not yet a local productized radar. | +| D1-D60 Divisional Charts | varga | covered | local_native | P0 | primary | varga, varga_full, shodasavarga, divisional_charts_extended | Local varga coverage is strong; keep VedAstro/PyJHora as benchmark evidence rather than replacing local math. | +| Birth Time ML / Rectification Assistant | birth_time_rectification | partial | hybrid_local_plus_vedastro | P1 | secondary | birth_time_rectifier.py, rectification_gate, jyotish-app rectification | Local rectification exists; ML parity with VedAstro-style service behavior is not established. | +| Numerology / Non-Jyotish Tools | adjacent_tools | missing | external_evidence_only | P2 | not_used | - | Adjacent product feature; not required for Jyotish adjudicator depth. | + +## Next Actions + +1. Promote `VedAstro adapter MVP` from contract to endpoint-backed smoke tests. +2. Add a relationship bridge for `Synastry / Ashtakoot` before using matching scores as primary labels. +3. Build `Life Event Graph v1` from local monthly/day scan plus optional VedAstro range-scan evidence. +4. Keep ayanamsa and Shadbala parity under oracle closure before claiming production tuning. + diff --git a/docs/superpowers/plans/2026-06-28-vedastro-parity-matrix-v1.md b/docs/superpowers/plans/2026-06-28-vedastro-parity-matrix-v1.md new file mode 100644 index 00000000..6df9baaf --- /dev/null +++ b/docs/superpowers/plans/2026-06-28-vedastro-parity-matrix-v1.md @@ -0,0 +1,55 @@ +# VedAstro Parity Matrix v1 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a repeatable VedAstro parity matrix that maps public VedAstro capability areas to local assets, external adapter options, and implementation priorities. + +**Architecture:** Add one focused generator script that consumes local capability audit output and a curated VedAstro seed list, then writes JSON and Markdown snapshots. Keep the matrix separate from strict adjudicator runtime so it cannot accidentally assert unverified production parity. + +**Tech Stack:** Python 3 standard library, existing `scripts/audit_capabilities.py`, existing research markdown conventions. + +## Global Constraints + +- Do not copy VedAstro implementation code. +- Treat VedAstro results as external adapter evidence unless promoted by local tests or oracle artifacts. +- Do not modify strict workflow scoring in this task. +- Do not stage unrelated untracked workspace residue. + +--- + +### Task 1: Generator And Tests + +**Files:** +- Create: `scripts/vedastro_parity_matrix.py` +- Create: `tests/test_vedastro_parity_matrix.py` +- Modify: `docs/research/ACTIVE_FRONTS.md` + +**Interfaces:** +- Produces: `build_matrix(audit: dict | None = None) -> dict` +- Produces: `render_markdown(matrix: dict) -> str` +- Produces CLI: `python3 scripts/vedastro_parity_matrix.py --format json|markdown --write` + +- [ ] **Step 1: Write tests for P0 rows and contracts** + +Run: `python3 -m pytest tests/test_vedastro_parity_matrix.py -q` + +- [ ] **Step 2: Implement the generator** + +Create the matrix with deterministic row ordering and explicit allowed values. + +- [ ] **Step 3: Generate snapshots** + +Run: `python3 scripts/vedastro_parity_matrix.py --write` + +- [ ] **Step 4: Verify** + +Run: + +```bash +python3 -m pytest tests/test_vedastro_parity_matrix.py -q +python3 scripts/vedastro_parity_matrix.py --format json >/tmp/vedastro_parity_matrix.json +``` + +- [ ] **Step 5: Commit** + +Stage only the parity matrix files, tests, and Active Fronts update. diff --git a/docs/superpowers/specs/2026-06-28-vedastro-parity-matrix-v1-design.md b/docs/superpowers/specs/2026-06-28-vedastro-parity-matrix-v1-design.md new file mode 100644 index 00000000..fd56c378 --- /dev/null +++ b/docs/superpowers/specs/2026-06-28-vedastro-parity-matrix-v1-design.md @@ -0,0 +1,47 @@ +# VedAstro Parity Matrix v1 Design + +## Goal + +Create a repeatable parity matrix that compares public VedAstro capability areas with this repository's local assets and recommends the safest integration path for each area. + +## Boundary + +The matrix is a planning and audit artifact. It does not claim that local behavior equals VedAstro behavior unless the local registry, tests, and external oracle status support that claim. + +## Inputs + +- `scripts/audit_capabilities.py --mode validate` +- `scripts/vedastro_service_adapter.py` schema and supported range-scan domains +- A curated public VedAstro capability seed list kept in the generator script +- Existing research indexes under `docs/research/ACTIVE_FRONTS.md` + +## Output Contract + +Each row includes: + +- `vedastro_capability` +- `category` +- `local_status` +- `local_assets` +- `can_call_vedastro` +- `recommended_path` +- `priority` +- `license_boundary` +- `adjudicator_use` +- `gap_notes` + +Allowed `recommended_path` values: + +- `local_native` +- `vedastro_adapter` +- `new_local_impl` +- `external_evidence_only` +- `hybrid_local_plus_vedastro` + +## v1 Priorities + +P0 rows cover: range scanning, ayanamsa parity, D1-D60/varga parity, Ashtakavarga/Shadbala oracle checks, Jaimini/Chara Dasha, Synastry/Ashtakoot, Tajika annual, Prashna, report rendering, and MCP/API parity. + +## Honesty Rule + +External VedAstro output must be marked as adapter evidence unless it is independently promoted through local tests or an oracle artifact. Local adjudicators remain the final reasoning layer. diff --git a/scripts/vedastro_parity_matrix.py b/scripts/vedastro_parity_matrix.py new file mode 100644 index 00000000..687ce230 --- /dev/null +++ b/scripts/vedastro_parity_matrix.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +"""Build a VedAstro-to-local parity matrix. + +The matrix is an audit/planning artifact. It keeps VedAstro service evidence, +local native implementation, and adjudicator use separate so external API +coverage is not mislabeled as local production parity. +""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +DEFAULT_JSON_PATH = ROOT / "docs" / "research" / "vedastro_parity_matrix_latest.json" +DEFAULT_MARKDOWN_PATH = ROOT / "docs" / "research" / "vedastro_parity_matrix_latest.md" + +ALLOWED_RECOMMENDED_PATHS = { + "local_native", + "vedastro_adapter", + "new_local_impl", + "external_evidence_only", + "hybrid_local_plus_vedastro", +} + + +VEDASTRO_CAPABILITY_SEEDS: list[dict[str, Any]] = [ + { + "vedastro_capability": "EventsAtRange / Life Event Graph", + "category": "timing_range_scan", + "domains": ["event", "timing", "transit"], + "local_assets": ["transit_trigger", "dasha", "narayana_dasha", "vedastro_service_adapter.range_scan"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_clean_room_only", + "adjudicator_use": "oracle_only", + "gap_notes": "Local timing modules exist, but a high-frequency day/hour event graph is not yet a local productized radar.", + }, + { + "vedastro_capability": "Ayanamsa Selection", + "category": "ephemeris_policy", + "domains": ["core"], + "local_assets": ["ayanamsa_utils", "ephemeris_adapter_contract"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_clean_room_only", + "adjudicator_use": "oracle_only", + "gap_notes": "Local Lahiri path is usable, but broad ayanamsa parity and public comparison artifacts remain incomplete.", + }, + { + "vedastro_capability": "D1-D60 Divisional Charts", + "category": "varga", + "domains": ["varga", "divisional", "d60"], + "local_assets": ["varga", "varga_full", "shodasavarga", "divisional_charts_extended"], + "can_call_vedastro": True, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_or_clean_room", + "adjudicator_use": "primary", + "gap_notes": "Local varga coverage is strong; keep VedAstro/PyJHora as benchmark evidence rather than replacing local math.", + }, + { + "vedastro_capability": "Ashtakavarga", + "category": "strength", + "domains": ["ashtakavarga", "strength"], + "local_assets": ["ashtakavarga", "ashtakavarga_pav", "ashtakavarga_sodhita", "finance_ashtakavarga_bridge"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "SAV/BAV is in production; PAV/Sodhita/Kakshya bridges need continued regression before being dominant labels.", + }, + { + "vedastro_capability": "Shadbala", + "category": "strength", + "domains": ["shadbala", "strength"], + "local_assets": ["shadbala", "shadbala_advanced", "shadbala_component_cap", "oracle_shadbala_queue"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "Local component-aware cap exists; absolute external oracle closure is still incomplete.", + }, + { + "vedastro_capability": "Jaimini / Chara Dasha", + "category": "jaimini", + "domains": ["jaimini", "narayana", "karaka"], + "local_assets": ["jaimini.py", "AK", "DK", "UL", "Chara Dasha", "Jaimini marriage bridge v1"], + "can_call_vedastro": True, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_mit_attribution_for_reused_parts", + "adjudicator_use": "secondary", + "gap_notes": "Core Jaimini exists; mission/career/marriage quality adjudicator folding is not yet exhaustive.", + }, + { + "vedastro_capability": "Synastry / Ashtakoot", + "category": "relationship_matching", + "domains": ["synastry", "relationship", "marriage"], + "local_assets": ["synastry.py", "ashtakoot.py", "36-point Ashtakoot", "16-factor compatibility"], + "can_call_vedastro": True, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_mit_attribution_for_reused_parts", + "adjudicator_use": "secondary", + "gap_notes": "Matching modules exist and API-backed; relationship adjudicator still needs a formal bridge.", + }, + { + "vedastro_capability": "Tajika Annual", + "category": "annual_prediction", + "domains": ["tajika", "annual", "varshaphala", "saham"], + "local_assets": ["tajika.py", "varshaphala.py", "sahams", "solar_return"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "local_native_or_external_oracle", + "adjudicator_use": "secondary", + "gap_notes": "Annual modules exist; yearly career/wealth/month windows need stronger strict-workflow integration.", + }, + { + "vedastro_capability": "Prashna / Horary", + "category": "horary", + "domains": ["prashna", "kp"], + "local_assets": ["prashna.py", "kp_system.py", "upagraha_gulika_maandi", "sphuta_trisphuta_family"], + "can_call_vedastro": True, + "recommended_path": "local_native", + "priority": "P0", + "license_boundary": "local_native_or_clean_room", + "adjudicator_use": "secondary", + "gap_notes": "Horary modules exist but are not yet a first-class question adjudicator route.", + }, + { + "vedastro_capability": "Report Rendering", + "category": "presentation", + "domains": ["report", "image"], + "local_assets": ["report_artifact API", "report_builder.py", "chart_renderer.py", "jyotish-app export"], + "can_call_vedastro": False, + "recommended_path": "new_local_impl", + "priority": "P0", + "license_boundary": "local_native", + "adjudicator_use": "not_used", + "gap_notes": "HTML/PDF artifact path exists; polished SVG/PDF chart rendering and cloud-scale report production are not finished.", + }, + { + "vedastro_capability": "MCP / API Surface", + "category": "service_surface", + "domains": ["event", "relationship", "wealth", "career"], + "local_assets": ["mcp_server.py", "jyotish_api_server.py", "strict workflows", "vedastro_service_adapter.py"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P0", + "license_boundary": "external_service_or_local_native", + "adjudicator_use": "primary", + "gap_notes": "Local API/MCP surfaces exist; VedAstro live adapter needs endpoint-backed smoke tests and provenance capture.", + }, + { + "vedastro_capability": "Numerology / Non-Jyotish Tools", + "category": "adjacent_tools", + "domains": [], + "local_assets": [], + "can_call_vedastro": True, + "recommended_path": "external_evidence_only", + "priority": "P2", + "license_boundary": "external_service_only", + "adjudicator_use": "not_used", + "gap_notes": "Adjacent product feature; not required for Jyotish adjudicator depth.", + }, + { + "vedastro_capability": "Birth Time ML / Rectification Assistant", + "category": "birth_time_rectification", + "domains": ["birth"], + "local_assets": ["birth_time_rectifier.py", "rectification_gate", "jyotish-app rectification"], + "can_call_vedastro": True, + "recommended_path": "hybrid_local_plus_vedastro", + "priority": "P1", + "license_boundary": "external_service_or_local_native", + "adjudicator_use": "secondary", + "gap_notes": "Local rectification exists; ML parity with VedAstro-style service behavior is not established.", + }, +] + + +def _run_audit_capabilities() -> dict[str, Any]: + proc = subprocess.run( + [sys.executable, "scripts/audit_capabilities.py", "--mode", "validate"], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + return json.loads(proc.stdout) + + +def _infer_local_status(seed: dict[str, Any], audit: dict[str, Any]) -> str: + domain_counts = audit.get("domain_counts") if isinstance(audit, dict) else {} + status_counts = audit.get("status_counts") if isinstance(audit, dict) else {} + domains = seed.get("domains") or [] + + if not seed.get("local_assets"): + return "missing" + + matched_domains = [domain for domain in domains if int(domain_counts.get(domain, 0) or 0) > 0] + if matched_domains: + if seed["recommended_path"] in {"new_local_impl", "hybrid_local_plus_vedastro"}: + return "partial" + return "covered" if int(status_counts.get("covered", 0) or 0) else "complete" + + if seed["recommended_path"] == "external_evidence_only": + return "external_only" + return "partial" + + +def _build_row(seed: dict[str, Any], audit: dict[str, Any]) -> dict[str, Any]: + recommended_path = seed["recommended_path"] + if recommended_path not in ALLOWED_RECOMMENDED_PATHS: + raise ValueError(f"unsupported recommended_path: {recommended_path}") + + return { + "vedastro_capability": seed["vedastro_capability"], + "category": seed["category"], + "local_status": _infer_local_status(seed, audit), + "local_assets": list(seed["local_assets"]), + "can_call_vedastro": bool(seed["can_call_vedastro"]), + "recommended_path": recommended_path, + "priority": seed["priority"], + "license_boundary": seed["license_boundary"], + "adjudicator_use": seed["adjudicator_use"], + "gap_notes": seed["gap_notes"], + } + + +def build_matrix(audit: dict[str, Any] | None = None) -> dict[str, Any]: + audit = audit if audit is not None else _run_audit_capabilities() + rows = [_build_row(seed, audit) for seed in VEDASTRO_CAPABILITY_SEEDS] + rows.sort(key=lambda row: (row["priority"], row["category"], row["vedastro_capability"])) + + status_counts: dict[str, int] = {} + path_counts: dict[str, int] = {} + priority_counts: dict[str, int] = {} + for row in rows: + status_counts[row["local_status"]] = status_counts.get(row["local_status"], 0) + 1 + path_counts[row["recommended_path"]] = path_counts.get(row["recommended_path"], 0) + 1 + priority_counts[row["priority"]] = priority_counts.get(row["priority"], 0) + 1 + + return { + "scope": "vedastro_parity_matrix", + "schema_version": 1, + "generated_at": datetime.now(timezone.utc).isoformat(), + "summary": { + "row_count": len(rows), + "p0_count": priority_counts.get("P0", 0), + "status_counts": status_counts, + "recommended_path_counts": path_counts, + "local_registry_technique_count": audit.get("technique_count"), + }, + "boundary": { + "not_a_clone_claim": True, + "external_outputs_are_adapter_evidence_until_promoted": True, + "local_adjudicator_remains_final_reasoning_layer": True, + }, + "rows": rows, + } + + +def render_markdown(matrix: dict[str, Any]) -> str: + summary = matrix["summary"] + lines = [ + "# VedAstro Parity Matrix", + "", + f"- Generated: `{matrix['generated_at']}`", + f"- Rows: `{summary['row_count']}`", + f"- P0 rows: `{summary['p0_count']}`", + f"- Local registry technique count: `{summary.get('local_registry_technique_count')}`", + "", + "## Honesty Boundary", + "", + "VedAstro calls are external adapter evidence until a capability is promoted by local tests, oracle artifacts, or strict workflow integration. This matrix does not claim clone-level parity.", + "", + "## Summary", + "", + f"- Local status counts: `{json.dumps(summary['status_counts'], ensure_ascii=False, sort_keys=True)}`", + f"- Recommended path counts: `{json.dumps(summary['recommended_path_counts'], ensure_ascii=False, sort_keys=True)}`", + "", + "## Matrix", + "", + "| VedAstro capability | Category | Local status | Path | Priority | Adjudicator use | Local assets | Gap notes |", + "|---|---|---:|---|---:|---|---|---|", + ] + for row in matrix["rows"]: + assets = ", ".join(row["local_assets"]) if row["local_assets"] else "-" + lines.append( + "| {vedastro_capability} | {category} | {local_status} | {recommended_path} | {priority} | {adjudicator_use} | {assets} | {gap_notes} |".format( + assets=assets, + **row, + ) + ) + lines.extend( + [ + "", + "## Next Actions", + "", + "1. Promote `VedAstro adapter MVP` from contract to endpoint-backed smoke tests.", + "2. Add a relationship bridge for `Synastry / Ashtakoot` before using matching scores as primary labels.", + "3. Build `Life Event Graph v1` from local monthly/day scan plus optional VedAstro range-scan evidence.", + "4. Keep ayanamsa and Shadbala parity under oracle closure before claiming production tuning.", + "", + ] + ) + return "\n".join(lines) + + +def write_outputs( + matrix: dict[str, Any], + *, + json_path: Path = DEFAULT_JSON_PATH, + markdown_path: Path = DEFAULT_MARKDOWN_PATH, +) -> None: + json_path.parent.mkdir(parents=True, exist_ok=True) + markdown_path.parent.mkdir(parents=True, exist_ok=True) + json_path.write_text(json.dumps(matrix, ensure_ascii=False, indent=2) + "\n") + markdown_path.write_text(render_markdown(matrix) + "\n") + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--format", choices=["json", "markdown"], default="json") + parser.add_argument("--write", action="store_true", help="write latest JSON and Markdown snapshots") + args = parser.parse_args(argv) + + matrix = build_matrix() + if args.write: + write_outputs(matrix) + if args.format == "json": + print(json.dumps(matrix, ensure_ascii=False, indent=2)) + else: + print(render_markdown(matrix)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_vedastro_parity_matrix.py b/tests/test_vedastro_parity_matrix.py new file mode 100644 index 00000000..751c713f --- /dev/null +++ b/tests/test_vedastro_parity_matrix.py @@ -0,0 +1,116 @@ +import json +from pathlib import Path + +from scripts import vedastro_parity_matrix + + +def _row(matrix, capability): + for row in matrix["rows"]: + if row["vedastro_capability"] == capability: + return row + raise AssertionError(f"missing row: {capability}") + + +def test_build_matrix_has_p0_public_vedastro_capability_rows(): + matrix = vedastro_parity_matrix.build_matrix() + + assert matrix["scope"] == "vedastro_parity_matrix" + assert matrix["summary"]["row_count"] >= 10 + assert matrix["summary"]["p0_count"] >= 10 + + required = { + "EventsAtRange / Life Event Graph", + "Ayanamsa Selection", + "D1-D60 Divisional Charts", + "Ashtakavarga", + "Shadbala", + "Jaimini / Chara Dasha", + "Synastry / Ashtakoot", + "Tajika Annual", + "Prashna / Horary", + "Report Rendering", + "MCP / API Surface", + } + assert required.issubset({row["vedastro_capability"] for row in matrix["rows"]}) + + +def test_range_scan_is_adapter_gap_not_local_complete_claim(): + matrix = vedastro_parity_matrix.build_matrix() + row = _row(matrix, "EventsAtRange / Life Event Graph") + + assert row["priority"] == "P0" + assert row["local_status"] in {"partial", "missing"} + assert row["can_call_vedastro"] is True + assert row["recommended_path"] == "hybrid_local_plus_vedastro" + assert row["adjudicator_use"] == "oracle_only" + assert "range" in " ".join(row["local_assets"]).lower() + + +def test_local_assets_still_need_parity_or_bridge_when_not_fully_exploited(): + matrix = vedastro_parity_matrix.build_matrix() + + jaimini = _row(matrix, "Jaimini / Chara Dasha") + assert jaimini["local_status"] in {"covered", "complete"} + assert "jaimini" in " ".join(jaimini["local_assets"]).lower() + assert jaimini["recommended_path"] in {"local_native", "hybrid_local_plus_vedastro"} + + synastry = _row(matrix, "Synastry / Ashtakoot") + assert synastry["local_status"] in {"covered", "complete"} + assert any("ashtakoot" in asset.lower() for asset in synastry["local_assets"]) + assert synastry["adjudicator_use"] in {"secondary", "primary"} + + +def test_matrix_rows_use_allowed_contract_values(): + matrix = vedastro_parity_matrix.build_matrix() + + allowed_status = {"complete", "covered", "partial", "missing", "external_only"} + allowed_path = { + "local_native", + "vedastro_adapter", + "new_local_impl", + "external_evidence_only", + "hybrid_local_plus_vedastro", + } + allowed_priority = {"P0", "P1", "P2"} + allowed_use = {"primary", "secondary", "oracle_only", "not_used"} + + for row in matrix["rows"]: + assert set(row) == { + "vedastro_capability", + "category", + "local_status", + "local_assets", + "can_call_vedastro", + "recommended_path", + "priority", + "license_boundary", + "adjudicator_use", + "gap_notes", + } + assert row["local_status"] in allowed_status + assert row["recommended_path"] in allowed_path + assert row["priority"] in allowed_priority + assert row["adjudicator_use"] in allowed_use + assert isinstance(row["local_assets"], list) + + +def test_render_markdown_contains_summary_and_honesty_boundary(): + matrix = vedastro_parity_matrix.build_matrix() + markdown = vedastro_parity_matrix.render_markdown(matrix) + + assert "# VedAstro Parity Matrix" in markdown + assert "## Honesty Boundary" in markdown + assert "EventsAtRange / Life Event Graph" in markdown + assert "external adapter evidence" in markdown + + +def test_write_outputs_creates_json_and_markdown(tmp_path): + matrix = vedastro_parity_matrix.build_matrix() + json_path = tmp_path / "matrix.json" + md_path = tmp_path / "matrix.md" + + vedastro_parity_matrix.write_outputs(matrix, json_path=json_path, markdown_path=md_path) + + loaded = json.loads(json_path.read_text()) + assert loaded["scope"] == "vedastro_parity_matrix" + assert md_path.read_text().startswith("# VedAstro Parity Matrix")