Harden skill rigor constraints and sync reading quality tools

This commit is contained in:
732642856
2026-06-27 18:52:10 +08:00
parent 7600986e34
commit b884b8e7ea
30 changed files with 1262 additions and 56 deletions
+26 -2
View File
@@ -26,6 +26,8 @@ FRONTS = {
],
"operator_card": "docs/benchmark/dasha_steve_jobs_first_packet_operator_card.md",
"packet_template": "references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json",
"oracle_file": "references/oracle/dasha_shadbala_oracle_cases.json",
"apply_script": "scripts/oracle_collection_queue.py",
"external_sources": [
"JHora Vimshottari Dasha screen",
"PyJHora black-box dasha output",
@@ -43,6 +45,8 @@ FRONTS = {
],
"operator_card": "docs/benchmark/tajika_einstein_1905_first_packet_operator_card.md",
"packet_template": "references/oracle/artifacts/pending_packets/external_template_einstein_varshaphala_1905_lahiri.json",
"oracle_file": "references/oracle/tajika_annual_oracle_cases.json",
"apply_script": "scripts/tajika_annual_oracle_queue.py",
"external_sources": [
"JHora Varshaphala/Tajika annual chart screen",
"PyJHora black-box annual output",
@@ -60,6 +64,8 @@ FRONTS = {
],
"operator_card": "docs/benchmark/shadbala_redacted_place_raman_first_packet_operator_card.md",
"packet_template": "references/oracle/evidence_packet_templates/shadbala_redacted_place_raman_first_packet.json",
"oracle_file": "references/oracle/dasha_shadbala_oracle_cases.json",
"apply_script": "scripts/oracle_collection_queue.py",
"external_sources": [
"JHora Shadbala component table screenshot",
"PyJHora black-box shadbala output",
@@ -69,6 +75,24 @@ FRONTS = {
}
def _fallback_apply_command(config: dict[str, Any]) -> str:
return (
f"python3 {config['apply_script']} "
f"--oracle-file {config['oracle_file']} "
f"--apply-packet {config['packet_template']} "
"--format json"
)
def _fallback_validate_command(config: dict[str, Any]) -> str:
queue_path = "/tmp/jyotish_oracle_queue_filled.json"
return (
f"python3 scripts/oracle_collection_queue.py --oracle-file {config['oracle_file']} "
f"--format json > {queue_path} && "
f"python3 scripts/oracle_evidence_validator.py --queue-file {queue_path}"
)
def _group_missing_fields(missing_fields: list[str]) -> dict[str, Any]:
metadata_fields = [field for field in missing_fields if field.startswith("metadata.")]
target_fields = [field for field in missing_fields if field.startswith("target.")]
@@ -177,8 +201,8 @@ def build_report(front: str) -> dict[str, Any]:
first = status.get("first_priority") or {
"case_id": packet.get("case_id"),
"capture_id": packet.get("capture_id"),
"apply_command": "",
"validate_command": "",
"apply_command": _fallback_apply_command(config),
"validate_command": _fallback_validate_command(config),
"packet_path": config["packet_template"],
}
packet_missing = _packet_missing(config["packet_template"])
+60 -5
View File
@@ -189,9 +189,21 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
path = urlparse(self.path).path
try:
if path == '/api/health':
swisseph_available = False
swisseph_version = None
try:
import swisseph as swe
swisseph_available = True
swisseph_version = getattr(swe, 'version', None) or getattr(swe, '__version__', None)
except Exception:
swisseph_available = False
swisseph_version = None
self._json({
'status': 'ok',
'version': '6.9.14',
'swisseph_available': swisseph_available,
'swisseph_version': swisseph_version,
'ayanamsa_default': 'lahiri',
'modules': 'Chart/KP/Synastry/Prashna/Remedies/Dasha/Varga/Jaimini/Ashtakavarga/Shadbala/Yoga/Aspects/Tajika/Muhurta/BhavaChalit/BhavaBala/Sudarshana/Nakshatra/Transit/RectificationGate/CaseValidation/DivisionalYoga/Kakshya',
})
elif path == '/api/cities':
@@ -359,6 +371,19 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
self._check_range(key, number, min_value, max_value)
return number
def _parse_timezone(self, body, lat, lon, year, month, day, hour, minute, second):
tz = body.get('tz')
if tz is not None and tz != "":
return self._get_float(body, 'tz', 8, -14, 14)
from timezone_utils import infer_timezone
from datetime import datetime
try:
dt = datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
except Exception:
dt = datetime.utcnow()
return infer_timezone(lat, lon, dt)
def _get_float(self, body, key, default, min_value=None, max_value=None):
value = body.get(key, default)
try:
@@ -1355,7 +1380,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
second = self._get_birth_second(body)
lat = self._get_float(body, 'lat', 39.9, -90, 90)
lon = self._get_float(body, 'lon', 116.4, -180, 180)
tz = self._get_float(body, 'tz', 8, -14, 14)
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
try:
datetime(year, month, day, int(hour), int(minute), int(second))
except ValueError as e:
@@ -1555,7 +1580,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
second = self._get_birth_second(body)
lat = self._get_float(body, 'lat', 39.9, -90, 90)
lon = self._get_float(body, 'lon', 116.4, -180, 180)
tz = self._get_float(body, 'tz', 8, -14, 14)
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
try:
datetime(year, month, day, int(hour), int(minute), int(second))
except ValueError as e:
@@ -1800,6 +1825,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
planets = chart.get('planets') or {}
dasha = chart.get('dasha') or {}
shadbala = chart.get('shadbala') or {}
functional_layer = self._functional_benefic_malefic_snapshot(planets, ascendant)
top_strength = sorted(
[
{
@@ -1868,6 +1894,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
'strength': {
'shadbala_ranking': top_strength,
},
'functional_benefic_malefic': functional_layer,
'quality_boundary': {
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
},
@@ -1892,6 +1919,34 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
},
}
def _functional_benefic_malefic_snapshot(self, planets, ascendant):
try:
from yoga_engine import YogaContext
asc_sign = ascendant.get('sign')
if not asc_sign or not isinstance(planets, dict):
raise ValueError('missing ascendant sign or planets')
context = YogaContext(planets, asc_sign)
benefics = context.functional_benefics()
malefics = context.functional_malefics()
return {
'status': 'used',
'ascendant': asc_sign,
'functional_benefics': benefics,
'functional_malefics': malefics,
'effect_on_confidence': (
'高严谨模式下必须叠加功能性吉凶星;若与自然吉凶属性冲突,'
'应降低置信度并在 Technique Audit Table 中显式说明。'
),
}
except Exception as exc:
return {
'status': 'blocked',
'ascendant': ascendant.get('sign'),
'functional_benefics': [],
'functional_malefics': [],
'effect_on_confidence': f'未完成功能性吉凶星判定,需降低高严谨结论置信度: {exc}',
}
def _detect_yogas(self, planets, asc_idx):
yogas = []
KENDRA = {1,4,7,10}
@@ -2870,7 +2925,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
day = self._get_int(body, 'day', 1, 1, 31)
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
hour_decimal = self._birth_hour_decimal(birth_hour, birth_minute, birth_second)
solar_lon = planets.get('Sun', {}).get('lon', 0)
base_planets = base_result.get('planets', {}) if isinstance(base_result, dict) else {}
@@ -2925,7 +2980,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
birth_dt = self._parse_birth_datetime(body)
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
solar_return = _load_local_module('solar_return')
report = solar_return.solar_return_full_report(
birth_dt.year,
@@ -3539,7 +3594,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
second = self._get_birth_second(body)
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
datetime(year, month, day, int(hour), int(minute), int(second))
import swisseph as swe
hour_ut = self._birth_hour_decimal(hour, minute, second) - tz
+166 -16
View File
@@ -701,17 +701,152 @@ def _planet_snapshot(planets, planet_name):
def _oracle_progress_snapshot():
return {
'scope': 'external_oracle_evidence_validation',
'collection_queue': 'external_oracle_collection_queue',
'total_packets': 5,
'valid_packets': 0,
'ready_for_calibration': 0,
'production_tuning_allowed': False,
'artifact_policy': 'references/oracle/artifacts/',
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
}
try:
import json
import subprocess
import sys
import tempfile
from pathlib import Path
root = Path(__file__).resolve().parents[1]
oracle_file = root / 'references' / 'oracle' / 'dasha_shadbala_oracle_cases.json'
with tempfile.NamedTemporaryFile('w+', suffix='.json', delete=True, encoding='utf-8') as handle:
queue = subprocess.run(
[sys.executable, 'scripts/oracle_collection_queue.py', '--oracle-file', str(oracle_file), '--format', 'json'],
cwd=root,
text=True,
capture_output=True,
timeout=90,
check=False,
)
if queue.returncode != 0:
raise RuntimeError(queue.stderr.strip() or queue.stdout.strip())
handle.write(queue.stdout)
handle.flush()
validation = subprocess.run(
[sys.executable, 'scripts/oracle_evidence_validator.py', '--queue-file', handle.name],
cwd=root,
text=True,
capture_output=True,
timeout=90,
check=False,
)
if validation.returncode != 0:
raise RuntimeError(validation.stderr.strip() or validation.stdout.strip())
report = json.loads(validation.stdout)
summary = report.get('summary', {})
return {
'scope': 'external_oracle_evidence_validation',
'collection_queue': 'external_oracle_collection_queue',
'total_packets': summary.get('total_packets', 0),
'valid_packets': summary.get('valid_packets', 0),
'ready_for_calibration': summary.get('ready_for_calibration', 0),
'production_tuning_allowed': summary.get('production_tuning_allowed', False),
'artifact_policy': 'references/oracle/artifacts/',
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
'boundary': report.get('boundary', 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.'),
}
except Exception as exc:
return {
'scope': 'external_oracle_evidence_validation',
'collection_queue': 'external_oracle_collection_queue',
'total_packets': 0,
'valid_packets': 0,
'ready_for_calibration': 0,
'production_tuning_allowed': False,
'artifact_policy': 'references/oracle/artifacts/',
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
'boundary': f'Oracle progress unavailable: {exc}',
}
def _functional_benefic_malefic_snapshot(planets, ascendant):
try:
from yoga_engine import YogaContext
asc_sign = ascendant.get('sign') if isinstance(ascendant, dict) else None
if not asc_sign or not isinstance(planets, dict):
raise ValueError('missing ascendant sign or planets')
context = YogaContext(planets, asc_sign)
benefics = context.functional_benefics()
malefics = context.functional_malefics()
return {
'status': 'used',
'ascendant': asc_sign,
'functional_benefics': benefics,
'functional_malefics': malefics,
'effect_on_confidence': (
'高严谨模式下必须叠加功能性吉凶星;若与自然吉凶属性冲突,'
'应降低置信度并在 Technique Audit Table 中显式说明。'
),
}
except Exception as exc:
return {
'status': 'blocked',
'ascendant': ascendant.get('sign') if isinstance(ascendant, dict) else None,
'functional_benefics': [],
'functional_malefics': [],
'effect_on_confidence': f'未完成功能性吉凶星判定,需降低高严谨结论置信度: {exc}',
}
def _build_technique_audit_table(functional_layer, oracle_progress, modules):
dasha = modules.get('dasha') if isinstance(modules, dict) else {}
narayana = modules.get('narayana_dasha') if isinstance(modules, dict) else {}
d9 = modules.get('d9_navamsa_expanded') if isinstance(modules, dict) else {}
shadbala = modules.get('shadbala') if isinstance(modules, dict) else {}
ashtakavarga = modules.get('ashtakavarga') if isinstance(modules, dict) else {}
vimsopaka = modules.get('vimsopaka') if isinstance(modules, dict) else {}
dasa_convergence = modules.get('dasa_convergence') if isinstance(modules, dict) else {}
rows = [
{
'technique': 'Functional Benefic/Malefic',
'status': functional_layer.get('status', 'blocked'),
'source': 'scripts/yoga_engine.py::YogaContext',
'note': functional_layer.get('effect_on_confidence', '高严谨模式缺少功能性吉凶星判定。'),
},
{
'technique': 'External Oracle Progress',
'status': 'used' if oracle_progress.get('total_packets', 0) else 'blocked',
'source': 'scripts/oracle_collection_queue.py + scripts/oracle_evidence_validator.py',
'note': (
f"当前 external oracle 进度 {oracle_progress.get('ready_for_calibration', 0)}/"
f"{oracle_progress.get('total_packets', 0)} ready"
f"production_tuning_allowed={oracle_progress.get('production_tuning_allowed', False)}"
),
},
]
rows.append({
'technique': 'Vimshottari + Narayana Cross-check',
'status': 'used' if isinstance(dasha, dict) and isinstance(narayana, dict) and dasha and narayana else 'blocked',
'source': 'modules.dasha + modules.narayana_dasha + modules.dasa_convergence',
'note': (
f"Vimshottari 当前主运={((dasha.get('current_dasha') or {}).get('lord')) if isinstance(dasha, dict) else None}; "
f"Narayana 当前主运={narayana.get('current_dasha') if isinstance(narayana, dict) else None}; "
f"多系统收敛摘要={dasa_convergence.get('top_convergent_domains') if isinstance(dasa_convergence, dict) else None}"
),
})
rows.append({
'technique': 'Relevant Vargas',
'status': 'used' if isinstance(d9, dict) and d9 else 'blocked',
'source': 'modules.d9_navamsa_expanded',
'note': (
f"D9 已接入;可见键={list(d9.keys())[:5] if isinstance(d9, dict) else []}"
"高严谨解读至少应交叉 D1 + D9,婚姻/关系主题不得跳过 D9。"
),
})
rows.append({
'technique': 'Strength Layers',
'status': 'used' if isinstance(shadbala, dict) and isinstance(ashtakavarga, dict) and isinstance(vimsopaka, dict) else 'blocked',
'source': 'modules.shadbala + modules.ashtakavarga + modules.vimsopaka',
'note': (
f"Shadbala status={shadbala.get('status') if isinstance(shadbala, dict) else None}; "
f"Ashtakavarga SAV={((ashtakavarga.get('sav') or {}).get('total')) if isinstance(ashtakavarga, dict) else None}; "
f"Vimsopaka status={vimsopaka.get('status') if isinstance(vimsopaka, dict) else None}"
),
})
return rows
def _build_ai_prompt_pack(report):
@@ -722,11 +857,16 @@ def _build_ai_prompt_pack(report):
birth_info = chart.get('birth_info', {}) if isinstance(chart, dict) else {}
dasha = modules.get('dasha') or {}
current_dasha = dasha.get('current_dasha') if isinstance(dasha, dict) else {}
narayana = modules.get('narayana_dasha') or {}
dasa_convergence = modules.get('dasa_convergence') or {}
shadbala = modules.get('shadbala') or {}
shadbala_planets = shadbala.get('planets', {}) if isinstance(shadbala, dict) else {}
ashtakavarga = modules.get('ashtakavarga') or {}
sav = ashtakavarga.get('sav') if isinstance(ashtakavarga, dict) else {}
d9 = modules.get('d9_navamsa_expanded') or {}
functional_layer = _functional_benefic_malefic_snapshot(planets, chart.get('ascendant', {}))
oracle_progress = _oracle_progress_snapshot()
technique_audit_table = _build_technique_audit_table(functional_layer, oracle_progress, modules)
shadbala_ranking = []
for planet_name, pdata in sorted(
@@ -763,10 +903,18 @@ def _build_ai_prompt_pack(report):
'Ketu': _planet_snapshot(planets, 'Ketu'),
},
'timing': {
'current_mahadasha': current_dasha.get('lord') if isinstance(current_dasha, dict) else None,
'current_antardasha': current_ad.get('lord') if isinstance(current_ad, dict) else None,
'current_md_start': current_dasha.get('start') if isinstance(current_dasha, dict) else None,
'current_md_end': current_dasha.get('end') if isinstance(current_dasha, dict) else None,
'vimshottari': {
'mahadasha': current_dasha.get('lord') if isinstance(current_dasha, dict) else None,
'antardasha': current_ad.get('lord') if isinstance(current_ad, dict) else None,
'start': current_dasha.get('start') if isinstance(current_dasha, dict) else None,
'end': current_dasha.get('end') if isinstance(current_dasha, dict) else None,
},
'narayana': {
'current_dasha': narayana.get('current_dasha') if isinstance(narayana, dict) else None,
'current_year': narayana.get('current_year') if isinstance(narayana, dict) else None,
'current_age': narayana.get('current_age') if isinstance(narayana, dict) else None,
},
'convergence_top_domains': dasa_convergence.get('top_convergent_domains', []) if isinstance(dasa_convergence, dict) else [],
},
'strength': {
'shadbala_ranking': shadbala_ranking[:7],
@@ -787,7 +935,9 @@ def _build_ai_prompt_pack(report):
'warnings': report.get('warnings', []),
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
},
'oracle_progress': _oracle_progress_snapshot(),
'oracle_progress': oracle_progress,
'functional_benefic_malefic': functional_layer,
'technique_audit_table': technique_audit_table,
}
prompt_lines = [
+7 -2
View File
@@ -64,11 +64,12 @@ def load_real_case_revalidation() -> dict[str, Any]:
def load_yoga_logic_benchmark() -> dict[str, Any]:
report = json.loads((ROOT / "references/validation_logic_report.json").read_text(encoding="utf-8"))
summary = report["summary"]
external_benchmark_total = summary.get("external_benchmark_total", summary.get("pyjhora_total", 0))
return {
"charts_tested": summary["charts_tested"],
"comparable_rules": summary["comparable_rules"],
"skill_total": summary["skill_total"],
"pyjhora_total": summary["pyjhora_total"],
"external_benchmark_total": external_benchmark_total,
"agreements": summary["agreements"],
"false_positives": summary["false_positives"],
"false_negatives": summary["false_negatives"],
@@ -162,7 +163,11 @@ def build_skill_matrix(checks: dict[str, Any]) -> list[dict[str, str]]:
{
"area": "Dasha and timing",
"local_status": "usable with boundary warning",
"accuracy_signal": "Local tests pass, but oracle evidence packets are 0 ready.",
"accuracy_signal": (
f"Local tests pass; external oracle packets ready "
f"{checks['dasha_shadbala_oracle_evidence']['ready_for_calibration']}/"
f"{checks['dasha_shadbala_oracle_evidence']['total_packets']}."
),
"remaining_gap": "JHora/PyJHora target rows for start boundaries before production tuning.",
},
{
+29 -12
View File
@@ -22,6 +22,7 @@ v6.9.12 修复:
import math
import sys
import os
import json
from typing import Dict, Tuple
# v6.1.10: 导入实际Varga计算器(支持脚本和包两种调用方式)
@@ -42,24 +43,40 @@ SIGN_LORDS = {
'Sagittarius': 'Jupiter', 'Capricorn': 'Saturn', 'Aquarius': 'Saturn', 'Pisces': 'Jupiter'
}
SHADBALA_CONSTANTS_PATH = os.path.join(_script_dir, '..', 'references', 'shat_bala_constants.json')
def _to_longitude(entry: Dict[str, float]) -> float:
return float(entry['sign']) * 30 + float(entry['degree'])
def _load_shadbala_constants() -> Dict:
with open(SHADBALA_CONSTANTS_PATH, 'r', encoding='utf-8') as handle:
return json.load(handle)
_SHADBALA_CONSTANTS = _load_shadbala_constants()
# 入庙度数(sign_idx * 30 + degree
EXALTATION_DEG = {
'Sun': 10.0, 'Moon': 33.0, 'Mars': 298.0, 'Mercury': 165.0,
'Jupiter': 95.0, 'Venus': 357.0, 'Saturn': 200.0
planet: _to_longitude(entry)
for planet, entry in _SHADBALA_CONSTANTS['exaltation_degrees'].items()
}
# 落陷度数(入庙 + 180°
DEBILITATION_DEG = {p: (d + 180) % 360 for p, d in EXALTATION_DEG.items()}
# 落陷度数(由参考 JSON 直接提供,避免隐式推导与外部真值漂移
DEBILITATION_DEG = {
planet: _to_longitude(entry)
for planet, entry in _SHADBALA_CONSTANTS['debilitation_degrees'].items()
}
# 行星友好/敌对关系
# 行星友好/敌对关系(静态真值表)
FRIENDSHIP = {
'Sun': {'friend': ['Moon', 'Mars', 'Jupiter'], 'enemy': ['Saturn', 'Venus'], 'neutral': ['Mercury']},
'Moon': {'friend': ['Sun', 'Mercury'], 'enemy': [], 'neutral': ['Mars', 'Jupiter', 'Venus', 'Saturn']},
'Mars': {'friend': ['Sun', 'Moon', 'Jupiter'], 'enemy': ['Mercury'], 'neutral': ['Venus', 'Saturn']},
'Mercury': {'friend': ['Sun', 'Venus'], 'enemy': ['Moon'], 'neutral': ['Mars', 'Jupiter', 'Saturn']},
'Jupiter': {'friend': ['Sun', 'Moon', 'Mars'], 'enemy': ['Mercury', 'Venus'], 'neutral': ['Saturn']},
'Venus': {'friend': ['Mercury', 'Saturn'], 'enemy': ['Sun', 'Moon'], 'neutral': ['Mars', 'Jupiter']},
'Saturn': {'friend': ['Mercury', 'Venus'], 'enemy': ['Sun', 'Moon', 'Mars'], 'neutral': ['Jupiter']},
planet: {
'friend': relation['friends'],
'enemy': relation['enemies'],
'neutral': relation['neutrals'],
}
for planet, relation in _SHADBALA_CONSTANTS['natural_relationships'].items()
}
# Dig Bala 最强宫位
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="/Users/wuyongnaren/Documents/印度占星"
FILES=(
"AGENTS.md"
"SKILL.md"
"references/technique_registry.json"
"references/quick-reference-guide.md"
"references/strict-workflow-router.md"
"skills/jyotish-engine-modules/SKILL.md"
"skills/jyotish-full-reading-integration/SKILL.md"
)
echo "About to stage only these skill-truth files:"
for file in "${FILES[@]}"; do
echo " - $file"
done
echo
echo "Diff vs origin/main:"
git -C "$ROOT" diff --stat origin/main -- "${FILES[@]}"
echo
git -C "$ROOT" add -- "${FILES[@]}"
echo "Staged whitelist files only."
echo
echo "Current staged diff summary:"
git -C "$ROOT" diff --cached --stat -- "${FILES[@]}"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="/Users/wuyongnaren/Documents/印度占星"
FILES=(
"AGENTS.md"
"SKILL.md"
"references/technique_registry.json"
"references/quick-reference-guide.md"
"references/strict-workflow-router.md"
"skills/jyotish-engine-modules/SKILL.md"
"skills/jyotish-full-reading-integration/SKILL.md"
)
DRY_RUN="${1:-}"
echo "Skill truth whitelist sync set:"
for file in "${FILES[@]}"; do
echo " - $file"
done
echo
echo "Diff vs origin/main:"
git -C "$ROOT" diff --stat origin/main -- "${FILES[@]}"
if [[ "$DRY_RUN" == "--dry-run" ]]; then
echo
echo "Dry run only. No files copied or pushed."
exit 0
fi
echo
echo "This script is audit-only by default."
echo "Use it to inspect the exact whitelist before any manual git add/commit/push."
+50 -3
View File
@@ -15,6 +15,8 @@ Tajika/Varshaphala年运盘模块 v1.0
from typing import Dict, List, Optional
from datetime import datetime
import math
import json
import os
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
@@ -29,6 +31,41 @@ DEBIL_SIGN = {'Sun': 6, 'Moon': 7, 'Mars': 3, 'Mercury': 11, 'Jupiter': 9, 'Venu
OWN_SIGNS = {'Sun': [4], 'Moon': [3], 'Mars': [0, 7], 'Mercury': [2, 5],
'Jupiter': [8, 11], 'Venus': [1, 6], 'Saturn': [9, 10]}
SAHAM_RULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'references', 'saham_rules.json')
def _load_saham_rules() -> Dict:
with open(SAHAM_RULES_PATH, 'r', encoding='utf-8') as handle:
return json.load(handle)
_SAHAM_RULES = _load_saham_rules()
def _resolve_saham_operand(operand: str, planet_lons: Dict[str, float], asc_lon: float, computed: Dict[str, float]) -> float:
if operand == 'Ascendant':
return float(asc_lon) % 360
if operand in computed:
return float(computed[operand]) % 360
if operand in planet_lons:
return float(planet_lons[operand]) % 360
raise KeyError(f"Unsupported saham operand: {operand}")
def _calc_formula_saham(
rule_name: str,
planet_lons: Dict[str, float],
asc_lon: float,
is_day: bool,
computed: Dict[str, float],
) -> float:
rule = _SAHAM_RULES['sahams'][rule_name]
formula = rule['formula_day'] if is_day else rule['formula_night']
first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed)
second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed)
third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed)
return (third + (first - second)) % 360
def calc_muntha(birth_asc_idx: int, age: int) -> Dict:
"""
@@ -833,9 +870,11 @@ def calc_all_sahams(planet_lons: Dict[str, float],
is_day = _is_daytime(birth_dt, sun_lon, asc_lon)
results = {}
computed_formula_sahams: Dict[str, float] = {}
# 1. Punya Saham(福德点):Moon - Sun + Asc
p_lon = _saham(sun_lon, moon_lon)
p_lon = _calc_formula_saham('Punya_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
computed_formula_sahams['Punya_Saham'] = p_lon
results['punya_saham'] = _saham_dict(p_lon, 'Punya Saham', '福德点——善业、精神成长、父系福德')
# 2. Karya Saham(事业点):
@@ -883,11 +922,19 @@ def calc_all_sahams(planet_lons: Dict[str, float],
results['raja_saham'] = _saham_dict(raja_lon, 'Raja Saham', '王权点——领导力、权威')
# 11. Yasha Saham(名声点):Jupiter - Sun + Asc
yasha_lon = _saham(sun_lon, jupiter_lon)
try:
yasha_lon = _calc_formula_saham('Yashas_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
computed_formula_sahams['Yashas_Saham'] = yasha_lon
except KeyError:
yasha_lon = _saham(sun_lon, jupiter_lon)
results['yasha_saham'] = _saham_dict(yasha_lon, 'Yasha Saham', '名声点——声誉、社会地位')
# 12. Karma Saham(业力点):Saturn - Mars + Asc
karma_lon = _saham(mars_lon, saturn_lon)
try:
karma_lon = _calc_formula_saham('Karma_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
computed_formula_sahams['Karma_Saham'] = karma_lon
except KeyError:
karma_lon = _saham(mars_lon, saturn_lon)
results['karma_saham'] = _saham_dict(karma_lon, 'Karma Saham', '业力点——前世因果、责任')
# 13. Bandhu Saham(兄弟点):Mars - Mercury + Asc