Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 / 豆包电脑版)通用;宿主须具备本地脚本执行能力。
Expand Down
4 changes: 2 additions & 2 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand Down Expand Up @@ -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) |
Expand Down
2 changes: 1 addition & 1 deletion docs/VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions docs/adr/0003-provider-selection.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -32,11 +33,23 @@ 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,
but Mercury deviates up to ~17′ and Pluto up to ~37′ — the gate is **not met**, so celestine is
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).
64 changes: 64 additions & 0 deletions tools/validate-current-docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ function baseFixture(): Map<string, string> {
'',
'需要脚本执行 能力。',
'',
'主星体经独立 JPL Horizons 金标交叉校验。',
'',
'verify:cloud 依次运行 scan:deps → scan:licenses → validate:sbom → scan:secrets.',
'',
'指向 GitHub Release `v0.1.6`.',
Expand Down Expand Up @@ -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',
);
});
});
36 changes: 34 additions & 2 deletions tools/validate-current-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 金标已完成声明',
},
],
},
{
Expand Down Expand Up @@ -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 阶段' },
Expand Down Expand Up @@ -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…出生在武汉的盘'));
Expand Down
Loading