diff --git a/README.md b/README.md index 6676c8f..1df9c9c 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ - 🛡️ **不虚构(no fabrication)** — 未实现或缺输入的部分只发**警告**,绝不编造。未知时间不伪造上升/宫位;缺性别不硬凑紫微盘。 - 🔒 **完全离线 + 确定性** — 星历(astronomy-engine·VSOP87+NOVAS)、时区(IANA)、历法全部内置。相同输入 + 相同版本 → **字节级一致**的 canonical JSON。 - 📚 **有来源可追溯** — 八字解读引用《子平真诠》《滴天髓》《渊海子平》公版古籍;每条结论带 `ruleId + 出处`。 -- 🕵️ **精度门禁(包装层一致性)** — 西方星体由 astronomy-engine(VSOP87 + NOVAS)计算;精度回归确保本包装层与其输出一致(非独立 JPL 对照)。上游宣称约 ±1′(对照 JPL Horizons);本仓库独立 JPL Horizons 金标待补。 +- 🕵️ **精度门禁(双层独立验证)** — 西方星体由 astronomy-engine(VSOP87 + NOVAS)计算;精度回归确保本包装层与其输出一致。主星体经独立 JPL Horizons 金标交叉校验(10 bodies × 3 合成技术时刻,最差 0.20′);五种宫制经独立 Swiss Ephemeris 宫位金标交叉校验(5 cases × 5 systems,最差 0.69′)——均 ≤1′。恋星/小行星为 approximate 级别,不受此门禁约束。 - 🔐 **隐私优先** — 解读事实层脱敏,不含姓名/经历/自由文本地名;不联网、不遥测。 - 🗣️ **解读输出有防火墙** — 专题报告(Channel B)交付前经 `lint-reading` 离线体检:第 1–5 部分禁命理术语与顾问黑话、禁空话与换词重复、禁越界预测(无收入 facts 不写加薪、不做群体比较、未知经历须用“如果/可能/例如”条件表达)。随 Skill 发布、可复现;为启发式文本检查,不保证宿主模型 100% 合规。 - 🧩 **可移植(需脚本执行)** — 一份 `SKILL.md` + 打包好的引擎,四个完整宿主(Codex / Qoder / WorkBuddy / 豆包电脑版)通用;宿主须具备本地脚本执行能力。 diff --git a/docs/STATUS.md b/docs/STATUS.md index 28ab970..bf2e8cc 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -66,7 +66,7 @@ retired and must never be pushed from or copied from. luck cycle `null` while year/month/day pillars remain; - approximate time ?`TIME_ACCURACY_APPROXIMATE`, charts still computed; - lunar input ?`LUNAR_CONVERTED` (lunar 1990-01-01 ?Gregorian 1990-01-27), charts computed. -- Western still emits `SYSTEM_NOT_YET_IMPLEMENTED` (ADR 0003 gate), never a fabricated chart. +- Western is integrated via astronomy-engine and computes all supported systems; unsupported or invalid inputs return structured errors or warnings — results are never fabricated. - Byte-identical canonical JSON between the source CLI and the isolated Skill reaffirmed. - `pnpm run forward:test` wired into `verify:all` as an enforced gate (runs after `smoke`). @@ -179,7 +179,7 @@ never by hand. | Command | Result | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pnpm run typecheck` | clean (tsc strict over packages, tools, tests) | -| `pnpm run test` | 675 tests / 33 files ?all passing (all systems + JPL Horizons 独立 golden + interpret + 吉凶 + 合婚 + reading-lint/空话/重复/越界 + validate-answer v2 结构与措辞门禁(约束引用事实豁免+全可见文本安全扫?资源上限+有界解析入口,非语义正确性证明) + western-rules/ziwei-rules 语义规则 + 版本迁移/回滚/目标白名?+ PII 隐私护栏 green) | +| `pnpm run test` | 680 tests / 33 files ?all passing (all systems + JPL Horizons 独立 golden + interpret + 吉凶 + 合婚 + reading-lint/空话/重复/越界 + validate-answer v2 结构与措辞门禁(约束引用事实豁免+全可见文本安全扫?资源上限+有界解析入口,非语义正确性证明) + western-rules/ziwei-rules 语义规则 + 版本迁移/回滚/目标白名?+ PII 隐私护栏 green) | | `pnpm run build` | `engine.mjs` ?2.8 MB + `sbom.cdx.json` + `sbom.spdx.json` (6 runtime deps) | | `pnpm run validate:skill` | 40 / 40 (incl. scripts/ no-stray-files guard + CycloneDX/SPDX SBOM checks + validate-answer/lint-reading gate-workflow doc checks) | | `pnpm run validate:reading` | 53 / 53 (topic example libraries + output-spec structure + 无术语区 firewall; offline, no LLM) | diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index 8ad1f31..271980c 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -59,7 +59,7 @@ the identical table in [STATUS.md](./STATUS.md) ("Commands & results"). Do not h resolve a disagreement; re-run the suite and copy the actual count. `pnpm run check:doc-counts` re-runs the suite and fails if either doc's `N tests / M files` count drifts from the real run. -- Typecheck: clean. Tests: **675 tests / 33 files ?all passing**. The Western provider +- Typecheck: clean. Tests: **680 tests / 33 files ?all passing**. The Western provider (astronomy-engine, VSOP87 + NOVAS) passes the ADR-0003 ??gate two ways: wrapper-consistency (vs astronomy-engine's own output) plus an **independent JPL Horizons golden** (10 bodies × 3 technical epochs fetched from the NASA/JPL Horizons service, query recorded in diff --git a/docs/adr/0003-provider-selection.md b/docs/adr/0003-provider-selection.md index e2d08f5..4fc53a3 100644 --- a/docs/adr/0003-provider-selection.md +++ b/docs/adr/0003-provider-selection.md @@ -1,7 +1,8 @@ # ADR 0003: Provider selection behind adapters (deferred to Phase 2) -- Status: Accepted (selection); providers not yet integrated +- Status: Accepted (selection); Western integrated via astronomy-engine; celestine rejected - Date: 2026-07-21 +- Updated: 2026-07-28 (current-status annotation; historical evaluation preserved below) ## Context @@ -32,7 +33,7 @@ Explicitly **not** adopted for the default build without owner approval: Swiss E BaZi/Zi Wei goldens need sourced references, and Western needs JPL/Swiss cross-checks. - Re-verify each LICENSE and package metadata at integration time (see `docs/LICENSE_AUDIT.md`). -## Outcome — Western evaluation (2026-07-21) +## Outcome — Western evaluation (2026-07-21) [HISTORICAL] celestine 0.2.1 was run through the precision regression (geocentric ecliptic longitude vs astronomy-engine (VSOP87 + NOVAS 上游;此为包装层一致性对照,非独立 JPL Horizons 金标对照), five dates 1955–2024). Sun–Neptune (8 bodies) agree to ≤1 arc-minute, @@ -40,3 +41,15 @@ but Mercury deviates up to ~17′ and Pluto up to ~37′ — the gate is **not m not wired in and Western still returns `SYSTEM_NOT_YET_IMPLEMENTED`. The reproducible harness is `packages/western/test/precision-regression.test.ts`; re-run it against any future celestine release or replacement MIT provider before adoption. + +## Current status (2026-07-28) + +- **Western provider**: astronomy-engine 2.1.19 (MIT) is now the integrated domain provider + (not merely the cross-check). `SYSTEM_NOT_YET_IMPLEMENTED` is no longer returned for + Western calculations. +- **Precision verification**: Two independent golden fixtures validate absolute accuracy: + 1. JPL Horizons golden (10 main bodies × 3 synthetic technical epochs; worst 0.20′); + 2. Swiss Ephemeris house golden (5 systems × 5 synthetic cases; worst 0.69′). + Both hold the ≤1′ gate. +- **celestine**: Remains rejected (ADR evaluation above unchanged). +- **BaZi / Zi Wei**: Integrated via tyme4ts + iztro respectively (as selected above). diff --git a/tools/validate-current-docs.test.ts b/tools/validate-current-docs.test.ts index 212088a..6b83138 100644 --- a/tools/validate-current-docs.test.ts +++ b/tools/validate-current-docs.test.ts @@ -28,6 +28,8 @@ function baseFixture(): Map { '', '需要脚本执行 能力。', '', + '主星体经独立 JPL Horizons 金标交叉校验。', + '', 'verify:cloud 依次运行 scan:deps → scan:licenses → validate:sbom → scan:secrets.', '', '指向 GitHub Release `v0.1.6`.', @@ -421,4 +423,66 @@ describe('validate-current-docs: injected reader', () => { 'scan-deps.ts: no promise of "CI always has network" / "advisory service is reachable"', ); }); + + it('13. README says JPL golden 待补 -> matching FAIL (stale claim)', () => { + const files = baseFixture(); + files.set( + 'README.md', + files + .get('README.md')! + .replace('主星体经独立 JPL Horizons 金标交叉校验。', '本仓库独立 JPL Horizons 金标待补。'), + ); + const { failed } = runChecks(readerOf(files)); + expect(failed.map((f) => f.name)).toContain('README.md: 不含 JPL golden 已存在,不得声称待补'); + }); + + it('14. README says SYSTEM_NOT_YET_IMPLEMENTED -> matching FAIL (outdated)', () => { + const files = baseFixture(); + files.set( + 'README.md', + files.get('README.md')! + '\nWestern returns SYSTEM_NOT_YET_IMPLEMENTED.', + ); + const { failed } = runChecks(readerOf(files)); + expect(failed.map((f) => f.name)).toContain( + 'README.md: 不含 不得声称 Western 返回 SYSTEM_NOT_YET_IMPLEMENTED(已集成)', + ); + }); + + it('15. README missing JPL golden existence statement -> matching FAIL', () => { + const files = baseFixture(); + files.set( + 'README.md', + files + .get('README.md')! + .replace('主星体经独立 JPL Horizons 金标交叉校验。', '主星体精度回归。'), + ); + const { failed } = runChecks(readerOf(files)); + expect(failed.map((f) => f.name)).toContain('README.md: 含 缺 JPL Horizons 金标已完成声明'); + }); + + it('16. STATUS.md says SYSTEM_NOT_YET_IMPLEMENTED -> matching FAIL (stale)', () => { + const files = baseFixture(); + files.set( + 'docs/STATUS.md', + files.get('docs/STATUS.md')! + '\nWestern still emits SYSTEM_NOT_YET_IMPLEMENTED.', + ); + const { failed } = runChecks(readerOf(files)); + expect(failed.map((f) => f.name)).toContain( + 'docs/STATUS.md: 不含 不得在当前状态段声称 Western 仍返回 SYSTEM_NOT_YET_IMPLEMENTED', + ); + }); + + it('16b. ADR 0003 [HISTORICAL] containing SYSTEM_NOT_YET_IMPLEMENTED does NOT trip the guard', () => { + const files = baseFixture(); + // ADR fixture already contains the string in its historical evaluation + files.set( + 'docs/adr/0003-provider-selection.md', + 'VSOP87 NOVAS. Western still returns SYSTEM_NOT_YET_IMPLEMENTED. [HISTORICAL]', + ); + const { failed } = runChecks(readerOf(files)); + // The ADR rule has no mustNot for SYSTEM_NOT_YET_IMPLEMENTED, so it must pass + expect(failed.map((f) => f.name)).not.toContain( + 'docs/adr/0003-provider-selection.md: 不含 不得在当前状态段声称 Western 仍返回 SYSTEM_NOT_YET_IMPLEMENTED', + ); + }); }); diff --git a/tools/validate-current-docs.ts b/tools/validate-current-docs.ts index 853dec1..b97e309 100644 --- a/tools/validate-current-docs.ts +++ b/tools/validate-current-docs.ts @@ -68,14 +68,26 @@ const RULES: DocRule[] = [ file: 'README.md', mustNot: [ { re: DE441, msg: 'DE441 错误声明' }, - { re: /三大\s*Agent/, msg: '“三大 Agent”过期表述' }, - { re: NO_NODE, msg: '“不需要 Node”错误声明' }, + { re: /三大\s*Agent/, msg: '"三大 Agent"过期表述' }, + { re: NO_NODE, msg: '"不需要 Node"错误声明' }, + { + re: /JPL[^。\n]{0,20}(待补|pending|尚未|还未)/i, + msg: 'JPL golden 已存在,不得声称待补', + }, + { + re: /SYSTEM_NOT_YET_IMPLEMENTED/, + msg: '不得声称 Western 返回 SYSTEM_NOT_YET_IMPLEMENTED(已集成)', + }, ], must: [ { re: /VSOP87|NOVAS/, msg: '缺 VSOP87/NOVAS 正确归因' }, { re: RUN_NODE, msg: '缺 运行 Node ≥ 22' }, { re: /Node(\.js)?\s*[>≥]=?\s*24/, msg: '缺 开发 Node ≥ 24' }, { re: /脚本执行|执行脚本|运行脚本/, msg: '缺 需要脚本执行能力说明' }, + { + re: /JPL Horizons[^。\n]{0,40}金标|独立 JPL Horizons/, + msg: '缺 JPL Horizons 金标已完成声明', + }, ], }, { @@ -115,6 +127,10 @@ const RULES: DocRule[] = [ mustNot: [ { re: DE441, msg: 'DE441 错误声明' }, { re: STARTMOON, msg: '仍指向已废弃 startmoon 旧工作区' }, + { + re: /SYSTEM_NOT_YET_IMPLEMENTED/, + msg: '不得在当前状态段声称 Western 仍返回 SYSTEM_NOT_YET_IMPLEMENTED', + }, ], must: [ { re: /validate:docs/, msg: '缺 validate:docs 阶段' }, @@ -254,6 +270,22 @@ function selfTest(add: (name: string, ok: boolean, detail?: string) => void): vo ); add('[self-test] 不需要-Node 命中', NO_NODE.test('也不需要安装 pnpm、Node 或 Git')); add('[self-test] HTML/SVG 输出命中', HTML_SVG_OUT.test('当前输出 HTML/SVG 报告')); + add( + '[self-test] JPL待补命中', + /JPL[^。\n]{0,20}(待补|pending|尚未|还未)/i.test('本仓库独立 JPL Horizons 金标待补'), + ); + add( + '[self-test] JPL已完成不误报', + !/JPL[^。\n]{0,20}(待补|pending|尚未|还未)/i.test('独立 JPL Horizons 金标交叉校验'), + ); + add( + '[self-test] SYSTEM_NOT_YET_IMPLEMENTED命中', + /SYSTEM_NOT_YET_IMPLEMENTED/.test('Western returns SYSTEM_NOT_YET_IMPLEMENTED'), + ); + add( + '[self-test] SYSTEM_NOT_YET_IMPLEMENTED不误报历史ADR', + !/SYSTEM_NOT_YET_IMPLEMENTED/.test('astronomy-engine integrated'), + ); add('[self-test] 干净归因不误报', !DE441.test('astronomy-engine 基于 VSOP87 与 NOVAS')); add('[self-test] 需要Node不误报', !NO_NODE.test('无需 pnpm、Git;运行需要 Node.js ≥ 22')); add('[self-test] 不完整排盘示例命中', BAD_CHART_PROMPT.test('帮我排一下 1990…出生在武汉的盘'));