修复拆类金样换机器就对不齐的问题,避免测试删掉共用排盘缓存。

指纹不再包含仓库绝对路径;采集时关闭缓存而不是删除目录。基线与本分支 8 个哈希一致,定向 pytest 11 通过。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-16 11:48:27 +08:00
co-authored by Grok (agent loop)
parent 14ad076fe4
commit a7fe7e9dd5
5 changed files with 182 additions and 60 deletions
+97 -45
View File
@@ -1,6 +1,7 @@
"""Canonical hashes for the compute/HTTP class split.
Fictional smoke chart only. Clock fields are removed before hashing.
Absolute repository paths in string values are replaced with <ROOT>.
Payload values are not written; the golden file stores sha256 and key-path types.
"""
from __future__ import annotations
@@ -8,18 +9,20 @@ from __future__ import annotations
import hashlib
import json
import os
import shutil
import sys
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[2]
SCRIPTS = ROOT / "scripts"
GOLDEN_PATH = ROOT / "tests" / "golden" / "api_server_service_extract_v1.json"
CACHE_DIR = ROOT / "scratch" / "local" / "api_chart_cache"
CLOCK_KEYS = {"cache_created_at", "cache_expires_at"}
THEMES = ("career", "wealth", "relationship")
BASELINE_SHA = "fbf04f0b5f31cd8421bc4546840b1c833aaed810"
CACHE_TTL_ENV = "JYOTISH_API_CHART_CACHE_TTL_SECONDS"
ROOT_PLACEHOLDER = "<ROOT>"
# Synthetic positions reused from tests/test_offline_compute_mixins.py.
# Not a birth record.
@@ -42,9 +45,54 @@ def prepare_env() -> None:
sys.path.insert(0, path)
def clear_chart_cache() -> None:
if CACHE_DIR.exists():
shutil.rmtree(CACHE_DIR)
@contextmanager
def chart_cache_disabled() -> Iterator[None]:
"""Turn the shared chart cache off. Do not delete the repository directory."""
previous = os.environ.get(CACHE_TTL_ENV)
os.environ[CACHE_TTL_ENV] = "0"
try:
yield
finally:
if previous is None:
os.environ.pop(CACHE_TTL_ENV, None)
else:
os.environ[CACHE_TTL_ENV] = previous
def root_spellings(root: Path = ROOT) -> tuple[str, ...]:
native = os.path.normpath(str(root))
forward = native.replace("\\", "/")
spellings = {native, forward}
if os.name == "nt":
spellings.add("\\\\?\\" + native)
return tuple(sorted(spellings, key=len, reverse=True))
def neutralize_path_string(text: str, root: Path = ROOT) -> str:
for spelling in root_spellings(root):
text = text.replace(spelling, ROOT_PLACEHOLDER)
if ROOT_PLACEHOLDER not in text:
return text
pieces = text.split(ROOT_PLACEHOLDER)
rebuilt = [pieces[0]]
for piece in pieces[1:]:
if piece.startswith("\\") or piece.startswith("/"):
end = 0
while end < len(piece) and piece[end] not in " \t\r\n\"'":
end += 1
piece = piece[:end].replace("\\", "/") + piece[end:]
rebuilt.append(piece)
return ROOT_PLACEHOLDER.join(rebuilt)
def neutralize_paths(value: Any, root: Path = ROOT) -> Any:
if isinstance(value, str):
return neutralize_path_string(value, root)
if isinstance(value, dict):
return {key: neutralize_paths(item, root) for key, item in value.items()}
if isinstance(value, list):
return [neutralize_paths(item, root) for item in value]
return value
def theme_body(theme: str) -> dict[str, Any]:
@@ -110,7 +158,7 @@ def flatten_keypaths(value: Any, prefix: str = "", *, depth: int = 0, max_depth:
def canonical_text(value: Any) -> str:
return json.dumps(strip_clock(value), sort_keys=True, ensure_ascii=False)
return json.dumps(neutralize_paths(strip_clock(value)), sort_keys=True, ensure_ascii=False)
def digest(value: Any) -> dict[str, Any]:
@@ -124,56 +172,55 @@ def digest(value: Any) -> dict[str, Any]:
def capture_mcp(theme: str) -> Any:
prepare_env()
clear_chart_cache()
from consultation_workflow_service import execute_consultation_workflow
with chart_cache_disabled():
from consultation_workflow_service import execute_consultation_workflow
return execute_consultation_workflow(theme_body(theme), surface="skill_mcp")
return execute_consultation_workflow(theme_body(theme), surface="skill_mcp")
def capture_http(theme: str) -> Any:
"""HTTP handler method, on the compute class after the split."""
prepare_env()
clear_chart_cache()
from jyotish_api_server import JyotishComputeService
with chart_cache_disabled():
from jyotish_api_server import JyotishComputeService
return JyotishComputeService()._compute_consultation_workflow(theme_body(theme))
return JyotishComputeService()._compute_consultation_workflow(theme_body(theme))
def capture_repairs() -> dict[str, Any]:
prepare_env()
clear_chart_cache()
from capture_report_blocked_repairs_golden import (
BIRTH,
MODULE_KEEP,
THEMES as REPAIR_THEMES,
VARGA_KEEP,
_body,
_handler,
_trim_workflow,
)
with chart_cache_disabled():
from capture_report_blocked_repairs_golden import (
BIRTH,
MODULE_KEEP,
THEMES as REPAIR_THEMES,
VARGA_KEEP,
_body,
_handler,
_trim_workflow,
)
payload: dict[str, Any] = {
"source": "scripts/jyotish_api_server.py _compute_consultation_workflow",
"smoke_birth": {**BIRTH, "note": "fictional capture identity; not a real person"},
"trim": {
"keep_varga_keys": list(VARGA_KEEP),
"keep_modules": list(MODULE_KEEP),
"drop": [
"ai_prompt_pack",
"vedastro",
"varga_spectrum",
"guided_topics",
"western_spectrum",
"technique_audit_table",
],
},
"themes": {},
}
for theme in REPAIR_THEMES:
clear_chart_cache()
raw = _handler()._compute_consultation_workflow(_body(theme))
payload["themes"][theme] = _trim_workflow(raw)
return payload
payload: dict[str, Any] = {
"source": "scripts/jyotish_api_server.py _compute_consultation_workflow",
"smoke_birth": {**BIRTH, "note": "fictional capture identity; not a real person"},
"trim": {
"keep_varga_keys": list(VARGA_KEEP),
"keep_modules": list(MODULE_KEEP),
"drop": [
"ai_prompt_pack",
"vedastro",
"varga_spectrum",
"guided_topics",
"western_spectrum",
"technique_audit_table",
],
},
"themes": {},
}
for theme in REPAIR_THEMES:
raw = _handler()._compute_consultation_workflow(_body(theme))
payload["themes"][theme] = _trim_workflow(raw)
return payload
def capture_evidence() -> Any:
@@ -207,10 +254,15 @@ def provenance() -> dict[str, Any]:
"env": {
"VEDASTRO_ENABLE_NETWORK": "0",
"JYOTISH_SKIP_LOCAL_ENV": "1",
"JYOTISH_API_CHART_CACHE_TTL_SECONDS": "0",
},
"clockFieldsRemoved": sorted(CLOCK_KEYS),
"rootPlaceholder": ROOT_PLACEHOLDER,
"mcpSurface": "skill_mcp",
"note": "fictional smoke chart from the task brief; payload values are not stored",
"note": (
"fictional smoke chart from the task brief; payload values are not stored; "
"repository absolute paths in string values are replaced with <ROOT>"
),
}