Merge remote-tracking branch 'upstream/codex/release-hygiene-ci'
# Conflicts: # docs/research/pre_work_error_ledger.md # jyotish-app/api-bridge.js # mcp_server.py # references/technique_registry.json # scripts/jyotish_api_server.py # scripts/skill_release_package.py
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# Real-Case Holdout V2 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:** Add ten new research-grade holdout cases, compare frozen v1/v2 Jyotish replay logic across twenty cases, and promote only improvements that generalize to holdout data.
|
||||
|
||||
**Architecture:** Keep batch 1 immutable as discovery/training evidence. Store batch 2 separately, run both rule versions through one benchmark runner, and generate batch/combined comparison reports. Production orchestration receives only the promoted rule version plus explicit external-oracle and specificity boundaries.
|
||||
|
||||
**Tech Stack:** Python 3, Swiss Ephemeris-backed local CLI, JSON manifests/schemas, pytest, public Astro-Databank and independently sourced event records.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Batch 2 must contain ten new people: five career events and five legal marriages.
|
||||
- Birth time must be Rodden A or AA; event date must have primary or verified-secondary source.
|
||||
- Freeze v2 before observing batch-2 results.
|
||||
- V2 additions: node dispositor, D10/D9 Lagna-lord and domain-house lord, A10/UL lord, Amatyakaraka/Darakaraka, and annual-layer audit when available.
|
||||
- Do not change score thresholds after batch-2 results.
|
||||
- No user birth data or private conversation feedback.
|
||||
- No scientific accuracy claim without sourced negative controls.
|
||||
|
||||
### Task 1: Freeze V2 Contract
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/test_public_real_case_benchmark.py`
|
||||
- Modify: `scripts/public_real_case_benchmark.py`
|
||||
|
||||
- [ ] Add failing tests for explicit `v1` and `v2` rule versions.
|
||||
- [ ] Add deterministic v2 signal helpers and preserve v1 output.
|
||||
- [ ] Verify v1 batch-1 results remain unchanged.
|
||||
|
||||
### Task 2: Add Holdout Manifest
|
||||
|
||||
**Files:**
|
||||
- Create: `references/real_case_calibration/replay_manifest_holdout_v2.json`
|
||||
- Modify: `tests/test_real_case_replay_validator.py`
|
||||
|
||||
- [ ] Add failing validation test for ten new A/AA cases and 5/5 domain split.
|
||||
- [ ] Research and import birth/event sources.
|
||||
- [ ] Validate no person overlaps batch 1.
|
||||
|
||||
### Task 3: Run V1/V2 Holdout Comparison
|
||||
|
||||
**Files:**
|
||||
- Modify: `scripts/public_real_case_benchmark.py`
|
||||
- Create: `docs/benchmark/public_real_case_holdout_v2_2026_07_11.json`
|
||||
|
||||
- [ ] Add batch comparison summary and rule-promotion decision.
|
||||
- [ ] Run v1 and frozen v2 on batch 2.
|
||||
- [ ] Run combined twenty-case summary.
|
||||
- [ ] Promote v2 only if holdout recall/exact-label improve without more blocked cases.
|
||||
|
||||
### Task 4: Integrate and Document
|
||||
|
||||
**Files:**
|
||||
- Modify: `scripts/unified_consultation_orchestrator.py`
|
||||
- Create: `docs/research/public_real_case_20_case_closure_2026_07_11.md`
|
||||
- Modify: `docs/research/pre_work_error_ledger.md`
|
||||
|
||||
- [ ] Expose promoted benchmark summary and remaining gaps.
|
||||
- [ ] Document misses, conflicts, external-engine states, and next negative-control design.
|
||||
- [ ] Run focused tests, privacy scan, diff check, and pre-work gate.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Commercial Domain Calculation Contract 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:** Port the mature research-domain calculation service into `jesse-ux/Jyotisha` so commercial API chart responses carry effective parameters and a stable result hash.
|
||||
|
||||
**Architecture:** Keep the two repositories independent. Copy the self-contained research `scripts/domain_calculation_service.py` into the commercial repository, then make the commercial API chart endpoint delegate local chart calculation to that service while preserving commercial auth/deployment code.
|
||||
|
||||
**Tech Stack:** Python stdlib, Swiss Ephemeris-backed existing `jyotish_engine.py`, pytest.
|
||||
|
||||
### Task 1: Add Commercial Regression Tests
|
||||
**Files:**
|
||||
- Create: `/tmp/Jyotisha-jesse-ux/tests/test_commercial_domain_calculation_contract.py`
|
||||
|
||||
- [ ] **Step 1: Write failing tests**
|
||||
Test direct service import and API `_compute_chart_sync()` response fields:
|
||||
```python
|
||||
def test_domain_chart_exposes_effective_parameters_and_result_hash():
|
||||
from domain_calculation_service import compute_chart
|
||||
result = compute_chart(...)
|
||||
assert result["calculation_contract"]["effective"]["node_mode"] == "true"
|
||||
assert result["result_hash"]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
Run: `python3 -m pytest -q tests/test_commercial_domain_calculation_contract.py`
|
||||
Expected: fail because `domain_calculation_service` is absent or API omits `calculation_contract`.
|
||||
|
||||
### Task 2: Port Minimal Service and API Delegation
|
||||
**Files:**
|
||||
- Create: `/tmp/Jyotisha-jesse-ux/scripts/domain_calculation_service.py`
|
||||
- Modify: `/tmp/Jyotisha-jesse-ux/scripts/jyotish_api_server.py`
|
||||
|
||||
- [ ] **Step 1: Copy the mature research service**
|
||||
Copy only `/Users/wuyongnaren/Documents/印度占星/scripts/domain_calculation_service.py`.
|
||||
|
||||
- [ ] **Step 2: Replace duplicated commercial chart calculation**
|
||||
In `JyotishAPIHandler._compute_chart_sync()`, call `domain_calculation_service.compute_chart(...)`.
|
||||
|
||||
- [ ] **Step 3: Preserve existing response shape**
|
||||
Return planets, ascendant, houses, divisional data and existing fields; add `calculation_contract` and `result_hash`.
|
||||
|
||||
### Task 3: Verify, Ledger, Push
|
||||
**Files:**
|
||||
- Modify both repos: `references/cross_project_contract/sync_ledger.json`
|
||||
|
||||
- [ ] **Step 1: Run focused tests**
|
||||
Run commercial:
|
||||
```bash
|
||||
python3 -m pytest -q tests/test_commercial_domain_calculation_contract.py tests/test_cross_project_contract.py tests/test_cross_project_sync_status.py
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run privacy and sync checks**
|
||||
Run both repositories' privacy scans and cross-project sync status checks.
|
||||
|
||||
- [ ] **Step 3: Commit and push**
|
||||
Commit commercial feature branch only. Update research ledger with the exact commercial commit. Push both feature branches.
|
||||
@@ -0,0 +1,115 @@
|
||||
# Dual-Project Contract Implementation Plan
|
||||
|
||||
> Execute after design approval. Two independent repositories; no runtime coupling.
|
||||
|
||||
## Scope
|
||||
|
||||
Implement the first synchronization foundation in both repositories:
|
||||
|
||||
1. versioned public synthetic-fixture manifest;
|
||||
2. stable compatibility-hash comparator;
|
||||
3. append-only cross-project synchronization ledger;
|
||||
4. focused tests and CI-friendly commands.
|
||||
|
||||
No production deployment, secret access, user-data migration, or formula change is
|
||||
in this phase.
|
||||
|
||||
## Shared Files
|
||||
|
||||
Create byte-identical copies in both repositories:
|
||||
|
||||
- `references/cross_project_contract/fixture_manifest.v1.json`
|
||||
- `references/cross_project_contract/sync_ledger.json`
|
||||
- `scripts/cross_project_contract.py`
|
||||
- `tests/test_cross_project_contract.py`
|
||||
|
||||
The fixture uses only generic public synthetic birth inputs. It declares the
|
||||
effective Ayanamsa and node mode. It contains a `compatibility_hash`, not a full
|
||||
internal `result_hash`: the former is generated from the fixed public contract
|
||||
fields below and is intentionally insensitive to extra report/evidence fields.
|
||||
|
||||
```text
|
||||
birth/effective params
|
||||
ascendant longitude/sign
|
||||
D1 Sun..Saturn/Rahu/Ketu longitude/sign
|
||||
```
|
||||
|
||||
## Task 1: Research Repository Contract
|
||||
|
||||
Files:
|
||||
|
||||
- Modify: `scripts/cross_project_contract.py` (new)
|
||||
- Modify: `references/cross_project_contract/fixture_manifest.v1.json` (new)
|
||||
- Modify: `references/cross_project_contract/sync_ledger.json` (new)
|
||||
- Modify: `tests/test_cross_project_contract.py` (new)
|
||||
|
||||
Tests first:
|
||||
|
||||
1. fixture accepts only synthetic/public metadata and complete effective settings;
|
||||
2. local calculation reproduces declared compatibility hash;
|
||||
3. altered node mode or expected hash produces non-zero comparator result;
|
||||
4. ledger entries require source/target commit, class, file allow-list, secret
|
||||
review, tests, hash result and rollback reference.
|
||||
|
||||
Implementation:
|
||||
|
||||
1. call common `jyotish_engine.compute_chart_data()` directly;
|
||||
2. normalize only the contract fields into sorted JSON;
|
||||
3. SHA-256 the normalized bytes;
|
||||
4. expose `--manifest`, `--format json`, `--require-match`;
|
||||
5. validate ledger shape without reading either repository's Git history.
|
||||
|
||||
Verification:
|
||||
|
||||
```bash
|
||||
python3 -m pytest -q tests/test_cross_project_contract.py
|
||||
python3 scripts/cross_project_contract.py --require-match --format json
|
||||
```
|
||||
|
||||
## Task 2: Commercial Repository Port
|
||||
|
||||
Working tree: `/tmp/Jyotisha-jesse-ux` only after reading its `AGENTS.md` and
|
||||
running its pre-work check.
|
||||
|
||||
Tests first: copy the same contract tests. The initial test must fail because the
|
||||
contract files do not exist. Port the source commit's four files without copying
|
||||
deployment configuration or secrets.
|
||||
|
||||
Verification:
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest -q tests/test_cross_project_contract.py
|
||||
.venv/bin/python scripts/cross_project_contract.py --require-match --format json
|
||||
```
|
||||
|
||||
If the commercial virtual environment does not exist, use its documented Python
|
||||
environment and report the missing dependency rather than installing into
|
||||
production or changing deployment configuration.
|
||||
|
||||
## Task 3: Bidirectional Check
|
||||
|
||||
1. run both comparator commands;
|
||||
2. compare their JSON `compatibility_hash` and manifest SHA-256;
|
||||
3. append a research-to-commercial ledger entry with exact commits;
|
||||
4. run public privacy scanning in both repositories;
|
||||
5. commit research changes to `codex/release-hygiene-ci` and commercial changes
|
||||
to `codex/cross-project-contract`; push both branches, but do not merge or
|
||||
deploy commercial `main`.
|
||||
|
||||
## Failure Handling
|
||||
|
||||
- Hash mismatch: do not normalize it away; record mismatch and identify the
|
||||
effective parameter/longitude difference.
|
||||
- Missing external raw: not relevant to a local compatibility hash; retain its
|
||||
existing `blocked` state in reports.
|
||||
- Private or production identifier found: reject the port, record the failure,
|
||||
and do not stage it.
|
||||
- Existing unrelated dirty files: leave untouched.
|
||||
|
||||
## Discovery Adjustment
|
||||
|
||||
Jyotisha does not yet contain the research repository's
|
||||
`domain_calculation_service.py`. This is a phase-two safety port, not a
|
||||
prerequisite for the shared raw-chart compatibility hash. The phase-one
|
||||
comparator therefore targets the common engine API and does not claim REST/API
|
||||
calculation-contract parity.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Rangacharya Phase 2 Source Cards Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add formula-level source cards and wire Rangacharya runtime to them, so no screenshot/archive rule can be silently promoted into calculation or adjudication.
|
||||
|
||||
**Architecture:** Keep `references/rangacharya_source_manifest.json` as source inventory. Add `references/rangacharya_source_cards.json` as formula inventory. `scripts/rangacharya.py` loads cards and attaches card/status/blocker metadata to every experimental output.
|
||||
|
||||
**Tech Stack:** Python 3 stdlib JSON, existing `scripts/rangacharya.py`, pytest.
|
||||
|
||||
## Tasks
|
||||
|
||||
### Task 1: Source Card Tests
|
||||
|
||||
**Files:**
|
||||
- Create `tests/test_rangacharya_source_cards.py`
|
||||
- Create `references/rangacharya_source_cards.json`
|
||||
|
||||
- [ ] Write tests requiring schema, no secrets, no adjudication, and card ids for Phase 2 rule groups.
|
||||
- [ ] Run tests and verify missing-card failure.
|
||||
- [ ] Add minimal cards for core Arudha, Active/Effective Lagna, Sanmukha/Prakriti, special mappings, named yogas, UL family rules, and future article tracks.
|
||||
- [ ] Run tests and verify pass.
|
||||
|
||||
### Task 2: Runtime Card Loader
|
||||
|
||||
**Files:**
|
||||
- Modify `scripts/rangacharya.py`
|
||||
- Modify `tests/test_rangacharya_variant.py`
|
||||
|
||||
- [ ] Write tests requiring outputs to include `source_card_id`, `source_card_status`, and `blocked_reason` when not verified.
|
||||
- [ ] Implement JSON loader with safe fallback.
|
||||
- [ ] Attach card metadata to AL/A7/A10/UL, active lagna, and effective lagna.
|
||||
- [ ] Run focused tests.
|
||||
|
||||
### Task 3: Planning Sync And Verification
|
||||
|
||||
**Files:**
|
||||
- Modify `.planning/rangacharya_vedastro_design_20260716/task_plan.md`
|
||||
- Modify `.planning/rangacharya_vedastro_design_20260716/progress.md`
|
||||
|
||||
- [ ] Record Phase 2 source-card completion.
|
||||
- [ ] Run focused Rangacharya tests.
|
||||
- [ ] Run mandatory pre-work check.
|
||||
- [ ] Run secret scan.
|
||||
@@ -0,0 +1,627 @@
|
||||
# Rangacharya Variant + VedAstro Closure Phase 1 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 the first safe Rangacharya variant slice: source manifest, core variant skeleton, current-vs-variant diff output, non-adjudication guard, and VedAstro raw-closure status hooks.
|
||||
|
||||
**Architecture:** Keep current Jaimini unchanged. Add a small `rangacharya` module that consumes the same chart inputs, emits experimental outputs, and is blocked from verdict/timing use by explicit validation status. Add source-manifest governance before importing any screenshot/archive/article rules into runtime.
|
||||
|
||||
**Tech Stack:** Python 3 standard library, existing `scripts/jaimini.py`, existing `scripts/jyotish_api_server.py`, existing VedAstro gateway/archive contracts, pytest.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Do not store or echo the VedAstro key.
|
||||
- Do not copy AGPL PyJHora code.
|
||||
- Do not use `.workbuddy` as runtime.
|
||||
- Do not change existing `calc_arudha_padas()` behavior.
|
||||
- Every new Rangacharya rule starts `transcribed`, `blocked`, or `experimental_not_for_adjudication`.
|
||||
- Keep unrelated dirty-tree files untouched.
|
||||
|
||||
## File Map
|
||||
|
||||
- Create `references/rangacharya_source_manifest.json`: source inventory and rule validation states.
|
||||
- Create `scripts/rangacharya.py`: independent variant functions and diff helpers.
|
||||
- Create `tests/test_rangacharya_source_manifest.py`: manifest schema/privacy tests.
|
||||
- Create `tests/test_rangacharya_variant.py`: core variant and non-adjudication tests.
|
||||
- Modify `scripts/jyotish_api_server.py`: optional `variant=rangacharya|all` in Jaimini endpoint.
|
||||
- Modify `tests/test_jaimini.py` or create `tests/test_jaimini_rangacharya_api.py`: endpoint contract tests.
|
||||
- Modify existing VedAstro tests only if needed to expose raw-closure status, not to run real credentials in CI.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Source Manifest
|
||||
|
||||
**Files:**
|
||||
- Create: `references/rangacharya_source_manifest.json`
|
||||
- Create: `tests/test_rangacharya_source_manifest.py`
|
||||
|
||||
- [ ] **Step 1: Write failing manifest tests**
|
||||
|
||||
Create `tests/test_rangacharya_source_manifest.py`:
|
||||
|
||||
```python
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MANIFEST = Path("references/rangacharya_source_manifest.json")
|
||||
|
||||
|
||||
def test_manifest_exists_and_has_required_sections():
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
assert data["schema_version"] == 1
|
||||
assert "sources" in data
|
||||
assert "rules" in data
|
||||
assert "validation_ladder" in data
|
||||
|
||||
|
||||
def test_manifest_does_not_contain_secrets():
|
||||
text = MANIFEST.read_text(encoding="utf-8")
|
||||
assert "sk_live_" not in text
|
||||
assert "api_key" not in text.lower()
|
||||
|
||||
|
||||
def test_rules_default_below_adjudication():
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
for rule in data["rules"]:
|
||||
assert rule["status"] in {
|
||||
"transcribed",
|
||||
"source_verified",
|
||||
"golden_verified",
|
||||
"engine_cross_checked",
|
||||
"case_calibrated",
|
||||
"blocked",
|
||||
}
|
||||
assert rule["status"] != "adjudication_enabled"
|
||||
assert rule["adjudication_enabled"] is False
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test, verify failure**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_source_manifest.py -q
|
||||
```
|
||||
|
||||
Expected: FAIL because manifest is missing.
|
||||
|
||||
- [ ] **Step 3: Add minimal manifest**
|
||||
|
||||
Create `references/rangacharya_source_manifest.json` with no secrets:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"created": "2026-07-16",
|
||||
"sources": [
|
||||
{
|
||||
"id": "uploaded_screenshots_20260716",
|
||||
"kind": "user_uploaded_screenshots",
|
||||
"paths": [
|
||||
"/Users/wuyongnaren/Downloads/IMG_3502.PNG",
|
||||
"/Users/wuyongnaren/Downloads/IMG_3503.PNG",
|
||||
"/Users/wuyongnaren/Downloads/IMG_3504.PNG",
|
||||
"/Users/wuyongnaren/Downloads/IMG_3505.PNG",
|
||||
"/Users/wuyongnaren/Downloads/IMG_3506.PNG",
|
||||
"/Users/wuyongnaren/Downloads/IMG_3507.PNG"
|
||||
],
|
||||
"license": "user_private_reference",
|
||||
"privacy": "private",
|
||||
"runtime_use": "reference_only_until_formula_verified"
|
||||
},
|
||||
{
|
||||
"id": "local_article_warehouse_20260716",
|
||||
"kind": "local_research_archive",
|
||||
"path": "/Users/wuyongnaren/文件仓库/印度占星文章",
|
||||
"license": "unknown",
|
||||
"privacy": "local_research",
|
||||
"runtime_use": "manifest_only_until_hash_and_license_review"
|
||||
},
|
||||
{
|
||||
"id": "vedastro_official",
|
||||
"kind": "external_oracle",
|
||||
"url": "https://github.com/VedAstro/VedAstro",
|
||||
"license": "MIT",
|
||||
"privacy": "public",
|
||||
"runtime_use": "oracle_raw_reference"
|
||||
},
|
||||
{
|
||||
"id": "pyjhora_official",
|
||||
"kind": "external_oracle",
|
||||
"url": "https://github.com/naturalstupid/PyJHora",
|
||||
"license": "AGPL-3.0",
|
||||
"privacy": "public",
|
||||
"runtime_use": "isolated_external_process_only"
|
||||
}
|
||||
],
|
||||
"validation_ladder": [
|
||||
"transcribed",
|
||||
"source_verified",
|
||||
"golden_verified",
|
||||
"engine_cross_checked",
|
||||
"case_calibrated",
|
||||
"adjudication_enabled",
|
||||
"blocked"
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"id": "rangacharya_core_arudha",
|
||||
"label": "Rangacharya Arudha core counting",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false
|
||||
},
|
||||
{
|
||||
"id": "active_effective_lagna",
|
||||
"label": "Active Lagna and Effective Lagna",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "transcribed",
|
||||
"adjudication_enabled": false
|
||||
},
|
||||
{
|
||||
"id": "rangacharya_named_yogas",
|
||||
"label": "Dhana/Nirdhana/Kemadruma and other named yogas",
|
||||
"source_ids": ["uploaded_screenshots_20260716"],
|
||||
"status": "blocked",
|
||||
"adjudication_enabled": false,
|
||||
"blocked_reason": "needs formula-level source cards before runtime use"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run test, verify pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_source_manifest.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Stage only manifest and its test.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Rangacharya Variant Skeleton
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/rangacharya.py`
|
||||
- Create: `tests/test_rangacharya_variant.py`
|
||||
|
||||
- [ ] **Step 1: Write failing core tests**
|
||||
|
||||
Create `tests/test_rangacharya_variant.py`:
|
||||
|
||||
```python
|
||||
from scripts import rangacharya
|
||||
|
||||
|
||||
SAMPLE_LONGS = {
|
||||
"Sun": 10.0,
|
||||
"Moon": 45.0,
|
||||
"Mars": 80.0,
|
||||
"Mercury": 110.0,
|
||||
"Jupiter": 145.0,
|
||||
"Venus": 200.0,
|
||||
"Saturn": 250.0,
|
||||
"Rahu": 300.0,
|
||||
"Ketu": 120.0,
|
||||
}
|
||||
|
||||
|
||||
def test_variant_result_is_experimental_and_not_for_adjudication():
|
||||
result = rangacharya.calc_rangacharya_variant(0, SAMPLE_LONGS)
|
||||
assert result["variant"] == "rangacharya"
|
||||
assert result["adjudication_enabled"] is False
|
||||
assert result["status"] == "experimental_not_for_adjudication"
|
||||
|
||||
|
||||
def test_variant_includes_core_sections():
|
||||
result = rangacharya.calc_rangacharya_variant(0, SAMPLE_LONGS)
|
||||
assert "source_status" in result
|
||||
assert "arudha_padas" in result
|
||||
assert "active_lagna" in result
|
||||
assert "effective_lagna" in result
|
||||
|
||||
|
||||
def test_diff_marks_algorithm_names():
|
||||
current = {"AL": {"sign": "Aries"}}
|
||||
variant = {"arudha_padas": {"AL": {"sign": "Taurus"}}}
|
||||
diff = rangacharya.diff_current_vs_rangacharya(current, variant)
|
||||
assert diff["current_algorithm"] == "current_jaimini"
|
||||
assert diff["variant_algorithm"] == "rangacharya"
|
||||
assert diff["differences"][0]["key"] == "AL.sign"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test, verify failure**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_variant.py -q
|
||||
```
|
||||
|
||||
Expected: FAIL because module is missing.
|
||||
|
||||
- [ ] **Step 3: Add minimal module**
|
||||
|
||||
Create `scripts/rangacharya.py`:
|
||||
|
||||
```python
|
||||
"""Experimental Rangacharya/Jaimini variant.
|
||||
|
||||
All outputs are blocked from adjudication until formula-level validation passes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Mapping
|
||||
|
||||
|
||||
SIGNS = [
|
||||
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
||||
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
|
||||
]
|
||||
|
||||
|
||||
def _sign_index(longitude: float) -> int:
|
||||
return int((longitude % 360) // 30)
|
||||
|
||||
|
||||
def _sign_name(index: int) -> str:
|
||||
return SIGNS[index % 12]
|
||||
|
||||
|
||||
def _placeholder_pada(label: str, asc_sign_idx: int, source_house: int) -> Dict[str, Any]:
|
||||
sign_idx = (asc_sign_idx + source_house - 1) % 12
|
||||
return {
|
||||
"label": label,
|
||||
"sign": _sign_name(sign_idx),
|
||||
"sign_index": sign_idx,
|
||||
"source_house": source_house,
|
||||
"validation_status": "transcribed",
|
||||
"adjudication_enabled": False,
|
||||
"note": "Rangacharya formula pending source-card implementation",
|
||||
}
|
||||
|
||||
|
||||
def calc_rangacharya_variant(asc_sign_idx: int, planet_longitudes: Mapping[str, float]) -> Dict[str, Any]:
|
||||
asc_sign_idx %= 12
|
||||
arudha_padas = {
|
||||
"AL": _placeholder_pada("AL", asc_sign_idx, 1),
|
||||
"A7": _placeholder_pada("A7", asc_sign_idx, 7),
|
||||
"A10": _placeholder_pada("A10", asc_sign_idx, 10),
|
||||
"UL": _placeholder_pada("UL", asc_sign_idx, 12),
|
||||
}
|
||||
return {
|
||||
"variant": "rangacharya",
|
||||
"status": "experimental_not_for_adjudication",
|
||||
"adjudication_enabled": False,
|
||||
"source_status": "transcribed",
|
||||
"active_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
"validation_status": "transcribed",
|
||||
"adjudication_enabled": False,
|
||||
},
|
||||
"effective_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
"validation_status": "transcribed",
|
||||
"adjudication_enabled": False,
|
||||
},
|
||||
"arudha_padas": arudha_padas,
|
||||
"input_planets_present": sorted(planet_longitudes),
|
||||
}
|
||||
|
||||
|
||||
def _flatten(prefix: str, value: Any) -> Dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
return {prefix: value}
|
||||
rows: Dict[str, Any] = {}
|
||||
for key, child in value.items():
|
||||
child_key = f"{prefix}.{key}" if prefix else str(key)
|
||||
rows.update(_flatten(child_key, child))
|
||||
return rows
|
||||
|
||||
|
||||
def diff_current_vs_rangacharya(current: Mapping[str, Any], variant: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
current_flat = _flatten("", dict(current))
|
||||
variant_flat = _flatten("", dict(variant.get("arudha_padas", variant)))
|
||||
differences = []
|
||||
for key in sorted(set(current_flat) | set(variant_flat)):
|
||||
current_value = current_flat.get(key)
|
||||
variant_value = variant_flat.get(key)
|
||||
if current_value != variant_value:
|
||||
differences.append({"key": key, "current": current_value, "rangacharya": variant_value})
|
||||
return {
|
||||
"current_algorithm": "current_jaimini",
|
||||
"variant_algorithm": "rangacharya",
|
||||
"adjudication_enabled": False,
|
||||
"differences": differences,
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_variant.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Stage only `scripts/rangacharya.py` and `tests/test_rangacharya_variant.py`.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: API Exposure Without Changing Current Defaults
|
||||
|
||||
**Files:**
|
||||
- Modify: `scripts/jyotish_api_server.py`
|
||||
- Create: `tests/test_jaimini_rangacharya_api.py`
|
||||
|
||||
- [ ] **Step 1: Write endpoint contract tests**
|
||||
|
||||
Create `tests/test_jaimini_rangacharya_api.py` using the existing API test helper style in nearby tests. Assert:
|
||||
|
||||
```python
|
||||
def test_jaimini_default_does_not_include_rangacharya(client):
|
||||
response = client.post("/api/jaimini", json={"ascendant": 0, "planets": []})
|
||||
data = response.get_json()
|
||||
assert "rangacharya" not in data["result"]
|
||||
|
||||
|
||||
def test_jaimini_variant_all_includes_current_variant_and_diff(client):
|
||||
response = client.post("/api/jaimini", json={
|
||||
"ascendant": 0,
|
||||
"variant": "all",
|
||||
"mode": "arudha",
|
||||
"planets": [
|
||||
{"name": "Sun", "longitude": 10},
|
||||
{"name": "Moon", "longitude": 45},
|
||||
{"name": "Mars", "longitude": 80},
|
||||
{"name": "Mercury", "longitude": 110},
|
||||
{"name": "Jupiter", "longitude": 145},
|
||||
{"name": "Venus", "longitude": 200},
|
||||
{"name": "Saturn", "longitude": 250}
|
||||
]
|
||||
})
|
||||
data = response.get_json()
|
||||
assert data["result"]["rangacharya"]["adjudication_enabled"] is False
|
||||
assert data["result"]["rangacharya_diff"]["adjudication_enabled"] is False
|
||||
```
|
||||
|
||||
If existing test fixtures use a different client factory, copy that local pattern exactly.
|
||||
|
||||
- [ ] **Step 2: Run test, verify failure**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_jaimini_rangacharya_api.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add optional variant handling**
|
||||
|
||||
In `scripts/jyotish_api_server.py::_compute_jaimini`:
|
||||
|
||||
```python
|
||||
variant = body.get("variant", "current")
|
||||
if not isinstance(variant, str):
|
||||
raise BadRequest("variant must be a string")
|
||||
variant = variant.strip().lower() or "current"
|
||||
allowed_variants = {"current", "rangacharya", "all"}
|
||||
if variant not in allowed_variants:
|
||||
raise BadRequest(f'variant must be one of: {", ".join(sorted(allowed_variants))}')
|
||||
```
|
||||
|
||||
After current arudha calculation:
|
||||
|
||||
```python
|
||||
if variant in ("rangacharya", "all"):
|
||||
rangacharya = _load_local_module("rangacharya")
|
||||
rangacharya_result = rangacharya.calc_rangacharya_variant(asc_sign_idx, planet_lons)
|
||||
result["rangacharya"] = rangacharya_result
|
||||
current_arudha = result.get("arudha_padas") or jaimini.calc_arudha_padas(asc_sign_idx, planet_lons)
|
||||
result["rangacharya_diff"] = rangacharya.diff_current_vs_rangacharya(current_arudha, rangacharya_result)
|
||||
```
|
||||
|
||||
Preserve default `variant=current`, so existing clients do not see new fields.
|
||||
|
||||
- [ ] **Step 4: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_jaimini_rangacharya_api.py tests/test_jaimini.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Stage only API/test files touched in this task.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Adjudication Guard
|
||||
|
||||
**Files:**
|
||||
- Create: `tests/test_rangacharya_adjudication_guard.py`
|
||||
- Modify: `scripts/rangacharya.py`
|
||||
|
||||
- [ ] **Step 1: Write guard tests**
|
||||
|
||||
Create `tests/test_rangacharya_adjudication_guard.py`:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
|
||||
from scripts import rangacharya
|
||||
|
||||
|
||||
def test_assert_adjudication_allowed_rejects_default_variant():
|
||||
result = rangacharya.calc_rangacharya_variant(0, {"Sun": 10.0})
|
||||
with pytest.raises(rangacharya.RangacharyaValidationError):
|
||||
rangacharya.assert_adjudication_allowed(result)
|
||||
|
||||
|
||||
def test_validation_summary_lists_blocking_rules():
|
||||
result = rangacharya.calc_rangacharya_variant(0, {"Sun": 10.0})
|
||||
summary = rangacharya.validation_summary(result)
|
||||
assert summary["adjudication_enabled"] is False
|
||||
assert summary["blocking_statuses"]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test, verify failure**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_adjudication_guard.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add guard functions**
|
||||
|
||||
Add to `scripts/rangacharya.py`:
|
||||
|
||||
```python
|
||||
class RangacharyaValidationError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def validation_summary(result: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
statuses = []
|
||||
for row in _flatten("", dict(result)).items():
|
||||
key, value = row
|
||||
if key.endswith("validation_status"):
|
||||
statuses.append(str(value))
|
||||
blocking = sorted({status for status in statuses if status != "adjudication_enabled"})
|
||||
return {
|
||||
"adjudication_enabled": bool(result.get("adjudication_enabled")) and not blocking,
|
||||
"blocking_statuses": blocking,
|
||||
}
|
||||
|
||||
|
||||
def assert_adjudication_allowed(result: Mapping[str, Any]) -> None:
|
||||
summary = validation_summary(result)
|
||||
if not summary["adjudication_enabled"]:
|
||||
raise RangacharyaValidationError(
|
||||
"Rangacharya variant is not adjudication-enabled; validation gates are incomplete"
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_rangacharya_adjudication_guard.py tests/test_rangacharya_variant.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Stage only guard files.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: VedAstro Closure Status Hook
|
||||
|
||||
**Files:**
|
||||
- Modify: existing VedAstro gateway/status module found by `rg -n "official_closure_state|VedAstro Raw Archive Manifest|gateway_status" scripts tests`
|
||||
- Modify/Create: focused VedAstro status test
|
||||
|
||||
- [ ] **Step 1: Locate existing contract**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg -n "official_closure_state|VedAstro Raw Archive Manifest|gateway_status|official_raw_response" scripts tests
|
||||
```
|
||||
|
||||
Use the current status function rather than adding a parallel gateway.
|
||||
|
||||
- [ ] **Step 2: Write failing test for env-only key and blocked raw**
|
||||
|
||||
In the existing VedAstro status test file, add a test equivalent to:
|
||||
|
||||
```python
|
||||
def test_vedastro_closure_status_never_exposes_secret(monkeypatch):
|
||||
monkeypatch.setenv("VEDASTRO_API_KEY", "sk_live_test_secret")
|
||||
status = gateway_status()
|
||||
text = json.dumps(status, sort_keys=True)
|
||||
assert "sk_live_test_secret" not in text
|
||||
assert status["official_closure_state"] in {
|
||||
"official_verified",
|
||||
"official_raw_missing_or_unverified",
|
||||
"blocked",
|
||||
"not_configured",
|
||||
}
|
||||
```
|
||||
|
||||
Adapt imports to the existing module.
|
||||
|
||||
- [ ] **Step 3: Implement minimal redaction/status preservation**
|
||||
|
||||
If status currently includes env values, replace with booleans:
|
||||
|
||||
```python
|
||||
"credential_configured": bool(os.environ.get("VEDASTRO_API_KEY")),
|
||||
```
|
||||
|
||||
Never include key contents. If no official raw response exists, set:
|
||||
|
||||
```python
|
||||
"official_closure_state": "official_raw_missing_or_unverified"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run focused VedAstro tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_vedastro_service_adapter_executor.py tests/test_vedastro_official_mcp_bridge.py -q
|
||||
```
|
||||
|
||||
If these are too broad, run only the specific test nodes touched and record that full files were not run.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Stage only VedAstro status/test files touched in this task.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Documentation And Planning Sync
|
||||
|
||||
**Files:**
|
||||
- Modify: `.planning/rangacharya_vedastro_design_20260716/task_plan.md`
|
||||
- Modify: `.planning/rangacharya_vedastro_design_20260716/progress.md`
|
||||
- Modify: `.planning/rangacharya_vedastro_design_20260716/findings.md` if new facts appear
|
||||
|
||||
- [ ] **Step 1: Update planning state**
|
||||
|
||||
Mark design/planning complete and implementation pending/executing according to actual state.
|
||||
|
||||
- [ ] **Step 2: Run mandatory pre-work and focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
|
||||
python3 -m pytest tests/test_rangacharya_source_manifest.py tests/test_rangacharya_variant.py tests/test_rangacharya_adjudication_guard.py tests/test_jaimini_rangacharya_api.py -q
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Final verify no secret leakage**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg -n "sk_live_|VEDASTRO_API_KEY=.*sk_|api_key.*sk_" references scripts tests docs .planning
|
||||
```
|
||||
|
||||
Expected: no real user secret. Existing synthetic examples are acceptable only if already present and clearly fake.
|
||||
|
||||
- [ ] **Step 4: Commit or report uncommitted state**
|
||||
|
||||
If asked to commit, stage only files from this plan. Otherwise leave changes unstaged and report exact files changed.
|
||||
Reference in New Issue
Block a user