fix(qizheng): pin vendor bytes to local a911c890 checkout
Re-copy LICENSE/README/package.json/cli.cjs from the local yinduzhanxing snapshot. Drop dist/index.cjs that was assembled from npm. Do not include 6c27aab6 or 8bba9cb5.
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
- **无 Docker:** 本机 `docker` 不在 PATH。无法构建 `deploy/railway-api.Dockerfile`,也无法在容器内跑 `node --version` 或断言 `Path('/app/vendor/stem-branch/dist/cli.cjs').exists()`。**不得把镜像构建或容器内验证写成通过。** Dockerfile 已按任务加上 `nodejs` 运行时与 `COPY vendor ./vendor`,待有 Docker 的环境验证。
|
||||
- **替代证据(宿主机):** `node --version` = `v22.23.2`。vendored `dist/cli.cjs` 45,963 行 / 2,682,464 字节,与任务书 45,963 行 / 2.68 MB 一致。用任务书虚构资料 `1990-04-09T13:24:00+08:00 / 31.19N 121.44E` 实跑 `getSevenGovernorsChart`:11 曜、12 宫、命宫星宿·午宫、太阳宿度 177.94 奎 7.44°、相位 27、神煞 0。
|
||||
- **上游 Git 仓不再从 GitHub 拉。** 本机目录 `G:\Ferti\yinduzhanxing-codex-add-birth-time-rectification-skill` 即任务书 `/workspace/yinduzhanxing`(分支 `codex/add-birth-time-rectification-skill` / `a911c890`)。任务书列出的 `vendor/stem-branch/{LICENSE,README.md,package.json,dist/cli.cjs}` 已按内容拷贝到本 worktree 同名路径,运行时只读仓库内相对路径。`scripts/qizheng_chart_engine.py` 以上游 168 行适配器为起点,按任务 2 改了派别参数、boundary、坐标系和错误路径。CLI 不暴露 `ketuMode`/`siderealMode`,默认走 `cli.cjs`,覆盖派别时用同包 `dist/index.cjs`。
|
||||
- **上游以本机目录为准,不从 GitHub 拉,也不带同分支 `6c27aab6` / `8bba9cb5`。** `G:\Ferti\yinduzhanxing-codex-add-birth-time-rectification-skill` = 任务书 `/workspace/yinduzhanxing` @ `a911c890`。`vendor/stem-branch/{LICENSE,README.md,package.json,dist/cli.cjs}` 已与该目录逐字节核对(LICENSE Apache-2.0,Copyright 2026 Albert Hui)。先前从 npm 另凑的 `dist/index.cjs` 已删除,vendor 树与该快照一致。CLI 无 `ketuMode` 旗标,请求里的派别会回写,`engine_ketu_mode` 以 CLI 实际输出为准。
|
||||
- **本机无仓库 `.venv` 目录:** 主仓 `.venv` 是指向 `/workspace/Jyotisha/.venv` 的 git symlink,Windows 检出为 25 字节指针文件。测试与预检使用 Anaconda Python 3.11.7(已有 `pytest` 与 `pyswisseph`)。`timezonefinder` 仍缺,与既有 BLOCKED 条目一致。
|
||||
|
||||
## 只读星盘页(2026-09-15,分支 `codex/chart-page-20260915`)
|
||||
|
||||
@@ -7,18 +7,16 @@ This product includes @4n6h4x0r/stem-branch 0.8.0 (Apache License 2.0).
|
||||
Upstream: https://github.com/h4x0r/stem-branch
|
||||
npm: https://www.npmjs.com/package/@4n6h4x0r/stem-branch
|
||||
|
||||
The following files were copied from the local upstream checkout
|
||||
`yinduzhanxing` branch `codex/add-birth-time-rectification-skill`
|
||||
(commit a911c890), same paths, unmodified:
|
||||
The following files were copied unmodified from the local upstream
|
||||
checkout of yinduzhanxing @ a911c890 (branch
|
||||
codex/add-birth-time-rectification-skill). That snapshot is the source
|
||||
of truth for bytes and LICENSE. Commits 6c27aab6 and 8bba9cb5 on the
|
||||
same branch are out of scope and were not copied.
|
||||
|
||||
vendor/stem-branch/LICENSE
|
||||
vendor/stem-branch/README.md
|
||||
vendor/stem-branch/package.json
|
||||
vendor/stem-branch/dist/cli.cjs
|
||||
|
||||
index.cjs is the same 0.8.0 library entry. The published CLI does not
|
||||
expose ketuMode / siderealMode flags; that file is kept so request-level
|
||||
school parameters can actually change the chart.
|
||||
|
||||
Jyotisha does not modify the vendored files. This NOTICE is the attribution
|
||||
required by Apache-2.0 §4.
|
||||
Jyotisha does not modify the vendored files. This NOTICE is the
|
||||
attribution required by Apache-2.0 §4.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## 任务 1 · vendored 归档与镜像 Node
|
||||
|
||||
- 上游不从 GitHub 拉。本机 `G:\Ferti\yinduzhanxing-codex-add-birth-time-rectification-skill` = 任务书 `/workspace/yinduzhanxing`(`a911c890`)。已按内容拷贝 `vendor/stem-branch/{LICENSE,README.md,package.json,dist/cli.cjs}` 到本仓同名路径。`cli.cjs` **45,963 行 / 2,682,464 B**,sha256 前缀 `cf1bca10507ffcc9`,与上游文件逐字节相同。
|
||||
- `scripts/qizheng_chart_engine.py` 以上游 168 行适配器为起点(`calculate_qizheng_chart` + CLI `--seven-governors --json`),再按任务 2 改派别参数、boundary、坐标系和错误路径。默认走 `cli.cjs`;覆盖 `ketu_mode` / `sidereal_mode` 时用同包 `dist/index.cjs`(CLI 无对应旗标)。
|
||||
- `scripts/qizheng_chart_engine.py` 以上游 168 行适配器为起点(`calculate_qizheng_chart` + CLI `--seven-governors --json`),再按任务 2 改派别参数、boundary、坐标系和错误路径。vendor 以本机 `a911c890` 目录为准,已删除从 npm 另凑的 `dist/index.cjs`。未带 `6c27aab6` / `8bba9cb5`。CLI 无 ketuMode 旗标:请求派别回写 `calculation.ketu_mode`,实际盘面以 `engine_ketu_mode` 为准。
|
||||
- 仓库根 `NOTICE`:包名、0.8.0、Apache-2.0、上游地址、取入方式。
|
||||
- `deploy/railway-api.Dockerfile`:`nodejs` 运行时(不装 npm/构建链),`COPY vendor ./vendor`。
|
||||
- `deploy/gated-paths.txt`:新增 `vendor/**`。
|
||||
|
||||
@@ -20,7 +20,6 @@ from typing import Any
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
VENDORED_CLI = REPO_ROOT / "vendor" / "stem-branch" / "dist" / "cli.cjs"
|
||||
VENDORED_LIB = REPO_ROOT / "vendor" / "stem-branch" / "dist" / "index.cjs"
|
||||
DEFAULT_TIMEOUT_SECONDS = 20
|
||||
COORDINATE_SYSTEM = "qizheng_mansion_degrees_from_jiao"
|
||||
DEFAULT_KETU_MODE = "apogee"
|
||||
@@ -48,17 +47,6 @@ BOUNDARY = (
|
||||
"庙旺判定未闭合(十一体×十二宫共 132 格仅 9 格有直接证据,"
|
||||
"runtime_promotable_count = 0),不得进入解读、报告或咨询结论。"
|
||||
)
|
||||
_NODE_EVAL = (
|
||||
"const fs=require('fs');"
|
||||
"const payload=JSON.parse(fs.readFileSync(0,'utf8'));"
|
||||
"const lib=require(payload.library);"
|
||||
"const chart=lib.getSevenGovernorsChart("
|
||||
"new Date(payload.iso),"
|
||||
"{lat:payload.lat,lon:payload.lon},"
|
||||
"{ketuMode:payload.ketuMode,siderealMode:payload.siderealMode}"
|
||||
");"
|
||||
"process.stdout.write(JSON.stringify({sevenGovernors:chart}));"
|
||||
)
|
||||
|
||||
|
||||
class QizhengChartError(ValueError):
|
||||
@@ -196,33 +184,10 @@ def _run_cli(iso: str, lat: float, lon: float, timeout_seconds: int) -> dict[str
|
||||
]
|
||||
if FORBIDDEN_CLI_FLAGS.intersection(command):
|
||||
raise QizhengChartError("forbidden engine subcommand blocked", error_code="ERR_QIZHENG_ENGINE")
|
||||
return _run_node(command, timeout_seconds=timeout_seconds)
|
||||
|
||||
|
||||
def _run_library(iso: str, lat: float, lon: float, ketu_mode: str, sidereal_mode: dict[str, Any], timeout_seconds: int) -> dict[str, Any]:
|
||||
if not VENDORED_LIB.is_file():
|
||||
raise QizhengChartError("vendored 七政四余 library is unavailable", error_code="ERR_QIZHENG_CLI_MISSING")
|
||||
payload = {
|
||||
"library": os.fspath(VENDORED_LIB),
|
||||
"iso": iso,
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"ketuMode": ketu_mode,
|
||||
"siderealMode": sidereal_mode,
|
||||
}
|
||||
return _run_node(
|
||||
[_node_bin(), "-e", _NODE_EVAL],
|
||||
timeout_seconds=timeout_seconds,
|
||||
stdin=json.dumps(payload, ensure_ascii=False),
|
||||
)
|
||||
|
||||
|
||||
def _run_node(command: list[str], *, timeout_seconds: int, stdin: str | None = None) -> dict[str, Any]:
|
||||
try:
|
||||
completed = subprocess.run(
|
||||
command,
|
||||
cwd=str(REPO_ROOT),
|
||||
input=stdin,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
@@ -320,11 +285,7 @@ def calculate_qizheng_chart(body: dict[str, Any], *, timeout_seconds: int = DEFA
|
||||
iso = _iso_local(body)
|
||||
ketu_mode = _ketu_mode(body)
|
||||
sidereal_mode = _sidereal_mode(body)
|
||||
uses_defaults = ketu_mode == DEFAULT_KETU_MODE and sidereal_mode == DEFAULT_SIDEREAL_MODE
|
||||
if uses_defaults:
|
||||
raw = _run_cli(iso, lat, lon, timeout_seconds)
|
||||
else:
|
||||
raw = _run_library(iso, lat, lon, ketu_mode, sidereal_mode, timeout_seconds)
|
||||
raw = _run_cli(iso, lat, lon, timeout_seconds)
|
||||
return _normalize(raw, {**body, "lat": lat, "lon": lon}, ketu_mode=ketu_mode, sidereal_mode=sidereal_mode, iso=iso)
|
||||
|
||||
|
||||
|
||||
@@ -69,15 +69,12 @@ def test_live_chart_matches_golden_and_product_contract() -> None:
|
||||
assert "\\" not in raw or "[redacted-path]" in raw
|
||||
|
||||
|
||||
def test_ketu_mode_request_overrides_and_is_echoed() -> None:
|
||||
def test_ketu_mode_request_is_echoed_and_engine_mode_is_visible() -> None:
|
||||
_require_node()
|
||||
result = calculate_qizheng_chart({**SAMPLE, "ketu_mode": "descending-node"})
|
||||
assert result["calculation"]["ketu_mode"] == "descending-node"
|
||||
assert result["calculation"]["engine_ketu_mode"] == "descending-node"
|
||||
rahu = result["bodies"]["rahu"]["siderealLon"]
|
||||
ketu = result["bodies"]["ketu"]["siderealLon"]
|
||||
separation = abs((ketu - rahu) % 360)
|
||||
assert min(separation, 360 - separation) > 170
|
||||
# a911c890 CLI has no ketuMode flag; actual school is the engine default.
|
||||
assert result["calculation"]["engine_ketu_mode"] == "apogee"
|
||||
|
||||
|
||||
def test_missing_lat_is_structured_input_error() -> None:
|
||||
@@ -95,7 +92,6 @@ def test_node_missing_error(monkeypatch) -> None:
|
||||
|
||||
def test_cli_missing_error(monkeypatch, tmp_path: Path) -> None:
|
||||
monkeypatch.setattr(engine, "VENDORED_CLI", tmp_path / "cli.cjs")
|
||||
monkeypatch.setattr(engine, "VENDORED_LIB", tmp_path / "index.cjs")
|
||||
monkeypatch.setattr(engine.shutil, "which", lambda name: "node")
|
||||
with pytest.raises(engine.QizhengChartError, match="unavailable") as caught:
|
||||
calculate_qizheng_chart(SAMPLE)
|
||||
@@ -143,9 +139,9 @@ def test_non_json_stdout_error(monkeypatch) -> None:
|
||||
|
||||
|
||||
def test_source_never_invokes_forbidden_cli_flags() -> None:
|
||||
assert "--pillars" not in engine._NODE_EVAL
|
||||
assert "--luck" not in engine._NODE_EVAL
|
||||
assert "--seven-governors" not in engine._NODE_EVAL
|
||||
assert "getSevenGovernorsChart" in engine._NODE_EVAL
|
||||
argv = ["node", "-e", engine._NODE_EVAL]
|
||||
assert not engine.FORBIDDEN_CLI_FLAGS.intersection(argv)
|
||||
source = Path(engine.__file__).read_text(encoding="utf-8")
|
||||
assert "--seven-governors" in source
|
||||
assert "--json" in source
|
||||
command_block = source.split("command = [", 1)[1].split("]", 1)[0]
|
||||
for flag in engine.FORBIDDEN_CLI_FLAGS:
|
||||
assert flag not in command_block
|
||||
|
||||
Vendored
-46608
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user