Merge PR #49: pass birth-time accuracy truth into consultation engine
Reviewed: tsc + consult 295 + rectification 747 + python suites green; guard functions and window/general instructions verified unchanged. Conflict: BUG entry renumbered 467→468 (467 taken by report-extraction). Review fix: test_consultation_birth_accuracy.py added to CORE_PYTEST_TARGETS (pytest-style file outside the rectification glob would never hit the auto gate). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Consultation birth-time accuracy truth-passing invariants."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
SCRIPTS = os.path.join(os.path.dirname(__file__), '..', 'scripts')
|
||||
if SCRIPTS not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS)
|
||||
|
||||
from birth_time_rectifier import get_effective_accuracy, get_enabled_vargas # noqa: E402
|
||||
from jyotish_api_server import JyotishAPIHandler, _build_consumer_context # noqa: E402
|
||||
|
||||
CANDIDATE_WINDOW_COPY = re.compile(
|
||||
r'candidate windows|not_auto_rectified|按时间范围|建议先做事件反验',
|
||||
re.I,
|
||||
)
|
||||
PRECISION_ANNOTATION_COPY = re.compile(r'填报时间|标注精度|precision', re.I)
|
||||
|
||||
|
||||
def _handler() -> JyotishAPIHandler:
|
||||
handler = JyotishAPIHandler.__new__(JyotishAPIHandler)
|
||||
handler._compute_vedastro_gateway_run = lambda body: { # type: ignore[method-assign]
|
||||
'status': 'official_blocked',
|
||||
'official_closure_reason': 'accuracy_contract_probe',
|
||||
}
|
||||
return handler
|
||||
|
||||
|
||||
def _gate(declared_accuracy: str, time_source: str) -> dict:
|
||||
return _handler()._compute_rectification_gate({
|
||||
'planets': {
|
||||
'Sun': {'lon': 80.0},
|
||||
'Moon': {'lon': 123.0},
|
||||
'Mars': {'lon': 210.0},
|
||||
'Mercury': {'lon': 75.0},
|
||||
'Jupiter': {'lon': 15.0},
|
||||
'Venus': {'lon': 102.0},
|
||||
'Saturn': {'lon': 330.0},
|
||||
'Rahu': {'lon': 5.0},
|
||||
'Ketu': {'lon': 185.0},
|
||||
},
|
||||
'ascendant': {'lon': 15.0},
|
||||
'declared_accuracy': declared_accuracy,
|
||||
'time_source': time_source,
|
||||
})
|
||||
|
||||
|
||||
def _consumer(rectification: dict) -> dict:
|
||||
return _build_consumer_context(
|
||||
question='我的事业接下来怎么走',
|
||||
route_packet={'question_type': 'career', 'primary_theme': 'career'},
|
||||
chart={'success': True},
|
||||
rectification=rectification,
|
||||
machine_evidence_packet={'sections': {'D1': {'status': 'used'}, 'D9': {'status': 'used'}, 'D10': {'status': 'used'}}},
|
||||
vedastro_official={'status': 'blocked'},
|
||||
)
|
||||
|
||||
|
||||
def _joined_boundaries(context: dict) -> str:
|
||||
return ' '.join(str(value) for value in context.get('domain_boundaries', {}).values())
|
||||
|
||||
|
||||
def test_hospital_gate_is_minute_with_d9_d10_and_no_uncertainty_boundary() -> None:
|
||||
result = _gate('minute', 'hospital')
|
||||
context = _consumer(result)
|
||||
|
||||
assert result['effective_accuracy'] == 'minute'
|
||||
assert result['lagna_boundary']['is_sensitive'] is False
|
||||
assert result['enabled_vargas']['D9'] == 'enabled'
|
||||
assert result['enabled_vargas']['D10'] == 'enabled'
|
||||
assert 'birth_time_uncertainty_boundary' not in context['domain_boundaries']
|
||||
assert CANDIDATE_WINDOW_COPY.search(_joined_boundaries(context)) is None
|
||||
|
||||
|
||||
def test_family_exact_and_approximate_use_precision_annotation_not_candidate_windows() -> None:
|
||||
family = _gate('minute', 'family_clear')
|
||||
approximate = _gate('15min', 'family_vague')
|
||||
|
||||
assert family['effective_accuracy'] == '5min'
|
||||
assert approximate['effective_accuracy'] == '15min'
|
||||
assert '按填报时间排盘' in family['summary']['headline']
|
||||
assert '按填报时间排盘' in approximate['summary']['headline']
|
||||
assert CANDIDATE_WINDOW_COPY.search(family['summary']['headline']) is None
|
||||
assert CANDIDATE_WINDOW_COPY.search(approximate['summary']['headline']) is None
|
||||
|
||||
family_context = _consumer(family)
|
||||
approximate_context = _consumer(approximate)
|
||||
for context in (family_context, approximate_context):
|
||||
joined = _joined_boundaries(context)
|
||||
assert CANDIDATE_WINDOW_COPY.search(joined) is None
|
||||
assert PRECISION_ANNOTATION_COPY.search(joined)
|
||||
assert 'not_auto_rectified' not in context.get('domain_context_layers', [])
|
||||
|
||||
|
||||
def test_five_min_and_fifteen_min_vargas_are_not_unknown() -> None:
|
||||
unknown = get_enabled_vargas('unknown')
|
||||
five = get_enabled_vargas('5min')
|
||||
fifteen = get_enabled_vargas('15min')
|
||||
|
||||
assert five != unknown
|
||||
assert fifteen != unknown
|
||||
assert five['D1'] == 'enabled'
|
||||
assert five['D9'] == 'enabled'
|
||||
assert five['D10'] == 'enabled'
|
||||
assert five['D5'] == 'enabled_with_warning'
|
||||
assert five['D4'] == 'enabled_with_warning'
|
||||
assert five['D7'] == 'enabled_with_warning'
|
||||
assert five['D30'] == 'disabled'
|
||||
assert five['D60'] == 'disabled'
|
||||
assert fifteen['D1'] == 'enabled'
|
||||
assert fifteen['D9'] == 'enabled'
|
||||
|
||||
|
||||
def test_rectified_and_hospital_effective_accuracy_stay_precise() -> None:
|
||||
assert get_effective_accuracy('rectified', 'rectified') == 'rectified'
|
||||
assert get_effective_accuracy('minute', 'hospital') == 'minute'
|
||||
assert get_effective_accuracy('minute', 'family_clear') == '5min'
|
||||
assert get_effective_accuracy('15min', 'family_vague') == '15min'
|
||||
|
||||
rectified = _gate('rectified', 'rectified')
|
||||
context = _consumer(rectified)
|
||||
assert rectified['effective_accuracy'] == 'rectified'
|
||||
assert 'birth_time_uncertainty_boundary' not in context['domain_boundaries']
|
||||
assert CANDIDATE_WINDOW_COPY.search(_joined_boundaries(context)) is None
|
||||
Reference in New Issue
Block a user