Harden high-rigor Jyotish timing workflow

This commit is contained in:
732642856
2026-07-01 19:15:35 +08:00
parent 6276309d41
commit 5a76abf8d6
87 changed files with 17685 additions and 382 deletions
@@ -0,0 +1,315 @@
# Shortest-Path Closure 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:** Collapse the remaining Jyotish hard fronts into the smallest executable closure path: relationship adjudicator closure, Vimsopaka/functional-role closure, oracle closure, and VedAstro strict ingestion.
**Architecture:** Reuse existing strict workflow, adjudicator, oracle, and VedAstro bridge assets instead of opening new product surfaces. Drive every remaining change from fixed regression packs and external-truth comparison scripts so the project stops expanding sideways and starts closing hard boundaries.
**Tech Stack:** Python, pytest, existing MCP strict workflow code, existing full-reading/report pipeline, existing VedAstro adapter scripts, existing oracle comparison scripts, existing frontend static contract tests.
## Global Constraints
- Must obey `/Users/wuyongnaren/Documents/印度占星/AGENTS.md` high-rigor rules.
- Timing/event/outcome work must use `Vimshottari + Narayana Dasha` and relationship work must include `D9 + UL`.
- Functional Benefic/Malefic must remain explicit in high-rigor evidence and user-visible audit surfaces.
- Prefer existing repo-native code and reusable open-source references over new standalone implementations.
- Do not expand scope into new product features while these closure fronts remain open.
- Any claim touching oracle accuracy, global-first status, or perfect precision must stay blocked until external evidence says otherwise.
---
### Task 1: Freeze the closure board and stop scope drift
**Files:**
- Create: `/Users/wuyongnaren/Documents/印度占星/docs/research/shortest_path_closure_board_2026_06_29.md`
- Modify: `/Users/wuyongnaren/Documents/印度占星/docs/research/ACTIVE_FRONTS.md`
- Modify: `/Users/wuyongnaren/Documents/印度占星/task_plan.md`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_skill_gap_truth_audit.py`
**Interfaces:**
- Consumes: `/Users/wuyongnaren/Documents/印度占星/docs/research/ACTIVE_FRONTS.md`, `/Users/wuyongnaren/Documents/印度占星/task_plan.md`
- Produces: `shortest_path_closure_board_2026_06_29.md` with four closure lanes (`relationship`, `vimsopaka_functional_role`, `oracle`, `vedastro_ingestion`) and a fixed task order
- [ ] **Step 1: Write the failing test**
```python
def test_skill_gap_truth_registry_lists_hard_fronts_and_past_corrections() -> None:
data = json.loads(REGISTRY.read_text(encoding="utf-8"))
required_fronts = {
"dasha_external_oracle",
"shadbala_external_absolute_values",
"tajika_sahams_annual_closure",
"article_template_industrialization",
"long_term_public_benchmark",
}
assert required_fronts <= set(data["hard_fronts"])
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_skill_gap_truth_audit.py -q`
Expected: FAIL if the closure board / active fronts drift exposes missing hard-front accounting
- [ ] **Step 3: Write minimal implementation**
```markdown
# Shortest Path Closure Board
## Lane 1 - Relationship Adjudicator Closure
1. lock legal_marriage/public_formalization regression pack
2. close Jaimini marriage bridge v1 regression loop
3. keep public_formalization_candidate narrative conservative everywhere
## Lane 2 - Vimsopaka + Functional Role Closure
1. map NEECHA_BHANGA / GREAT_FRIEND / GREAT_ENEMY
2. render functional benefic/malefic everywhere the user sees Technique Audit
## Lane 3 - Oracle Closure
1. batch Dasha/Shadbala/JHora comparison pack
2. promote first external verified packet
3. update oracle dashboard only from validated packets
## Lane 4 - VedAstro Strict Ingestion
1. keep current blocked boundary
2. run one endpoint-backed smoke only after relationship loop is closed
3. ingest only allowlisted event windows into evidence ledger
```
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_skill_gap_truth_audit.py -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add docs/research/shortest_path_closure_board_2026_06_29.md docs/research/ACTIVE_FRONTS.md task_plan.md tests/test_skill_gap_truth_audit.py
git commit -m "docs: freeze shortest-path closure board"
```
### Task 2: Close the relationship adjudicator regression pack
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_relationship.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes: `event_judgement.dominant_label`, `event_judgement.secondary_context`, `present_evidence.jaimini_marriage_support`
- Produces: locked conservative handling for `public_formalization_candidate`, and red/green regression coverage for `legal_marriage` vs `public_formalization`
- [ ] **Step 1: Write the failing tests**
```python
def test_relationship_narrative_payload_does_not_translate_public_formalization_candidate_plus_synastry_support_plus_weak_core_promise_into_marriage_approach() -> None:
strict = _collect_strict_evidence("relationship", _base_relationship_result())
strict["event_judgement"]["dominant_label"] = None
strict["event_judgement"]["secondary_context"] = [
"darakaraka_active",
"ul_support",
"synastry_support",
"synastry_compatibility_support",
"public_formalization_candidate",
]
strict["confidence_cap"] = "low"
payload = jyotish_engine._build_relationship_narrative_payload(strict)
assert any("不能误读成接近结婚" in item for item in payload["risks"])
assert any("不得越权抬升 legal_marriage" in item for item in payload["boundaries"])
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `python3 -m pytest tests/test_mcp_strict_workflow_relationship.py tests/test_frontend_productization.py tests/test_api_server_security.py -q -k "public_formalization or weak_core_promise or misread_as_near_marriage"`
Expected: FAIL on the exact conservative boundary you are adding
- [ ] **Step 3: Write minimal implementation**
```python
if "public_formalization_candidate" in secondary_context:
risks.append("当前虽更接近 public_formalization_candidate,但在 timing conflict 未解除前,不能误读成接近结婚。")
boundaries.append("public_formalization_candidate 只表示公开化候选,不等于法律婚姻,不能越权替代 legal_marriage。")
```
```javascript
const status = hasPublicFormalizationCandidate && hasConflictWarning ? 'needs_context' : ...
const statusLabel = hasPublicFormalizationCandidate && hasConflictWarning
? '公开化候选,不等于婚姻逼近'
: ...
```
- [ ] **Step 4: Run tests to verify they pass**
Run: `python3 -m pytest tests/test_mcp_strict_workflow_relationship.py tests/test_frontend_productization.py tests/test_api_server_security.py -q -k "public_formalization or weak_core_promise or misread_as_near_marriage"`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py scripts/jyotish_engine.py jyotish-app/main.js tests/test_mcp_strict_workflow_relationship.py tests/test_frontend_productization.py tests/test_api_server_security.py
git commit -m "test: lock relationship public formalization boundaries"
```
### Task 3: Close Vimsopaka semantic mapping and functional-role rendering
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/skill-map.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/references/strict-workflow-router.md`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes: existing dignity guardrail output and functional benefic/malefic evidence
- Produces: user-visible audit rendering for functional benefics/malefics/yogakarakas/neutrals and Vimsopaka semantic labels
- [ ] **Step 1: Write the failing tests**
```python
def test_report_artifact_relationship_strict_narrative_surfaces_public_formalization_candidate_boundary() -> None:
html = artifact["html"]
assert "Functional Benefic/Malefic" in html
assert "Yogakaraka" in html
assert "functional neutral" in html.lower() or "中性星" in html
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `python3 -m pytest tests/test_frontend_productization.py tests/test_api_server_security.py -q -k "Functional Benefic or Yogakaraka or Vimsopaka"`
Expected: FAIL because at least one user-visible rendering path is incomplete
- [ ] **Step 3: Write minimal implementation**
```python
summary_parts.append(f"Yogakaraka: {', '.join(yogakarakas) if yogakarakas else 'None'}")
summary_parts.append(f"Functional neutrals: {', '.join(functional_neutrals) if functional_neutrals else 'None'}")
```
```javascript
['Functional Benefic/Malefic / 功能吉凶星', '已接入', '按 Lagna 输出功能吉星、功能凶星、Yogakaraka 与中性星,并进入 Technique Audit Table']
```
- [ ] **Step 4: Run tests to verify they pass**
Run: `python3 -m pytest tests/test_frontend_productization.py tests/test_api_server_security.py -q -k "Functional Benefic or Yogakaraka or Vimsopaka"`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py scripts/jyotish_api_server.py jyotish-app/skill-map.js references/strict-workflow-router.md tests/test_frontend_productization.py tests/test_api_server_security.py
git commit -m "feat: close functional role and vimsopaka rendering gaps"
```
### Task 4: Batch the oracle closure work into one comparison pack
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/shadbala_oracle_comparison.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/oracle_benchmark_inventory.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/references/oracle/dasha_shadbala_oracle_cases.json`
- Create: `/Users/wuyongnaren/Documents/印度占星/docs/research/oracle_batch_closure_pack_2026_06_29.md`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_dasha_oracle_closure_status.py`
**Interfaces:**
- Consumes: external oracle packets, current shadbala comparison script, oracle benchmark inventory
- Produces: one batch comparison report with pass/fail/tolerance rows and updated blocked/unblocked truth summary
- [ ] **Step 1: Write the failing test**
```python
def test_dasha_oracle_closure_status_markdown_keeps_global_calibration_blocked_until_non_dasha_packets_pass() -> None:
markdown = build_status_markdown(sample_status)
assert "Keep global calibration blocked" in markdown
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_dasha_oracle_closure_status.py -q`
Expected: FAIL if the batch comparison summary or blocked boundary drifts
- [ ] **Step 3: Write minimal implementation**
```markdown
# Oracle Batch Closure Pack
| Case | Source | Domain | Pass | Notes |
|---|---|---|---|---|
| steve_jobs | JHora/PyJHora | Dasha | yes/no | boundary diff |
| ... | ... | Shadbala | yes/no | tolerance diff |
```
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_dasha_oracle_closure_status.py -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/shadbala_oracle_comparison.py scripts/oracle_benchmark_inventory.py references/oracle/dasha_shadbala_oracle_cases.json docs/research/oracle_batch_closure_pack_2026_06_29.md tests/test_dasha_oracle_closure_status.py
git commit -m "feat: batch oracle closure comparison pack"
```
### Task 5: Keep VedAstro ingestion minimal and honest
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/vedastro_service_adapter.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/run_quality_gate.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_life_event_graph_v1.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_service_adapter_executor.py`
**Interfaces:**
- Consumes: current `external_window` payload contract and allowlist audit
- Produces: one endpoint-backed smoke path or one controlled blocked path, both feeding the same evidence ledger contract
- [ ] **Step 1: Write the failing tests**
```python
def test_life_event_graph_keeps_external_window_nodes_allowlisted() -> None:
assert any(node["kind"] == "external_window" for node in strict["life_event_graph"]["event_nodes"])
```
- [ ] **Step 2: Run tests to verify they fail**
Run: `python3 -m pytest tests/test_life_event_graph_v1.py tests/test_vedastro_service_adapter_executor.py -q`
Expected: FAIL if the allowlist / blocked contract is incomplete
- [ ] **Step 3: Write minimal implementation**
```python
if not endpoint or not network_enabled:
return {"status": "blocked", "reason": "vedastro_live_endpoint_or_network_flag_missing"}
return {"status": "ok", "event_nodes": filtered_nodes}
```
- [ ] **Step 4: Run tests to verify they pass**
Run: `python3 -m pytest tests/test_life_event_graph_v1.py tests/test_vedastro_service_adapter_executor.py -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/vedastro_service_adapter.py scripts/run_quality_gate.py tests/test_life_event_graph_v1.py tests/test_vedastro_service_adapter_executor.py
git commit -m "feat: keep vedastro ingestion minimal and allowlisted"
```
## Self-Review
- Spec coverage: the plan covers the four remaining closure lanes called out by current active fronts.
- Placeholder scan: no TBD/TODO placeholders remain.
- Type consistency: plan uses the existing `dominant_label`, `secondary_context`, `relationship_narrative`, `external_window`, and blocked-boundary terminology already present in the repo.
## Execution Handoff
Plan complete and saved to `docs/superpowers/plans/2026-06-29-shortest-path-closure-plan.md`. Two execution options:
**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
Which approach?
@@ -0,0 +1,410 @@
# Chart Async And Unified Stage 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:** Add a reusable lightweight async `job_id + poll` lane for normal `/api/chart` while promoting `full-reading` stage timings into a clearer unified contract.
**Architecture:** Reuse the existing file-backed high-rigor job runner by extracting shared chart/high-rigor job helpers inside `scripts/jyotish_api_server.py`. Reuse the existing `full-reading` stage timing instrumentation in `scripts/jyotish_engine.py` and only reshape it into grouped stage metadata instead of adding new heavy computation.
**Tech Stack:** Python, existing HTTPServer API server, existing file-backed scratch job records, pytest.
## Global Constraints
- Reuse current job runner; do not add Redis, Celery, RQ, or a second queue.
- Keep sync `/api/chart` behavior unchanged unless `async`/`enqueue` is explicitly requested.
- Keep completed chart async result identical to sync chart payload.
- Do not add new heavy computation for stage grouping; reshape existing timing only.
- Preserve current honesty boundaries around VedAstro partial/blocked states.
---
### Task 1: Promote full-reading stage timings into a unified stage contract
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
**Interfaces:**
- Consumes:
- `summary["stage_timings"]: list[dict]`
- Produces:
- `summary["stage_contract_version"]: int`
- `summary["stage_groups"]: list[dict]`
- `summary["cache_recommendations"]: dict`
- `summary["async_recommendations"]: dict`
- [ ] **Step 1: Write the failing test**
Add a focused test in `tests/test_cli_smoke.py` asserting the new contract fields:
```python
def test_full_reading_summary_exposes_unified_stage_groups() -> None:
result = run_engine(
"full-reading",
"--year", "1990",
"--month", "1",
"--day", "1",
"--hour", "12",
"--minute", "0",
"--lat", "39.9",
"--lon", "116.4",
"--tz", "8",
"--today", "2026-01-01",
"--transit-date", "2026-01-01",
)
summary = result["summary"]
assert summary["stage_contract_version"] == 1
assert isinstance(summary["stage_groups"], list)
assert any(group["group"] == "official_evidence" for group in summary["stage_groups"])
assert summary["cache_recommendations"]["api_chart_response"] == "recommended"
assert summary["async_recommendations"]["chart_async_optional"] is True
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py::test_full_reading_summary_exposes_unified_stage_groups -q
```
Expected: FAIL with missing `stage_contract_version` or `stage_groups`.
- [ ] **Step 3: Write minimal implementation**
In `scripts/jyotish_engine.py`, add a small helper near the stage-timing helpers:
```python
def _build_unified_stage_contract(stage_timings):
groups = {
'local_core': ['core_chart_and_setup', 'dasha_and_core_varga_stack', 'advanced_interpretation_and_timing_layers', 'dynamic_hooks'],
'official_evidence': ['vedastro_official_snapshot', 'vedastro_main_entry_overview'],
'contract_and_prompt': ['strict_contracts', 'guided_topics', 'ai_prompt_pack'],
}
rows = []
for group_name, stage_names in groups.items():
matched = [row for row in stage_timings if row.get('stage') in stage_names]
rows.append({
'group': group_name,
'stages': [row.get('stage') for row in matched],
'elapsed_seconds': round(sum(float(row.get('elapsed_seconds', 0) or 0) for row in matched), 4),
'execution_mode': (
'sync_remote_heavy' if group_name == 'official_evidence'
else 'sync_structuring' if group_name == 'contract_and_prompt'
else 'sync_local'
),
})
return {
'stage_contract_version': 1,
'stage_groups': rows,
'cache_recommendations': {
'api_chart_response': 'recommended',
'official_full_snapshot_semantic': 'recommended',
},
'async_recommendations': {
'chart_async_optional': True,
'high_rigor_async_recommended': True,
},
}
```
Then merge that helper output into `report['summary']` after `stage_timings` and `slowest_stages` are computed.
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py::test_full_reading_summary_exposes_unified_stage_groups -q
```
Expected: PASS
### Task 2: Generalize the file-backed async job runner for chart and high-rigor scopes
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes:
- existing `_write_high_rigor_job_record(job_id, payload)`
- existing `_load_high_rigor_job_record(job_id)`
- Produces:
- shared `_enqueue_async_job(...)`
- shared `_get_async_job(...)`
- chart scope job records
- [ ] **Step 1: Write the failing tests**
Add tests in `tests/test_api_server_security.py`:
```python
def test_chart_async_submit_returns_job_id(monkeypatch: pytest.MonkeyPatch) -> None:
handler = _handler()
monkeypatch.setattr(handler, '_enqueue_chart_job', lambda body: {
'success': True,
'endpoint': 'chart_async',
'mode': 'async_submitted',
'job_id': 'chart_test_job_1',
'status': 'queued',
'poll_path': '/api/chart/jobs/chart_test_job_1',
'scope': 'api_chart_response',
})
result = handler._compute_chart({'async': True, 'year': REDACTED_YEAR, 'month': 4, 'day': 17, 'hour': 14, 'minute': 49, 'lat': 36.42, 'lon': 114.2, 'tz': 8})
assert result['mode'] == 'async_submitted'
assert result['job_id'] == 'chart_test_job_1'
assert result['poll_path'].endswith('/chart_test_job_1')
```
```python
def test_chart_job_poll_endpoint_returns_cached_job_payload(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(jyotish_api_server, '_load_async_job_record', lambda scope, job_id: {
'success': True,
'endpoint': 'chart_async',
'mode': 'async_result',
'job_id': job_id,
'status': 'completed',
'result': {'success': True, 'runtime_cache': {'scope': 'api_chart_response'}},
})
handler = _HighRigorJobCaptureHandler('/api/chart/jobs/chart_test_job_2')
handler.do_GET()
payload = handler.payload()
assert payload['job_id'] == 'chart_test_job_2'
assert payload['result']['runtime_cache']['scope'] == 'api_chart_response'
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "chart_async_submit_returns_job_id or chart_job_poll_endpoint_returns_cached_job_payload" -q
```
Expected: FAIL because `/api/chart` has no async branch and `/api/chart/jobs/...` is not routed.
- [ ] **Step 3: Write minimal implementation**
In `scripts/jyotish_api_server.py`:
1. Add a generic job storage layer:
```python
def _job_dir(scope: str) -> Path:
path = Path(REPO_ROOT) / 'scratch' / 'local' / f'{scope}_jobs'
path.mkdir(parents=True, exist_ok=True)
return path
def _job_path(scope: str, job_id: str) -> Path:
return _job_dir(scope) / f'{job_id}.json'
def _load_async_job_record(scope: str, job_id: str) -> dict | None:
path = _job_path(scope, job_id)
...
def _write_async_job_record(scope: str, job_id: str, payload: dict) -> dict:
...
```
2. Keep high-rigor wrappers calling the shared helpers.
3. Add `_enqueue_chart_job(body)` that runs `_compute_chart_sync(body_without_async_flags)` in a background thread.
4. Add `_compute_chart_sync(body)` by moving current synchronous `_compute_chart` body there.
5. Make `_compute_chart(body)` return `_enqueue_chart_job(body)` when `async` or `enqueue` is set.
6. Add `GET /api/chart/jobs/{job_id}` in `do_GET`.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "chart_async_submit_returns_job_id or chart_job_poll_endpoint_returns_cached_job_payload" -q
```
Expected: PASS
### Task 3: Make chart async completion return the normal chart payload unchanged
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes:
- `_compute_chart_sync(body: dict) -> dict`
- Produces:
- async chart completed record with `result` equal to normal chart payload shape
- [ ] **Step 1: Write the failing test**
Add:
```python
def test_chart_async_job_executes_in_background(monkeypatch: pytest.MonkeyPatch) -> None:
handler = _handler()
writes = []
def fake_write(scope: str, job_id: str, payload: dict) -> dict:
writes.append((scope, job_id, dict(payload)))
return payload
def fake_sync(body: dict) -> dict:
time.sleep(0.05)
return {'success': True, 'modules': {'chart': {'planets': {}, 'ascendant': {}}}, 'runtime_cache': {'scope': 'api_chart_response'}}
monkeypatch.setattr(jyotish_api_server, '_write_async_job_record', fake_write)
monkeypatch.setattr(handler, '_compute_chart_sync', fake_sync)
result = handler._enqueue_chart_job({'async': True, 'year': REDACTED_YEAR, 'month': 4, 'day': 17, 'hour': 14, 'minute': 49, 'lat': 36.42, 'lon': 114.2, 'tz': 8})
assert result['endpoint'] == 'chart_async'
assert result['status'] == 'queued'
deadline = time.time() + 1.0
while len(writes) < 3 and time.time() < deadline:
time.sleep(0.01)
assert writes[-1][2]['status'] == 'completed'
assert writes[-1][2]['result']['runtime_cache']['scope'] == 'api_chart_response'
assert 'modules' in writes[-1][2]['result']
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py::test_chart_async_job_executes_in_background -q
```
Expected: FAIL because chart async job path does not exist yet.
- [ ] **Step 3: Write minimal implementation**
Implement `_enqueue_chart_job(body)` as a thin wrapper around the shared async runner:
```python
def _enqueue_chart_job(self, body):
body_copy = dict(body or {})
body_copy.pop('async', None)
body_copy.pop('enqueue', None)
return self._enqueue_async_job(
scope='api_chart_response',
endpoint='chart_async',
job_prefix='chart',
poll_base='/api/chart/jobs',
compute_fn=lambda: self._compute_chart_sync(body_copy),
)
```
Keep completed `result` untouched.
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py::test_chart_async_job_executes_in_background -q
```
Expected: PASS
### Task 4: Keep existing high-rigor async path working on top of the shared helper
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes:
- shared async helper
- Produces:
- backward-compatible high-rigor async submit and polling
- [ ] **Step 1: Re-run existing high-rigor async tests as regression guards**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "high_rigor_async_submit_returns_job_id or high_rigor_job_poll_endpoint_returns_cached_job_payload or high_rigor_async_job_executes_in_background" -q
```
Expected: If this fails after Task 2/3 changes, fix compatibility before proceeding.
- [ ] **Step 2: Minimal compatibility implementation**
Keep wrappers like:
```python
def _enqueue_high_rigor_job(self, body):
...
return self._enqueue_async_job(
scope=_HIGH_RIGOR_JOB_SCOPE,
endpoint='high_rigor_workflow_async',
job_prefix='hrw',
poll_base='/api/high_rigor_workflow/jobs',
compute_fn=lambda: self._compute_high_rigor_workflow_sync(body_copy),
)
```
And route `GET /api/high_rigor_workflow/jobs/{job_id}` through the shared loader.
- [ ] **Step 3: Run regression tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "high_rigor_async_submit_returns_job_id or high_rigor_job_poll_endpoint_returns_cached_job_payload or high_rigor_async_job_executes_in_background" -q
```
Expected: PASS
### Task 5: Run focused regression bundle and update progress
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/progress.md`
**Interfaces:**
- Consumes:
- all prior tasks
- Produces:
- verification record in `progress.md`
- [ ] **Step 1: Run focused regression bundle**
Run:
```bash
python3 -m pytest \
tests/test_cli_smoke.py::test_full_reading_summary_exposes_stage_timing_contract \
tests/test_cli_smoke.py::test_full_reading_summary_exposes_unified_stage_groups \
tests/test_api_server_security.py -k "chart_async or high_rigor_async or runtime_cache or fragment_audit_blocks_registry_surface_drift" \
tests/test_mcp_strict_workflow_career.py \
tests/test_mcp_strict_workflow_relationship.py \
tests/test_mcp_strict_workflow_finance.py \
-q
```
Expected: PASS
- [ ] **Step 2: Run chart/high-rigor API regression bundle**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py tests/test_historical_event_backtest.py -q
```
Expected: PASS
- [ ] **Step 3: Update progress.md**
Add an entry covering:
- unified stage contract added to `full-reading`
- `/api/chart` async submit + poll landed
- shared async helper now serves chart + high-rigor
- focused verification results
@@ -0,0 +1,357 @@
# Technique Audit Strict Adjudication 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:** Make the existing `Technique Audit Table` a required compact gate inside strict adjudication for `career`, `relationship`, and `finance`.
**Architecture:** Reuse the current strict workflow contracts in `mcp_server.py` and the current prompt-pack audit table in `scripts/jyotish_engine.py`. Add one compact shared audit summary, then surface it through the existing engine/API/frontend layers without adding new compute-heavy logic.
**Tech Stack:** Python, existing strict workflow collectors, existing prompt-pack contract, pytest, existing frontend static contract tests.
## Global Constraints
- Reuse current `Technique Audit Table`; do not create a second audit table system.
- Do not add new heavy computation or new external requests.
- Keep `career`, `relationship`, and `finance` route-specific gates intact.
- Preserve honesty boundaries: `blocked`, `fallback_used`, `conflicts`, and `confidence_cap` must remain explicit.
- Functional benefic/malefic must remain visible as a first-class high-rigor gate.
---
### Task 1: Add compact technique audit summary to strict workflow contracts
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_career.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_relationship.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_finance.py`
**Interfaces:**
- Consumes:
- `strict["present_evidence"]`
- `strict["official_primary_evidence"]`
- `strict["local_supplemental_evidence"]`
- `strict["fallback_used"]`
- `strict["blocked_items"]`
- `strict["conflicts"]`
- Produces:
- `strict["technique_audit_summary"]`
- `strict["multi_reference_reading_summary"]["audit_gate_frame"]`
- [ ] **Step 1: Write the failing tests**
```python
def test_career_strict_contract_exposes_compact_technique_audit_summary() -> None:
strict = _collect_strict_evidence("career", _sample_result())
audit = strict["technique_audit_summary"]
assert audit["functional_benefic_malefic"]["gate"] == "hard"
assert audit["relevant_vargas"]["gate"] == "hard"
assert audit["vimshottari_narayana_crosscheck"]["gate"] == "hard"
assert audit["source_priority_boundary"]["fallback_used"] == strict["fallback_used"]
```
```python
def test_relationship_multi_reference_summary_carries_audit_gate_frame() -> None:
strict = _collect_strict_evidence("relationship", _sample_result())
frame = strict["multi_reference_reading_summary"]["audit_gate_frame"]
assert frame["functional_benefic_malefic"]["used"] in {True, False}
assert frame["source_priority_boundary"]["blocked_items"] == strict["blocked_items"]
```
```python
def test_finance_strict_contract_compact_audit_marks_dual_dasha_gate() -> None:
strict = _collect_strict_evidence("finance", {"modules": {"source_priority": {"mode": "local_fallback_official_blocked"}}})
audit = strict["technique_audit_summary"]
assert audit["vimshottari_narayana_crosscheck"]["gate"] == "hard"
assert "official_primary_chart_blocked" in audit["source_priority_boundary"]["blocked_items"]
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py -k "compact_technique_audit_summary or audit_gate_frame or dual_dasha_gate" -q
```
Expected: FAIL with missing `technique_audit_summary` or `audit_gate_frame`.
- [ ] **Step 3: Write minimal implementation**
Add compact builders in `mcp_server.py`:
```python
def _route_varga_gate_keys(route: str) -> list[str]:
if route == "career":
return ["d10_dasamsa", "a10_karma_pada", "amatyakaraka", "karakamsha"]
if route == "relationship":
return ["d9_navamsa", "upapada_lagna", "darakaraka", "vivah_saham"]
if route == "finance":
return ["d2_hora", "d10_dasamsa", "shadbala", "ashtakavarga_house_scores"]
return []
def _build_technique_audit_summary(route: str, strict: Dict[str, Any]) -> Dict[str, Any]:
present = strict.get("present_evidence") or {}
official = strict.get("official_primary_evidence") or {}
local = strict.get("local_supplemental_evidence") or {}
fallback_used = strict.get("fallback_used") or []
blocked_items = strict.get("blocked_items") or []
conflicts = strict.get("conflicts") or []
return {
"functional_benefic_malefic": {
"gate": "hard",
"used": bool((present.get("functional_benefic_malefic") or {}).get("status") == "used"),
"note": (present.get("functional_benefic_malefic") or {}).get("effect_on_confidence"),
},
"relevant_vargas": {
"gate": "hard",
"required_keys": _route_varga_gate_keys(route),
"present_keys": [key for key in _route_varga_gate_keys(route) if present.get(key)],
},
"vimshottari_narayana_crosscheck": {
"gate": "hard",
"used": bool(present.get("vimshottari_current")) and bool(present.get("narayana_current")),
"required_timing_systems": ["Vimshottari", "Narayana"],
},
"source_priority_boundary": {
"gate": "boundary",
"official": official,
"local": local,
"fallback_used": fallback_used,
"blocked_items": blocked_items,
"conflicts": conflicts,
},
}
```
Attach it in `_attach_top_reader_contract(...)` and mirror it into `multi_reference_reading_summary["audit_gate_frame"]`.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py -k "compact_technique_audit_summary or audit_gate_frame or dual_dasha_gate" -q
```
Expected: PASS
### Task 2: Compact and expose the audit summary through full-reading and prompt pack
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
**Interfaces:**
- Consumes:
- `modules[*_strict_evidence]`
- Produces:
- compact strict contracts that include `technique_audit_summary`
- [ ] **Step 1: Write the failing test**
```python
def test_full_reading_prompt_pack_carries_compact_technique_audit_summary() -> None:
result = run_engine(...)
career = result["ai_prompt_pack"]["evidence_snapshot"]["strict_workflow_contracts"]["career"]
assert "technique_audit_summary" in career
assert career["technique_audit_summary"]["functional_benefic_malefic"]["gate"] == "hard"
assert "audit_gate_frame" in career["multi_reference_reading_summary"]
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py -k "compact_technique_audit_summary" -q
```
Expected: FAIL with missing `technique_audit_summary`.
- [ ] **Step 3: Write minimal implementation**
Extend `_compact_strict_workflow_contract(...)` in `scripts/jyotish_engine.py`:
```python
"technique_audit_summary": strict.get("technique_audit_summary") or {},
```
Do not recompute anything new; just pass through the strict contract.
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py -k "compact_technique_audit_summary" -q
```
Expected: PASS
### Task 3: Surface the same compact audit summary in API outputs
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes:
- strict contract from prompt pack / official full snapshot
- Produces:
- API outputs containing `technique_audit_summary`
- [ ] **Step 1: Write the failing tests**
```python
def test_high_rigor_summary_passes_through_compact_technique_audit_summary() -> None:
result = handler._high_rigor_vedastro_official_summary(chart)
assert result["technique_audit_summary"]["functional_benefic_malefic"]["gate"] == "hard"
```
```python
def test_consultation_workflow_surfaces_compact_technique_audit_summary(monkeypatch) -> None:
result = handler._compute_consultation_workflow(payload)
assert "technique_audit_summary" in result["vedastro_official"]
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "compact_technique_audit_summary" -q
```
Expected: FAIL with missing API passthrough key.
- [ ] **Step 3: Write minimal implementation**
In `scripts/jyotish_api_server.py`, add:
```python
'technique_audit_summary': primary_contract.get('technique_audit_summary') or {},
```
to the shared official summary output path.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py -k "compact_technique_audit_summary" -q
```
Expected: PASS
### Task 4: Make frontend and AI chat consume the compact audit summary
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/ai-chat.js`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
**Interfaces:**
- Consumes:
- compact strict contract
- Produces:
- UI/AI references to `technique_audit_summary`
- [ ] **Step 1: Write the failing test**
```python
def test_frontend_consumes_compact_technique_audit_summary_in_top_reader_contract() -> None:
main = read("main.js")
ai_chat = read("ai-chat.js")
assert "technique_audit_summary" in main
assert "technique_audit_summary" in ai_chat
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
python3 -m pytest tests/test_frontend_productization.py -k "compact_technique_audit_summary" -q
```
Expected: FAIL with missing frontend references.
- [ ] **Step 3: Write minimal implementation**
In `main.js` and `ai-chat.js`, read:
```javascript
const techniqueAuditSummary = topReaderContract.technique_audit_summary || {};
```
and surface only a compact summary line, not the whole raw audit table.
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
python3 -m pytest tests/test_frontend_productization.py -k "compact_technique_audit_summary" -q
```
Expected: PASS
### Task 5: Run focused regression and update progress
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/progress.md`
**Interfaces:**
- Consumes:
- all changes from Tasks 1-4
- Produces:
- verification note in `progress.md`
- [ ] **Step 1: Run focused regression**
Run:
```bash
python3 -m pytest \
tests/test_mcp_strict_workflow_career.py \
tests/test_mcp_strict_workflow_relationship.py \
tests/test_mcp_strict_workflow_finance.py \
tests/test_cli_smoke.py \
tests/test_api_server_security.py \
tests/test_frontend_productization.py \
-k "compact_technique_audit_summary or audit_gate_frame" -q
```
Expected: PASS
- [ ] **Step 2: Update progress**
Add an entry that the compact `Technique Audit Table` gate now sits inside default strict adjudication.
- [ ] **Step 3: Run diff hygiene**
Run:
```bash
git diff --check
```
Expected: no output
## Self-Review
- Spec coverage: compact strict audit gate, prompt-pack passthrough, API passthrough, frontend consumption, and focused regression are all mapped to tasks.
- Placeholder scan: no TODO/TBD placeholders remain.
- Type consistency: `technique_audit_summary` and `audit_gate_frame` are used consistently across strict contract, prompt pack, API, and frontend.
## Execution Handoff
**Plan complete and saved to `docs/superpowers/plans/2026-06-30-technique-audit-strict-adjudication.md`. Two execution options:**
**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
**Which approach?**
@@ -0,0 +1,571 @@
# Top Reader Adjudication 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:** Upgrade the default `career`, `relationship`, and `finance` reading workflows to use a shared top-reader-style adjudication skeleton with multi-reference summaries and selected modifier bridges, while reusing existing strict workflow code and minimizing extra compute.
**Architecture:** Reuse the current strict workflow builders in `mcp_server.py` as the primary evidence source, normalize them through a shared adjudication helper, and surface the reshaped contract through `jyotish_engine.py`, `jyotish_api_server.py`, and the existing prompt-pack/frontend consumer layers. Do not introduce a second engine or a full 641-callable VedAstro execution path; instead, reshape current evidence into a common four-stage contract plus a lightweight `multi_reference_reading_summary`.
**Tech Stack:** Python, existing strict workflow collectors, pytest, existing frontend productization tests, existing prompt-pack evidence snapshot contract.
## Global Constraints
- Reuse current strict workflow collectors before adding new collectors.
- Reuse existing bridge helpers before creating new scoring paths.
- Reuse current full-reading/module outputs by reference where possible.
- Do not add all-method VedAstro execution to the default path.
- Prefer small contract reshaping over new compute-heavy logic.
- Keep official calls cached and reused; do not add new heavyweight request fans.
- Preserve the existing honesty boundaries: emit `blocked`, `conflicts`, and `confidence_cap` instead of smoothing over missing layers.
- Keep `career`, `relationship`, and `finance` domain-specific evidence requirements intact while sharing structure.
---
### Task 1: Add the shared adjudication contract builder in `mcp_server.py`
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_career.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_relationship.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_finance.py`
**Interfaces:**
- Consumes:
- existing strict route `present_evidence`
- existing `event_judgement`
- existing `official_primary_evidence`
- existing `local_supplemental_evidence`
- existing `conflicts`
- existing `confidence_cap`
- Produces:
- `_build_adjudication_stages(route: str, present: Dict[str, Any], event_judgement: Dict[str, Any]) -> Dict[str, Any]`
- `_build_multi_reference_reading_summary(route: str, present: Dict[str, Any], strict: Dict[str, Any]) -> Dict[str, Any]`
- strict contract keys:
- `adjudication_stages`
- `multi_reference_reading_summary`
- `verdict`
- `dominant_label`
- `main_conflicts`
- [ ] **Step 1: Write the failing tests**
Add assertions to each strict workflow domain test file for the new shared fields:
```python
def test_career_strict_contract_exposes_adjudication_stages() -> None:
result = mcp_server._collect_strict_evidence("career", modules)
assert result["adjudication_stages"]["promise"]["status"] in {"present", "weak", "missing"}
assert result["adjudication_stages"]["activation"]["required_timing_systems"] == ["Vimshottari", "Narayana"]
assert "multi_reference_reading_summary" in result
assert "root_frame" in result["multi_reference_reading_summary"]
```
Repeat the same shape expectation for `relationship` and `finance`, adapted to each route.
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py -k "adjudication_stages or multi_reference_reading_summary" -q
```
Expected: FAIL with missing strict contract keys such as `adjudication_stages` or `multi_reference_reading_summary`.
- [ ] **Step 3: Write the minimal implementation**
Implement shared helpers near the existing strict helper section in `mcp_server.py`:
```python
def _build_adjudication_stages(route: str, present: Dict[str, Any], event_judgement: Dict[str, Any]) -> Dict[str, Any]:
dominant_label = event_judgement.get("dominant_label")
return {
"promise": {
"status": "present" if _has_promise_evidence(route, present) else "weak",
"drivers": _promise_drivers(route, present),
},
"activation": {
"status": "present" if _has_activation_evidence(route, present) else "weak",
"required_timing_systems": ["Vimshottari", "Narayana"],
"drivers": _activation_drivers(route, present),
},
"manifestation": {
"status": "present" if dominant_label else "weak",
"drivers": event_judgement.get("secondary_context") or [],
},
"label": {
"status": "present" if dominant_label else "missing",
"value": dominant_label,
"verdict": event_judgement.get("verdict"),
},
}
```
Also add a small route-aware summary builder:
```python
def _build_multi_reference_reading_summary(route: str, present: Dict[str, Any], strict: Dict[str, Any]) -> Dict[str, Any]:
return {
"root_frame": _summary_root_frame(route, present),
"divisional_frame": _summary_divisional_frame(route, present),
"visibility_frame": _summary_visibility_frame(route, present),
"karaka_frame": _summary_karaka_frame(route, present),
"timing_frame": _summary_timing_frame(route, present),
"modifier_frame": _summary_modifier_frame(route, present),
"conflict_frame": {
"conflicts": strict.get("conflicts") or [],
"confidence_cap": strict.get("confidence_cap"),
},
}
```
Attach these fields inside each strict route result, reusing the current per-route `present` and `event_judgement`.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py -k "adjudication_stages or multi_reference_reading_summary" -q
```
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py
git commit -m "feat: add shared top-reader adjudication contract"
```
### Task 2: Promote selected bridge layers into the shared modifier frame
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_finance.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_relationship.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_functional_layer.py`
**Interfaces:**
- Consumes:
- `present["functional_benefic_malefic"]`
- `present["ashtakavarga_finance_support"]`
- `present["wealth_promise_strength"]`
- marriage route evidence already emitted into `present`
- `yogi_active` in finance `event_judgement.secondary_context`
- Produces:
- `multi_reference_reading_summary["modifier_frame"]`
- `adjudication_stages["manifestation"]["bridge_modifiers"]`
- [ ] **Step 1: Write the failing tests**
Add focused bridge promotion assertions:
```python
def test_finance_summary_modifier_frame_includes_yogi_and_ashtakavarga_only_as_modifiers() -> None:
strict = mcp_server._collect_strict_evidence("finance", modules)
modifier = strict["multi_reference_reading_summary"]["modifier_frame"]
assert "functional_benefic_malefic" in modifier
assert modifier["ashtakavarga_finance_support"]["source"] == "ashtakavarga_house_scores_bridge_v1"
assert modifier["yogi_support"]["role"] == "modifier_only"
```
```python
def test_relationship_summary_modifier_frame_surfaces_label_lift_related_modifiers() -> None:
strict = mcp_server._collect_strict_evidence("relationship", modules)
modifier = strict["multi_reference_reading_summary"]["modifier_frame"]
assert "functional_benefic_malefic" in modifier
assert "manifestation_split" in modifier
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_finance.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_functional_layer.py -k "modifier_frame or yogi or manifestation_split" -q
```
Expected: FAIL with missing modifier-frame keys.
- [ ] **Step 3: Write the minimal implementation**
Extend the summary helpers in `mcp_server.py`:
```python
def _summary_modifier_frame(route: str, present: Dict[str, Any]) -> Dict[str, Any]:
frame = {
"functional_benefic_malefic": present.get("functional_benefic_malefic"),
"shadbala": present.get("shadbala"),
"argala_support": present.get("argala_support"),
}
if route == "finance":
frame["ashtakavarga_finance_support"] = present.get("ashtakavarga_finance_support")
frame["yogi_support"] = {
"role": "modifier_only",
"value": present.get("wealth_promise_strength"),
}
if route == "relationship":
frame["manifestation_split"] = {
"role": "modifier_only",
"signals": [
"relationship_formation",
"legal_marriage",
"public_formalization",
],
}
return frame
```
Do not create new calculators here; only repackage current evidence and known route-specific bridge metadata.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_finance.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_functional_layer.py -k "modifier_frame or yogi or manifestation_split" -q
```
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py tests/test_mcp_strict_workflow_finance.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_functional_layer.py
git commit -m "feat: promote selected bridge layers into shared modifiers"
```
### Task 3: Compact and expose the new contract through `jyotish_engine.py`
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_official_full_snapshot.py`
**Interfaces:**
- Consumes:
- strict workflow contracts embedded in `modules`
- Produces:
- `_compact_strict_workflow_contract(strict: Dict[str, Any]) -> Dict[str, Any]`
- `ai_prompt_pack["evidence_snapshot"]["strict_workflow_contracts"][route]["adjudication_stages"]`
- `ai_prompt_pack["evidence_snapshot"]["strict_workflow_contracts"][route]["multi_reference_reading_summary"]`
- [ ] **Step 1: Write the failing tests**
Add prompt-pack expectations:
```python
def test_full_reading_prompt_pack_carries_adjudication_stages_and_multi_reference_summary() -> None:
result = run_full_reading(...)
strict = result["ai_prompt_pack"]["evidence_snapshot"]["strict_workflow_contracts"]["career"]
assert "adjudication_stages" in strict
assert "multi_reference_reading_summary" in strict
assert "modifier_frame" in strict["multi_reference_reading_summary"]
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py tests/test_vedastro_official_full_snapshot.py -k "adjudication_stages or multi_reference_reading_summary" -q
```
Expected: FAIL with missing keys in compact strict workflow contract or evidence snapshot.
- [ ] **Step 3: Write the minimal implementation**
Extend the strict contract compactor in `scripts/jyotish_engine.py`:
```python
def _compact_strict_workflow_contract(strict):
return {
"confidence_cap": strict.get("confidence_cap"),
"blocked": strict.get("blocked"),
"blocked_items": strict.get("blocked_items") or [],
"conflicts": strict.get("conflicts") or [],
"official_primary_evidence": strict.get("official_primary_evidence") or {},
"local_supplemental_evidence": strict.get("local_supplemental_evidence") or {},
"adjudication_stages": strict.get("adjudication_stages") or {},
"multi_reference_reading_summary": strict.get("multi_reference_reading_summary") or {},
"verdict": strict.get("verdict"),
"dominant_label": strict.get("dominant_label"),
"main_conflicts": strict.get("main_conflicts") or [],
}
```
Also ensure the prompt-pack evidence snapshot reuses this compacted form instead of recomputing anything heavy.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_cli_smoke.py tests/test_vedastro_official_full_snapshot.py -k "adjudication_stages or multi_reference_reading_summary" -q
```
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/jyotish_engine.py tests/test_cli_smoke.py tests/test_vedastro_official_full_snapshot.py
git commit -m "feat: expose top-reader contract in prompt pack"
```
### Task 4: Surface the reshaped contract in API outputs with no extra heavy recompute
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_historical_event_backtest.py`
**Interfaces:**
- Consumes:
- prompt-pack `evidence_snapshot`
- existing consultation workflow contract
- strict workflow contract summary
- Produces:
- consultation/high-rigor API outputs that include:
- `adjudication_stages`
- `multi_reference_reading_summary`
- `verdict`
- `dominant_label`
- `main_conflicts`
- [ ] **Step 1: Write the failing tests**
Add API-level shape assertions:
```python
def test_consultation_workflow_passes_through_top_reader_contract(monkeypatch) -> None:
result = handler._compute_consultation_workflow(payload)
guided = result["guided_topics"][0]
assert "adjudication_stages" in guided
assert "multi_reference_reading_summary" in guided
```
```python
def test_high_rigor_summary_passes_through_multi_reference_summary(monkeypatch) -> None:
summary = handler._high_rigor_vedastro_official_summary(prompt_official, range_scan, range_metadata)
assert "multi_reference_reading_summary" in summary
```
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py tests/test_historical_event_backtest.py -k "top_reader_contract or multi_reference_reading_summary" -q
```
Expected: FAIL with missing API passthrough keys.
- [ ] **Step 3: Write the minimal implementation**
Update `scripts/jyotish_api_server.py` to reuse existing prompt-pack or strict contract nodes:
```python
strict_contract = prompt_official.get("strict_workflow_contracts", {}).get(route_key, {})
summary["adjudication_stages"] = strict_contract.get("adjudication_stages") or {}
summary["multi_reference_reading_summary"] = strict_contract.get("multi_reference_reading_summary") or {}
summary["verdict"] = strict_contract.get("verdict")
summary["dominant_label"] = strict_contract.get("dominant_label")
summary["main_conflicts"] = strict_contract.get("main_conflicts") or strict_contract.get("conflicts") or []
```
Where guided topic objects are built, attach the same already-computed contract by reference or compact copy; do not call full-reading again.
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_api_server_security.py tests/test_historical_event_backtest.py -k "top_reader_contract or multi_reference_reading_summary" -q
```
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/jyotish_api_server.py tests/test_api_server_security.py tests/test_historical_event_backtest.py
git commit -m "feat: surface top-reader adjudication contract in api outputs"
```
### Task 5: Keep the frontend and user-facing surfaces simple while consuming the richer contract
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/ai-chat.js`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
**Interfaces:**
- Consumes:
- consultation workflow output
- prompt-pack evidence snapshot
- compact strict contract
- Produces:
- visible simple user summaries
- AI chat context that includes the new top-reader contract
- [ ] **Step 1: Write the failing tests**
Add frontend token tests:
```python
def test_ai_chat_and_complete_reading_surface_top_reader_contract_tokens() -> None:
main = read_main_js()
ai_chat = read_ai_chat_js()
assert "multi_reference_reading_summary" in main
assert "adjudication_stages" in main
assert "multi_reference_reading_summary" in ai_chat
assert "adjudication_stages" in ai_chat
```
Also add one test that the UI still prefers compact summaries rather than dumping raw full evidence.
- [ ] **Step 2: Run tests to verify they fail**
Run:
```bash
python3 -m pytest tests/test_frontend_productization.py -k "top_reader_contract or multi_reference_reading_summary or adjudication_stages" -q
```
Expected: FAIL with missing frontend references to the new contract.
- [ ] **Step 3: Write the minimal implementation**
Update frontend readers to expose only compact user-facing summaries and AI context:
```javascript
const topReaderContract = chartData?.ai_prompt_pack?.evidence_snapshot?.strict_workflow_contracts?.[routeKey] || {};
const adjudicationStages = topReaderContract.adjudication_stages || {};
const multiReferenceSummary = topReaderContract.multi_reference_reading_summary || {};
```
Use these to:
- show a compact “how this conclusion was formed” section
- append structured context to AI chat
- avoid rendering the full raw evidence tree unless already needed in an audit panel
- [ ] **Step 4: Run tests to verify they pass**
Run:
```bash
python3 -m pytest tests/test_frontend_productization.py -k "top_reader_contract or multi_reference_reading_summary or adjudication_stages" -q
```
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add jyotish-app/main.js jyotish-app/ai-chat.js tests/test_frontend_productization.py
git commit -m "feat: consume top-reader adjudication contract in frontend"
```
### Task 6: Run the focused regression bundle and then the broader verification pass
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/progress.md`
**Interfaces:**
- Consumes:
- all modified code from Tasks 1-5
- Produces:
- recorded verification summary in `progress.md`
- [ ] **Step 1: Run the focused contract regressions**
Run:
```bash
python3 -m pytest \
tests/test_mcp_strict_workflow_career.py \
tests/test_mcp_strict_workflow_relationship.py \
tests/test_mcp_strict_workflow_finance.py \
tests/test_mcp_strict_workflow_functional_layer.py \
tests/test_cli_smoke.py \
tests/test_vedastro_official_full_snapshot.py \
tests/test_api_server_security.py \
tests/test_historical_event_backtest.py \
tests/test_frontend_productization.py \
-k "adjudication_stages or multi_reference_reading_summary or top_reader_contract or modifier_frame" -q
```
Expected: PASS
- [ ] **Step 2: Run the broader targeted verification**
Run:
```bash
python3 -m pytest tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py tests/test_api_server_security.py tests/test_cli_smoke.py tests/test_frontend_productization.py -q
```
Expected: PASS
- [ ] **Step 3: Update `progress.md` with the landed contract and verification notes**
Add an entry similar to:
```markdown
- 2026-06-30 Top-reader adjudication contract landed:
- shared `promise -> activation -> manifestation -> label`
- `multi_reference_reading_summary`
- bridge promotion kept modifier-only
- prompt-pack/API/frontend all consume the same compact contract
- focused and targeted regressions passed
```
- [ ] **Step 4: Run diff hygiene checks**
Run:
```bash
git diff --check
```
Expected: no output
- [ ] **Step 5: Commit**
```bash
git add progress.md
git commit -m "docs: record top-reader adjudication verification"
```
## Self-Review
### Spec coverage
- Shared four-stage skeleton: covered by Task 1
- Multi-reference summary: covered by Tasks 1, 3, 4, and 5
- Selected bridge promotion: covered by Task 2
- Prompt-pack/API/frontend consumption: covered by Tasks 3, 4, and 5
- Compute minimization and reuse constraints: enforced in every task through existing-contract reuse and no new engine work
### Placeholder scan
- No `TODO`, `TBD`, or “implement later” placeholders remain.
- Each task includes exact files, exact commands, and exact expected behavior.
### Type consistency
- `adjudication_stages` and `multi_reference_reading_summary` are introduced first in `mcp_server.py`, then compacted in `jyotish_engine.py`, then consumed in `jyotish_api_server.py` and frontend.
- `verdict`, `dominant_label`, and `main_conflicts` are named consistently across all tasks.
## Execution Handoff
**Plan complete and saved to `docs/superpowers/plans/2026-06-30-top-reader-adjudication.md`. Two execution options:**
**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
**Which approach?**
@@ -0,0 +1,405 @@
# VedAstro Daily Window Priority 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:** Promote official VedAstro range-scan output into reusable day-window evidence that strict career/marriage/wealth workflows can consume directly.
**Architecture:** Reuse the existing `SearchEvents` adapter, orchestration, and strict workflow chain. Add adapter-level day-window aggregation, propagate it through `vedastro_evidence_orchestrator`, expose it inside `external_activation`, and surface it through `life_event_graph_v1` and `full-reading` outputs.
**Tech Stack:** Python 3, existing Jyotish engine, existing VedAstro service adapter, pytest.
## Global Constraints
- Reuse existing `scripts/vedastro_service_adapter.py`, `scripts/vedastro_evidence_orchestrator.py`, `mcp_server.py`, and `scripts/jyotish_engine.py`; do not create a parallel official-event stack.
- Keep official VedAstro as primary raw evidence and local Jyotish modules as adjudication/promise/timing cross-check layers.
- Do not bypass D9/D10/D2/UL/A10/Narayana/Functional Benefic-Malefic gates.
- Preserve existing response cache and free-tier queue logic.
- Keep changes focused on daily-window extraction and propagation, not a month-grid product.
---
### Task 1: Add failing adapter tests for daily-window aggregation
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_range_scan_replay.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_range_scan_replay.py`
**Interfaces:**
- Consumes: `scripts.vedastro_service_adapter._normalize_range_scan_success(payload, endpoint, request_preview, attempt_count=1, retry_error_codes=None) -> dict`
- Produces: adapter results with `daily_windows: list[dict]` and `top_daily_window: dict | None`
- [ ] **Step 1: Write the failing test**
```python
def test_range_scan_builds_ranked_daily_windows_from_same_day_events() -> None:
payload = {
"Status": "Pass",
"Payload": [
{
"Name": "GocharJupiterAspect10th",
"Description": "Career support transit.",
"StartTime": "2026-07-18",
"EndTime": "2026-07-18",
"EventTags": ["Travel", "General"],
},
{
"Name": "CareerExpansionWindow",
"Description": "Strong career expansion signal.",
"StartTime": "2026-07-18",
"EndTime": "2026-07-18",
"EventTags": ["career", "transit"],
},
{
"Name": "GocharJupiterAspect10th",
"Description": "Career support transit.",
"StartTime": "2026-07-26",
"EndTime": "2026-07-26",
"EventTags": ["Travel", "General"],
},
],
}
report = vedastro_service_adapter._normalize_range_scan_success( # noqa: SLF001
payload,
"https://api.vedastro.org/api",
_request_preview("career"),
)
assert report["daily_windows"][0]["date"] == "2026-07-18"
assert report["daily_windows"][0]["event_count"] == 2
assert report["top_daily_window"]["date"] == "2026-07-18"
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_vedastro_range_scan_replay.py::test_range_scan_builds_ranked_daily_windows_from_same_day_events -q`
Expected: FAIL because `daily_windows` / `top_daily_window` are missing.
- [ ] **Step 3: Write minimal implementation**
Add a helper in `scripts/vedastro_service_adapter.py`:
```python
def _build_daily_windows(domain: str, evidence_ledger: list[dict[str, Any]]) -> tuple[list[dict[str, Any]], dict[str, Any] | None]:
...
```
and call it from `_normalize_range_scan_success(...)`.
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_vedastro_range_scan_replay.py::test_range_scan_builds_ranked_daily_windows_from_same_day_events -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add tests/test_vedastro_range_scan_replay.py scripts/vedastro_service_adapter.py
git commit -m "feat: add vedastro daily window aggregation"
```
### Task 2: Propagate daily windows through orchestrator
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/vedastro_evidence_orchestrator.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_evidence_orchestrator.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_vedastro_evidence_orchestrator.py`
**Interfaces:**
- Consumes: `run_range_scan_for_case(...) -> dict` with `daily_windows` and `top_daily_window`
- Produces: orchestrator result keys:
- `daily_windows_by_domain: dict[str, list[dict]]`
- `top_daily_window_by_domain: dict[str, dict]`
- [ ] **Step 1: Write the failing test**
```python
def test_vedastro_orchestrator_surfaces_daily_windows_by_domain(monkeypatch) -> None:
from scripts import vedastro_evidence_orchestrator as orchestrator
monkeypatch.setattr(orchestrator, "run_official_full_snapshot_for_case", lambda *args, **kwargs: {"status": "ok", "source_metadata": {}})
monkeypatch.setattr(
orchestrator,
"run_range_scan_for_case",
lambda *args, **kwargs: {
"status": "ok",
"available": True,
"event_count": 2,
"daily_windows": [{"date": "2026-07-18", "domain": "career", "score": 5, "event_count": 2}],
"top_daily_window": {"date": "2026-07-18", "domain": "career", "score": 5, "event_count": 2},
"evidence_ledger": [],
},
)
result = orchestrator.orchestrate_vedastro_evidence(
{"year": REDACTED_YEAR, "month": 4, "day": 17, "hour": 14, "minute": 49, "lat": 36.42, "lon": 114.2, "tz": 8},
route="career",
reference_date="2026-06-30",
)
assert result["daily_windows_by_domain"]["career"][0]["date"] == "2026-07-18"
assert result["top_daily_window_by_domain"]["career"]["score"] == 5
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_vedastro_evidence_orchestrator.py::test_vedastro_orchestrator_surfaces_daily_windows_by_domain -q`
Expected: FAIL because orchestrator does not yet expose these keys.
- [ ] **Step 3: Write minimal implementation**
In `scripts/vedastro_evidence_orchestrator.py`, collect from each domain report:
```python
daily_windows_by_domain[domain] = report.get("daily_windows") or []
if isinstance(report.get("top_daily_window"), dict):
top_daily_window_by_domain[domain] = report["top_daily_window"]
```
and return them in the final payload.
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_vedastro_evidence_orchestrator.py::test_vedastro_orchestrator_surfaces_daily_windows_by_domain -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/vedastro_evidence_orchestrator.py tests/test_vedastro_evidence_orchestrator.py
git commit -m "feat: propagate vedastro daily windows through orchestrator"
```
### Task 3: Promote daily windows into strict workflow external activation
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_career.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_relationship.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_mcp_strict_workflow_finance.py`
- Test: same files
**Interfaces:**
- Consumes: `modules.vedastro_range_scan_result.daily_windows` / `top_daily_window`
- Produces: `present_evidence.external_activation.daily_windows` and `present_evidence.external_activation.top_daily_window`
- [ ] **Step 1: Write the failing test**
```python
def test_relationship_external_activation_exposes_top_daily_window() -> None:
result = {
"modules": {
"vedastro_range_scan_result": {
"backend": "vedastro_service_adapter_candidate",
"status": "ok",
"operation": "range_scan",
"domain": "marriage",
"evidence_ledger": [],
"daily_windows": [{"date": "2026-08-02", "domain": "marriage", "score": 5, "event_count": 2}],
"top_daily_window": {"date": "2026-08-02", "domain": "marriage", "score": 5, "event_count": 2},
"source_metadata": {},
},
}
}
strict = _collect_strict_evidence("relationship", result)
external = strict["present_evidence"]["external_activation"]
assert external["top_daily_window"]["date"] == "2026-08-02"
assert external["daily_windows"][0]["score"] == 5
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_mcp_strict_workflow_relationship.py::test_relationship_external_activation_exposes_top_daily_window -q`
Expected: FAIL because `external_activation` does not yet carry day-window fields.
- [ ] **Step 3: Write minimal implementation**
Extend `_derive_external_activation_support(...)` in `mcp_server.py` to read:
```python
daily_windows = adapter_result.get("daily_windows") or []
top_daily_window = adapter_result.get("top_daily_window")
```
and include them in the returned dict.
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_mcp_strict_workflow_relationship.py::test_relationship_external_activation_exposes_top_daily_window -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_finance.py
git commit -m "feat: expose vedastro daily windows in strict workflow"
```
### Task 4: Surface official day windows in life event graph
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_life_event_graph_v1.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_life_event_graph_v1.py`
**Interfaces:**
- Consumes: `present_evidence.external_activation.daily_windows`
- Produces: `life_event_graph.event_nodes[]` entries with `kind: "official_day_window"`
- [ ] **Step 1: Write the failing test**
```python
def test_life_event_graph_surfaces_ranked_official_day_window_nodes() -> None:
strict = {
"event_judgement": {"event_family": "career", "verdict": "moderate_probability_window", "score": 74},
"present_evidence": {
"external_activation": {
"level": "moderate",
"source": "vedastro_service_adapter_candidate",
"daily_windows": [
{
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "medium_high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
}
],
}
},
"confidence_cap": "medium",
"missing_evidence": [],
"blocked": False,
}
graph = _build_life_event_graph("career", strict)
assert any(node["kind"] == "official_day_window" and node["date"] == "2026-07-18" for node in graph["event_nodes"])
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_life_event_graph_v1.py::test_life_event_graph_surfaces_ranked_official_day_window_nodes -q`
Expected: FAIL because no `official_day_window` nodes exist yet.
- [ ] **Step 3: Write minimal implementation**
In `_build_life_event_graph(...)`, after `external_window` nodes, append:
```python
{
"kind": "official_day_window",
"date": window.get("date"),
"domain": window.get("domain"),
"score": window.get("score"),
"confidence": window.get("confidence"),
"event_count": window.get("event_count"),
"top_signal_label": window.get("top_signal_label"),
"signal_families": window.get("signal_families") or [],
"event_ids": window.get("event_ids") or [],
"source": external_activation.get("source"),
}
```
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_life_event_graph_v1.py::test_life_event_graph_surfaces_ranked_official_day_window_nodes -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add mcp_server.py tests/test_life_event_graph_v1.py
git commit -m "feat: show official vedastro day windows in life event graph"
```
### Task 5: Attach daily-window expansion to full-reading and verify output contract
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
**Interfaces:**
- Consumes: existing `modules.vedastro_range_scan_result`
- Produces: `modules.vedastro_range_scan_result.daily_windows`, `summary.guided_topics`, and strict contracts that can reach day-window evidence through downstream modules
- [ ] **Step 1: Write the failing test**
```python
def test_full_reading_preserves_official_daily_window_fields_in_range_scan_result() -> None:
result = run_engine(
"full-reading",
"--year", "REDACTED_YEAR",
"--month", "4",
"--day", "17",
"--hour", "14",
"--minute", "49",
"--lat", "36.42",
"--lon", "114.2",
"--tz", "8",
"--today", "2026-06-30",
"--transit-date", "2026-06-30",
)
vedastro = result["modules"]["vedastro_range_scan_result"]
assert "daily_windows" in vedastro
assert "top_daily_window" in vedastro
```
- [ ] **Step 2: Run test to verify it fails**
Run: `python3 -m pytest tests/test_cli_smoke.py::test_full_reading_preserves_official_daily_window_fields_in_range_scan_result -q`
Expected: FAIL if full-reading path still attaches only overview fields.
- [ ] **Step 3: Write minimal implementation**
Ensure `scripts/jyotish_engine.py` keeps adapter-derived daily-window fields intact when composing:
- `modules.vedastro_range_scan_result`
- `ai_prompt_pack.evidence_snapshot.vedastro_overview`
No reformatting layer should drop them.
- [ ] **Step 4: Run test to verify it passes**
Run: `python3 -m pytest tests/test_cli_smoke.py::test_full_reading_preserves_official_daily_window_fields_in_range_scan_result -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add scripts/jyotish_engine.py tests/test_cli_smoke.py
git commit -m "feat: preserve vedastro daily window evidence in full reading"
```
## Self-Review
- Spec coverage: adapter aggregation, orchestrator propagation, strict workflow promotion, graph exposure, full-reading preservation are all covered.
- Placeholder scan: no TBD/TODO placeholders remain.
- Type consistency: `daily_windows` is always `list[dict]`; `top_daily_window` is always `dict | None`; `official_day_window` is the graph node name across tasks.
## Execution Handoff
Plan complete and saved to `docs/superpowers/plans/2026-06-30-vedastro-daily-window-priority.md`. Two execution options:
**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints
**Which approach?**
@@ -0,0 +1,376 @@
# VedAstro Official Hard-Override 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:** Make `relationship`, `career`, and `wealth` default workflows enforce `VedAstro official -> local supplemental -> local fallback`, with honest `blocked`, `conflicts`, and `confidence_cap` output.
**Architecture:** Reuse the existing `vedastro_evidence_orchestrator`, `vedastro_priority`, `mcp_server` strict evidence collectors, `jyotish_engine`, and `jyotish_api_server` rather than creating a second workflow. Promote official-first evidence into a shared strict contract, then expose that same contract through API and report surfaces.
**Tech Stack:** Python standard library, existing MCP strict workflow, pytest, existing API/frontend static tests.
## Global Constraints
- Official VedAstro data is primary when available.
- Local modules may supplement, cross-check, or fallback, but may not silently overwrite official values.
- Timing and event outputs must keep `Vimshottari + Narayana` dual-track rigor.
- Relationship must keep `D9 + UL`.
- Career must keep `D10 + A10`.
- Wealth must keep `D2 / D11`.
- Functional Benefic/Malefic must remain explicit in high-rigor outputs.
- New behavior must be introduced with failing tests first.
- Do not create a parallel orchestration stack when existing files can be extended safely.
---
### Task 1: Add Red Tests For The Shared Official-First Strict Contract
**Files:**
- Modify: `tests/test_mcp_strict_workflow_relationship.py`
- Modify: `tests/test_mcp_strict_workflow_career.py`
- Modify: `tests/test_mcp_strict_workflow_finance.py`
- Modify: `tests/test_historical_event_backtest.py`
**Interfaces:**
- Produces strict fields:
- `official_primary_evidence: dict`
- `local_supplemental_evidence: dict`
- `fallback_used: list[str]`
- `blocked_items: list[str]`
- `conflicts: list[dict]`
- `confidence_cap: str`
- [ ] **Step 1: Write the failing relationship test**
```python
def test_relationship_strict_contract_exposes_official_primary_and_local_supplemental_layers() -> None:
result = _base_relationship_result()
result["modules"]["source_priority"] = {"mode": "vedastro_official_primary"}
result["modules"]["vedastro_official_full_snapshot"] = {
"status": "partial",
"available": True,
"official_chart": {"planets": {"Sun": {}}, "ascendant": {"sign": "Leo"}},
"section_statuses": {"chart_core": "ok", "dasha_all": "ok", "events_overview": "partial"},
}
strict = _collect_strict_evidence("relationship", result)
assert strict["official_primary_evidence"]["chart_core"]["status"] == "ok"
assert strict["local_supplemental_evidence"]["upapada_lagna"]["role"] == "required_local_supplement"
assert isinstance(strict["blocked_items"], list)
assert isinstance(strict["fallback_used"], list)
assert isinstance(strict["conflicts"], list)
```
- [ ] **Step 2: Write the failing career and finance tests**
```python
def test_career_strict_contract_marks_a10_as_local_supplement_to_official_primary() -> None:
result = _base_career_result()
result["modules"]["source_priority"] = {"mode": "vedastro_official_primary"}
result["modules"]["vedastro_official_full_snapshot"] = {
"status": "partial",
"available": True,
"official_chart": {"planets": {"Sun": {}}, "ascendant": {"sign": "Leo"}},
"section_statuses": {"chart_core": "ok", "dasha_all": "ok"},
}
strict = _collect_strict_evidence("career", result)
assert strict["official_primary_evidence"]["dasha"]["status"] == "ok"
assert strict["local_supplemental_evidence"]["a10_karma_pada"]["role"] == "required_local_supplement"
def test_finance_strict_contract_surfaces_official_block_and_local_fallback_usage() -> None:
result = {"modules": {"source_priority": {"mode": "local_fallback_official_blocked"}}}
strict = _collect_strict_evidence("finance", result)
assert "official_primary_chart_blocked" in strict["blocked_items"]
assert isinstance(strict["fallback_used"], list)
```
- [ ] **Step 3: Write the failing backtest test**
```python
def test_backtest_carries_conflicts_and_blocked_items_from_strict_contract(monkeypatch) -> None:
def fake_strict_workflow(**kwargs):
packet = _strict_packet("career", verdict="high_probability_window", dominant_label="career_status", score=84)
packet["strict_workflow"]["blocked_items"] = ["official_event_radar_partial"]
packet["strict_workflow"]["conflicts"] = [{"type": "official_local_dasha_conflict"}]
return packet
monkeypatch.setattr(backtest.mcp_server, "strict_workflow", fake_strict_workflow)
report = backtest.build_report(_payload([{"id": "career_turn_2019", "date": "2019-12-15", "domain": "career"}]))
assert report["events"][0]["evidence"]["blocked_items"] == ["official_event_radar_partial"]
assert report["events"][0]["evidence"]["conflicts"] == [{"type": "official_local_dasha_conflict"}]
```
- [ ] **Step 4: Run tests to verify they fail**
Run:
`python3 -m pytest tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_finance.py tests/test_historical_event_backtest.py -k "strict_contract or conflicts_and_blocked_items" -q`
Expected: FAIL because the strict contract fields do not yet exist.
### Task 2: Implement Shared Official-First Strict Contract In MCP Strict Workflow
**Files:**
- Modify: `mcp_server.py`
- Test: `tests/test_mcp_strict_workflow_relationship.py`
- Test: `tests/test_mcp_strict_workflow_career.py`
- Test: `tests/test_mcp_strict_workflow_finance.py`
**Interfaces:**
- Produces:
- `_build_official_primary_evidence(route: str, modules: dict, present: dict) -> dict`
- `_build_local_supplemental_evidence(route: str, present: dict) -> dict`
- `_build_fallback_and_blocked(route: str, present: dict, missing: list[str]) -> tuple[list[str], list[str]]`
- `_build_conflicts(route: str, present: dict, missing: list[str]) -> list[dict]`
- [ ] **Step 1: Implement helper skeletons in `mcp_server.py`**
```python
def _build_official_primary_evidence(route: str, modules: Dict[str, Any], present: Dict[str, Any]) -> Dict[str, Any]:
snapshot = present.get("vedastro_official_snapshot") or {}
section_statuses = snapshot.get("section_statuses") or {}
base = {
"chart_core": {"source": "vedastro_official", "status": section_statuses.get("chart_core", "blocked")},
"dasha": {"source": "vedastro_official", "status": section_statuses.get("dasha_all", "blocked")},
"event_radar": {"source": "vedastro_official", "status": section_statuses.get("events_overview", "blocked")},
}
if route == "relationship":
base["d9"] = {"source": "vedastro_official", "status": section_statuses.get("varga_d9", "unknown")}
elif route == "career":
base["d10"] = {"source": "vedastro_official", "status": section_statuses.get("varga_d10", "unknown")}
elif route == "finance":
base["d2_d11"] = {"source": "vedastro_official", "status": section_statuses.get("varga_d2_d11", "unknown")}
return base
```
- [ ] **Step 2: Implement local supplemental mapping**
```python
def _build_local_supplemental_evidence(route: str, present: Dict[str, Any]) -> Dict[str, Any]:
if route == "relationship":
keys = ("upapada_lagna", "darakaraka", "narayana_current", "functional_benefic_malefic")
elif route == "career":
keys = ("a10_karma_pada", "narayana_current", "functional_benefic_malefic")
else:
keys = ("wealth_promise_strength", "narayana_current", "functional_benefic_malefic")
return {
key: {
"role": "required_local_supplement",
"present": bool(present.get(key)),
}
for key in keys
}
```
- [ ] **Step 3: Implement blocked, fallback, and conflict derivation**
```python
def _build_fallback_and_blocked(route: str, present: Dict[str, Any], missing: List[str]) -> tuple[List[str], List[str]]:
blocked_items: List[str] = []
fallback_used: List[str] = []
official = present.get("vedastro_official_snapshot") or {}
if official.get("level") != "primary":
blocked_items.append("official_primary_chart_blocked")
fallback_used.append("local_chart_fallback")
if "external_activation" in present and (present.get("external_activation") or {}).get("level") == "missing_required_external_radar":
blocked_items.append("official_event_radar_partial")
for key in missing:
blocked_items.append(f"missing_required_{key}")
return fallback_used, blocked_items
def _build_conflicts(route: str, present: Dict[str, Any], missing: List[str]) -> List[Dict[str, Any]]:
conflicts: List[Dict[str, Any]] = []
dignity = present.get("dignity_guardrail") or {}
if dignity.get("status") == "conflict":
conflicts.append({
"type": "official_local_divisional_conflict",
"primary_source": "vedastro_official",
"supplemental_source": "local_module",
"impact": "interpretation",
"resolution": "keep_official_primary_and_downgrade_confidence",
"details": {"dignity_guardrail": dignity},
})
return conflicts
```
- [ ] **Step 4: Attach the new contract fields to each strict route**
```python
strict["official_primary_evidence"] = _build_official_primary_evidence(route, modules, present)
strict["local_supplemental_evidence"] = _build_local_supplemental_evidence(route, present)
strict["fallback_used"], strict["blocked_items"] = _build_fallback_and_blocked(route, present, missing)
strict["conflicts"] = _build_conflicts(route, present, missing)
```
- [ ] **Step 5: Run tests to verify they pass**
Run:
`python3 -m pytest tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_finance.py tests/test_historical_event_backtest.py -k "strict_contract or conflicts_and_blocked_items" -q`
Expected: PASS for the new contract tests.
### Task 3: Promote Official Snapshot Section Status Into Shared Orchestration Metadata
**Files:**
- Modify: `scripts/vedastro_evidence_orchestrator.py`
- Modify: `scripts/vedastro_priority.py`
- Modify: `tests/test_vedastro_evidence_orchestrator.py`
- Modify: `tests/test_vedastro_official_full_snapshot.py`
**Interfaces:**
- Produces:
- `source_metadata.official_section_statuses`
- `source_metadata.theme_requirements`
- `official_snapshot_evidence(...).section_statuses`
- [ ] **Step 1: Write the failing orchestrator test**
```python
def test_vedastro_orchestrator_surfaces_official_section_statuses_and_theme_requirements(monkeypatch) -> None:
from scripts import vedastro_evidence_orchestrator as orchestrator
monkeypatch.setattr(orchestrator, "run_official_full_snapshot_for_case", lambda *args, **kwargs: {
"status": "partial",
"available": True,
"official_chart": {"planets": {"Sun": {}}, "ascendant": {"sign": "Leo"}},
"section_statuses": {"chart_core": "ok", "dasha_all": "ok", "events_overview": "partial"},
"source_metadata": {},
})
monkeypatch.setattr(orchestrator, "run_range_scan_for_case", lambda *args, **kwargs: {
"status": "ok",
"available": True,
"event_count": 1,
"evidence_ledger": [],
})
result = orchestrator.orchestrate_vedastro_evidence({"year": REDACTED_YEAR, "month": 4, "day": 17, "hour": 14, "minute": 49, "lat": 36.42, "lon": 114.2, "tz": 8}, route="relationship", reference_date="2026-06-29")
assert result["source_metadata"]["official_section_statuses"]["dasha_all"] == "ok"
assert result["source_metadata"]["theme_requirements"]["route"] == "relationship"
```
- [ ] **Step 2: Run test to verify it fails**
Run:
`python3 -m pytest tests/test_vedastro_evidence_orchestrator.py -k official_section_statuses -q`
Expected: FAIL because the metadata keys are missing.
- [ ] **Step 3: Implement minimal metadata propagation**
```python
official_section_statuses = official_full_snapshot.get("section_statuses") if isinstance(official_full_snapshot, dict) else {}
theme_requirements = {
"route": route,
"domains": domains,
"requires_dual_dasha": True,
}
```
Add both into the orchestrator `source_metadata` and keep `official_snapshot_evidence()` returning `section_statuses`.
- [ ] **Step 4: Run tests to verify they pass**
Run:
`python3 -m pytest tests/test_vedastro_evidence_orchestrator.py tests/test_vedastro_official_full_snapshot.py -k "official_section_statuses or official_full_snapshot" -q`
Expected: PASS for the new metadata contract.
### Task 4: Surface The Shared Contract Through API And Report Payloads
**Files:**
- Modify: `scripts/jyotish_api_server.py`
- Modify: `scripts/jyotish_engine.py`
- Modify: `tests/test_api_server_security.py`
**Interfaces:**
- Produces:
- API theme outputs that include `official_primary_evidence`, `local_supplemental_evidence`, `fallback_used`, `blocked_items`, `conflicts`
- prompt/report payloads that carry the same structure
- [ ] **Step 1: Write the failing API test**
```python
def test_high_rigor_workflow_plan_only_exposes_official_hard_override_contract() -> None:
handler = _handler()
result = handler._high_rigor_workflow_plan_only(
{"year": REDACTED_YEAR, "month": 4, "day": 17, "hour": 14, "minute": 49, "lat": 36.42, "lon": 114.2, "tz": 8},
["career", "marriage", "wealth"],
[],
)
assert result["source_priority"]["mode"] == "vedastro_official_snapshot_first"
assert result["execution_plan"][-1] == "return_official_primary_supplemental_fallback_conflict_contract"
```
- [ ] **Step 2: Run test to verify it fails**
Run:
`python3 -m pytest tests/test_api_server_security.py -k hard_override_contract -q`
Expected: FAIL because the execution plan and API contract are not yet updated.
- [ ] **Step 3: Implement API/report passthrough**
```python
result["contract"] = {
"official_primary_evidence": strict.get("official_primary_evidence") if isinstance(strict, dict) else {},
"local_supplemental_evidence": strict.get("local_supplemental_evidence") if isinstance(strict, dict) else {},
"fallback_used": strict.get("fallback_used") if isinstance(strict, dict) else [],
"blocked_items": strict.get("blocked_items") if isinstance(strict, dict) else [],
"conflicts": strict.get("conflicts") if isinstance(strict, dict) else [],
}
```
Update prompt/report payload builders to include the same keys when strict evidence exists.
- [ ] **Step 4: Run test to verify it passes**
Run:
`python3 -m pytest tests/test_api_server_security.py -k hard_override_contract -q`
Expected: PASS.
### Task 5: Verify The Closed Path And Update Project Logs
**Files:**
- Modify: `progress.md`
- Modify: `findings.md`
**Interfaces:**
- Produces:
- final verification log for the official hard-override path
- [ ] **Step 1: Run focused strict-workflow verification**
Run:
`python3 -m pytest tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_career.py tests/test_mcp_strict_workflow_finance.py tests/test_historical_event_backtest.py -q`
Expected: PASS.
- [ ] **Step 2: Run orchestration verification**
Run:
`python3 -m pytest tests/test_vedastro_evidence_orchestrator.py tests/test_vedastro_official_full_snapshot.py tests/test_vedastro_python_bridge.py -q`
Expected: PASS.
- [ ] **Step 3: Run API/static verification**
Run:
`python3 -m pytest tests/test_api_server_security.py tests/test_frontend_productization.py -k "vedastro or hard_override_contract or source_priority" -q`
Expected: PASS.
- [ ] **Step 4: Update project logs**
Record:
- files changed
- official-first contract now enforced where implemented
- remaining blocked boundaries
- exact verification commands and results
## Self-Review
- Spec coverage: official-first authority, shared contract, conflict honesty, blocked honesty, API/report propagation, and verification each map to a task.
- Placeholder scan: no TBD/TODO placeholders remain.
- Type consistency: the same contract keys are used across strict workflow, backtest, API, and report surfaces.
@@ -0,0 +1,42 @@
# Monthly Adjudication Structure 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:** Replace the old month-facing "opportunity / pressure" simplification with a route-aware monthly adjudication structure for career, relationship, and finance that reuses existing strict workflow evidence.
**Architecture:** Reuse `mcp_server.py` strict workflow evidence as the single source, derive a lightweight `monthly_adjudication_summary` from existing promise/activation/manifestation layers plus VedAstro official day signals, then surface it through prompt-pack, guided topics, AI chat context, and frontend cards. Keep `official_day_signal_summary` as a compatibility layer during the transition.
**Tech Stack:** Python, existing strict workflow builders, pytest, current guided-topic/frontend consumers.
## Global Constraints
- Reuse current strict workflow collectors before adding new collectors.
- Reuse current `event_judgement`, `adjudication_stages`, and `official_day_signal_summary` outputs.
- Do not add new heavy VedAstro calls for this task.
- Keep `Functional Benefic/Malefic`, relevant vargas, and `Vimshottari + Narayana` hard gates visible.
- Preserve `blocked`, `conflicts`, and `confidence_cap` honesty boundaries.
---
### Task 1: Define and test the new contract
- [ ] Add failing backend tests for `monthly_adjudication_summary` in career/relationship/finance strict workflow tests.
- [ ] Add failing guided-topic and frontend tests asserting the new field is carried and displayed.
### Task 2: Implement route-aware monthly adjudication
- [ ] Add helper builders in `/Users/wuyongnaren/Documents/印度占星/mcp_server.py`.
- [ ] Attach `monthly_adjudication_summary` to strict workflow contracts.
- [ ] Keep `official_day_signal_summary` unchanged for compatibility.
### Task 3: Surface the contract through consumers
- [ ] Add `monthly_adjudication_summary` to compact prompt-pack contracts in `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`.
- [ ] Add it to guided topics in `/Users/wuyongnaren/Documents/印度占星/scripts/guided_topic_discovery.py`.
- [ ] Expose it in `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js` and `/Users/wuyongnaren/Documents/印度占星/jyotish-app/ai-chat.js`.
### Task 4: Verify targeted regressions
- [ ] Run the strict workflow tests for career/relationship/finance.
- [ ] Run the guided-topic smoke test.
- [ ] Run the targeted frontend productization assertions.
@@ -0,0 +1,351 @@
# Unified Consultation Runtime Hardening 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:** Make the consultation runtime planner execute real reusable steps, make thematic report consume upstream unified contracts before deriving again, and collapse monthly adjudication / interpretation axes / strict audit gate into one shared adjudication object reused by guided topics, AI payloads, and frontend.
**Architecture:** Keep the existing repo surfaces, but stop letting each layer rebuild its own partial truth. The API workflow becomes the single executor of route steps, full-reading/chart strict outputs become preferred upstream evidence for thematic report, and a compact `strict_adjudication_bundle` becomes the canonical shared object carried through backend, prompt-pack, guided topics, and UI.
**Tech Stack:** Python 3, existing `jyotish_api_server.py`, `jyotish_engine.py`, `guided_topic_discovery.py`, vanilla frontend JS, pytest.
## Global Constraints
- Reuse existing repo code paths instead of inventing new parallel engines.
- Use TDD: failing tests first, then minimal implementation.
- Do not break current API payload compatibility where avoidable.
- Preserve VedAstro official-first boundary and fallback honesty.
- Keep edits scoped to the current workflow and display contract.
---
### Task 1: Lock the unified runtime and adjudication bundle contract in tests
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
**Interfaces:**
- Consumes: `_compute_consultation_workflow(...)`, `_compute_thematic_report(...)`, `build_guided_topics(...)`
- Produces: failing tests requiring `runtime_planner.executed_steps`, `strict_adjudication_bundle`, and thematic-report reuse behavior
- [ ] **Step 1: Write failing API workflow/runtime tests**
Add assertions to `tests/test_api_server_security.py` for:
```python
assert result["runtime_planner"]["executed_steps"] == [
"compute_chart",
"run_rectification_gate",
"run_historical_event_backtest",
"run_thematic_report",
]
assert result["runtime_planner"]["skipped_steps"] == []
assert result["thematic_report"]["mode"] == "upstream_contract_reuse"
assert result["thematic_report"]["evidence_source"]["source"] == "consultation_workflow_upstream_contract"
```
- [ ] **Step 2: Run targeted API workflow test to verify failure**
Run: `python3 -m pytest tests/test_api_server_security.py::test_consultation_workflow_reuses_chart_data_for_thematic_report_without_recursive_full_reading -q`
Expected: FAIL because `executed_steps` / `mode` / `evidence_source` are missing or different.
- [ ] **Step 3: Write failing shared adjudication bundle tests**
Add assertions in `tests/test_api_server_security.py` and `tests/test_cli_smoke.py`:
```python
bundle = career["strict_adjudication_bundle"]
assert bundle["monthly_adjudication_summary"]["primary_state"]["value"] == "推进"
assert bundle["strict_audit_gate"]["functional_benefic_malefic"]["gate"] == "hard"
assert bundle["interpretation_axes"][0]["axis"] == "角色定位"
```
and:
```python
assert topic["strict_adjudication_bundle"]["monthly_adjudication_summary"] == topic["monthly_adjudication_summary"]
assert topic["strict_adjudication_bundle"]["strict_audit_gate"] == topic["strict_audit_gate"]
```
- [ ] **Step 4: Run targeted adjudication tests to verify failure**
Run: `python3 -m pytest tests/test_api_server_security.py::test_thematic_report_interpretation_axes_are_strict_paragraphs_for_each_theme tests/test_cli_smoke.py::test_full_reading_guided_topics_can_carry_official_day_signal_summary -q`
Expected: FAIL because `strict_adjudication_bundle` is missing.
- [ ] **Step 5: Commit**
```bash
git add tests/test_api_server_security.py tests/test_cli_smoke.py tests/test_frontend_productization.py
git commit -m "test: lock unified consultation runtime contract"
```
### Task 2: Make runtime_planner a real executor and make thematic report prefer upstream unified contracts
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/unified_consultation_orchestrator.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
**Interfaces:**
- Consumes: `UnifiedConsultationOrchestrator.runtime_planner(...)`, `_compute_consultation_workflow(...)`
- Produces: planner with `executed_steps` / `skipped_steps`, thematic input payload with `strict_workflow_contracts`, and `_compute_thematic_report(...)` mode `upstream_contract_reuse`
- [ ] **Step 1: Add failing helper-level test coverage if needed**
If `tests/test_unified_consultation_orchestrator.py` lacks direct planner coverage, add:
```python
planner = orchestrator.runtime_planner(...)
assert planner["sync_steps"][0] == "compute_chart"
assert planner["reuse_contract"]["thematic_report"] == "thematic_report"
```
- [ ] **Step 2: Implement runtime executed/skipped step recording**
In `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`, refactor `_compute_consultation_workflow(...)` so it:
- initializes `executed_steps = []`, `skipped_steps = []`
- runs only steps listed in `runtime_planner["sync_steps"]`
- appends actual executed step names
- records skipped steps from the known runtime step set
- [ ] **Step 3: Pass upstream strict data into thematic report**
When building the thematic payload in `_compute_consultation_workflow(...)`, pass:
```python
"upstream_contract": {
"chart": chart_for_theme,
"strict_workflow_contracts": prompt_snapshot_contracts,
"guided_topics": chart_modules_guided_topics,
}
```
where `prompt_snapshot_contracts` comes from `chart["ai_prompt_pack"]["evidence_snapshot"]["strict_workflow_contracts"]` when available.
- [ ] **Step 4: Implement upstream-contract-first thematic reuse**
In `_compute_thematic_report(...)`, before calling `_derive_thematic_evidence(...)`, detect:
```python
upstream_contract = body.get("upstream_contract")
```
and if it contains usable `strict_workflow_contracts` or chart evidence, set:
- `mode = "upstream_contract_reuse"`
- `evidence_source["source"] = "consultation_workflow_upstream_contract"`
- reuse upstream evidence/contracts before local derivation fallback
- [ ] **Step 5: Run tests to verify green**
Run: `python3 -m pytest tests/test_api_server_security.py::test_consultation_workflow_reuses_chart_data_for_thematic_report_without_recursive_full_reading tests/test_api_server_security.py::test_thematic_report_handles_missing_dasa_convergence_without_crash tests/test_unified_consultation_orchestrator.py -q`
Expected: PASS
- [ ] **Step 6: Commit**
```bash
git add scripts/unified_consultation_orchestrator.py scripts/jyotish_api_server.py tests/test_api_server_security.py tests/test_unified_consultation_orchestrator.py
git commit -m "feat: execute unified consultation runtime steps"
```
### Task 3: Collapse monthly adjudication, audit gate, and axes into one canonical strict adjudication bundle
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/guided_topic_discovery.py`
- Modify: `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_api_server_security.py`
- Test: `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
**Interfaces:**
- Consumes: strict workflow contracts, `monthly_adjudication_summary`, `technique_audit_summary`, `interpretation_axes`
- Produces: `strict_adjudication_bundle` with:
- `monthly_adjudication_summary`
- `monthly_adjudication_summary_humanized`
- `strict_audit_gate`
- `interpretation_axes`
- `narrative_contract`
- [ ] **Step 1: Add bundle builder in API server**
Create a helper in `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_api_server.py` that returns:
```python
{
"theme": theme_name,
"monthly_adjudication_summary": monthly_frame,
"monthly_adjudication_summary_humanized": humanized,
"strict_audit_gate": report_payload.get("technique_audit_summary") or {},
"interpretation_axes": axes,
"narrative_contract": {...},
}
```
- [ ] **Step 2: Attach canonical bundle to thematic report payload**
In `_apply_monthly_adjudication_to_theme_report(...)`, set:
```python
report_payload["strict_adjudication_bundle"] = bundle
```
while keeping legacy top-level fields for compatibility.
- [ ] **Step 3: Make guided topics read only the bundle first**
In `/Users/wuyongnaren/Documents/印度占星/scripts/guided_topic_discovery.py`, replace separate contract lookups with:
```python
bundle = _as_dict(contract.get("strict_adjudication_bundle"))
```
and populate:
- `strict_audit_gate`
- `monthly_adjudication_summary`
- `official_day_signal_summary` fallback only if not in bundle
- [ ] **Step 4: Make prompt pack carry the bundle through**
In `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`, ensure each strict workflow contract already exported into `ai_prompt_pack.evidence_snapshot.strict_workflow_contracts` includes `strict_adjudication_bundle`.
- [ ] **Step 5: Run backend/shared contract tests**
Run: `python3 -m pytest tests/test_api_server_security.py::test_apply_monthly_adjudication_to_theme_report_injects_four_layers_into_final_chinese_fields tests/test_api_server_security.py::test_thematic_report_interpretation_axes_are_strict_paragraphs_for_each_theme tests/test_cli_smoke.py::test_full_reading_guided_topics_can_carry_official_day_signal_summary -q`
Expected: PASS
- [ ] **Step 6: Commit**
```bash
git add scripts/jyotish_api_server.py scripts/guided_topic_discovery.py scripts/jyotish_engine.py tests/test_api_server_security.py tests/test_cli_smoke.py
git commit -m "feat: unify strict adjudication bundle"
```
### Task 4: Make frontend and AI consume the single shared bundle
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/jyotish-app/ai-chat.js`
- Modify: `/Users/wuyongnaren/Documents/印度占星/tests/test_frontend_productization.py`
**Interfaces:**
- Consumes: `strict_adjudication_bundle`
- Produces: UI and AI prompt entry consuming bundle first, legacy fields second
- [ ] **Step 1: Add failing frontend assertions**
Add assertions requiring:
```python
assert "strict_adjudication_bundle" in main
assert "strict_adjudication_bundle" in ai_chat
```
and keep legacy checks for compatibility.
- [ ] **Step 2: Update guided topic card rendering**
In `/Users/wuyongnaren/Documents/印度占星/jyotish-app/main.js`, read:
```javascript
const bundle = topic?.strict_adjudication_bundle || {};
```
and derive:
- `strict_audit_gate`
- `monthly_adjudication_summary`
- `interpretation_axes`
from the bundle first.
- [ ] **Step 3: Update AI chat payload construction**
In `/Users/wuyongnaren/Documents/印度占星/jyotish-app/ai-chat.js`, include:
```javascript
guidedTopicContext.strict_adjudication_bundle
```
as the first-class context object, while preserving old fields.
- [ ] **Step 4: Run frontend contract tests**
Run: `python3 -m pytest tests/test_frontend_productization.py::test_guided_topic_questions_reuse_ai_chat_entry tests/test_frontend_productization.py::test_complete_reading_surfaces_guided_topic_discovery -q`
Expected: PASS
- [ ] **Step 5: Commit**
```bash
git add jyotish-app/main.js jyotish-app/ai-chat.js tests/test_frontend_productization.py
git commit -m "feat: make frontend consume strict adjudication bundle"
```
### Task 5: Run the smallest real regressions and inspect real-user output
**Files:**
- Modify: `/Users/wuyongnaren/Documents/印度占星/progress.md` (only if you are already tracking this thread there)
**Interfaces:**
- Consumes: completed runtime/thematic/bundle/frontend flow
- Produces: verified real output for the REDACTED_DATE REDACTED_TIME REDACTED_PLACE case
- [ ] **Step 1: Run focused regression suite**
Run:
```bash
python3 -m pytest \
tests/test_api_server_security.py::test_consultation_workflow_reuses_chart_data_for_thematic_report_without_recursive_full_reading \
tests/test_api_server_security.py::test_thematic_report_interpretation_axes_are_strict_paragraphs_for_each_theme \
tests/test_cli_smoke.py::test_full_reading_guided_topics_can_carry_official_day_signal_summary \
tests/test_frontend_productization.py::test_guided_topic_questions_reuse_ai_chat_entry \
-q
```
Expected: all PASS.
- [ ] **Step 2: Run real consultation workflow sample**
Run a local Python one-off calling `_compute_consultation_workflow(...)` with:
- `REDACTED_DATE REDACTED_TIME`
- `lat=36.42`
- `lon=114.2`
- `tz=8`
- themes `career/marriage/wealth`
Verify:
- `runtime_planner.executed_steps` is populated
- `thematic_report.mode == "upstream_contract_reuse"`
- each theme contains `strict_adjudication_bundle`
- [ ] **Step 3: Summarize verified behavior**
Record the exact outputs observed for:
- executed runtime steps
- thematic report mode
- presence of strict adjudication bundle in theme payloads and guided topics
- [ ] **Step 4: Commit**
```bash
git add progress.md
git commit -m "docs: record unified consultation runtime verification"
```
## Self-Review
- Spec coverage: the three requested root-cause cuts are directly covered by Tasks 2, 3, and 4.
- Placeholder scan: no TBD/TODO placeholders remain.
- Type consistency: the canonical object is always named `strict_adjudication_bundle`; runtime execution telemetry always uses `executed_steps` and `skipped_steps`.
## Execution Handoff
Plan complete and saved to `docs/superpowers/plans/2026-07-01-unified-consultation-runtime-hardening.md`. Two execution options:
**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration
**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints