dsh-vibe-math 2.8.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/AUDIT-CHECKLIST.md +6 -3
- package/docs/release-notes/README.en.md +1 -0
- package/docs/release-notes/README.md +1 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.1.en.md +49 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.1.md +49 -0
- package/docs/release-playbook.md +8 -0
- package/docs/test-timing.md +1 -1
- package/package.json +8 -3
- package/scripts/release-check.mjs +181 -0
- package/tests/release-check.mutants.mjs +78 -0
- package/tests/v4-final-paper.mutants.mjs +8 -0
- package/tests/v4-final-paper.test.mjs +628 -0
- package/vibe-math-v4/vibe-math-v4.js +30 -0
package/README.en.md
CHANGED
|
@@ -628,7 +628,7 @@ Common controls: `vibe_v5_configure` (configure first) → `vibe_v5_start` (star
|
|
|
628
628
|
- **The persona source text of the four presets**: [`prompt-corpus-persona/persona-corpus.md`](prompt-corpus-persona/persona-corpus.md) (the prompts the main agent actually receives: which tools, which parameters, which slash subcommands; generated by `audit-persona-surface.test.mjs` and shipped with the package)
|
|
629
629
|
- **Lean formal verification (a contract shared by the four architectures)**: [`docs/formal-verification.md`](docs/formal-verification.md)
|
|
630
630
|
- **Final paper (a contract shared by the four architectures)**: [`docs/final-paper.md`](docs/final-paper.md) (the five parameters plus `paperEditor` on v4/v5, the "paper phase before the run is marked complete" ordering, the `Paper/<id>/` artifacts and the nine-section skeleton, LaTeX detection with repair-then-degrade, the v4/v5 team co-authoring flow and the v5 office-consultation rule)
|
|
631
|
-
- **Test timing baseline and parallel run recipes**: [`docs/test-timing.md`](docs/test-timing.md) (`node tests/run-tests.mjs` runs **every suite AND every probe** in parallel: `TOTAL
|
|
631
|
+
- **Test timing baseline and parallel run recipes**: [`docs/test-timing.md`](docs/test-timing.md) (`node tests/run-tests.mjs` runs **every suite AND every probe** in parallel: `TOTAL 106 (44 suites + 62 probes/variants — JOB counts, not file counts); the authority is `node tests/run-tests.mjs --counts` (JOB counts, not file counts), checked by [`tests/audit-readme-counts.mjs`](tests/audit-readme-counts.mjs); which 71 of the `tests/*.mjs` files ship in the package (18 suites + 53 probe/script files) and which are repository-only is in §1.1 of that document; every runner prints its elapsed time/speedup so the next strategy can be chosen)
|
|
632
632
|
- **Static prompt-surface consistency (persona ↔ tool registry ↔ slash command hint/usage)**: [`audit-persona-surface.test.mjs`](tests/audit-persona-surface.test.mjs) (260 assertions, and generates [`prompt-corpus-persona/persona-corpus.md`](prompt-corpus-persona/persona-corpus.md) for manual review) + [`audit-persona-sensitivity.mjs`](tests/audit-persona-sensitivity.mjs) (16 sensitivity probes) — guarding "every registered tool must appear in the persona / every name in the persona must really be registered / the `prefix` and `text` blocks must match line by line / hint, usage, and the actual branch must agree in all three places"
|
|
633
633
|
- **Mandatory checklist for a full review**: [`AUDIT-CHECKLIST.md`](docs/AUDIT-CHECKLIST.md) (this repository's mandatory audit process; §1.9 specifically checks "whether the tool parameter schema can accommodate them")
|
|
634
634
|
- **Prompt/interaction invariants (all four sets together, re-checkable in one command)**: [`audit-prompt-invariants.mjs`](tests/audit-prompt-invariants.mjs) (157 assertions) — encoding, one by one, "the classes of prompt/tool-surface defects that have really occurred historically" as static invariants (abbreviated tool names, projecting a fidelity defect as 0, `defect` written only in the prompt but not implemented, a receipt contract missing `defect`, passing without a note, wrong field names, the `off` tier still being able to write state in its receipt, uncertain corpora, missing probes, **a tool's closed schema that cannot accommodate the parameters in its own documentation**, **keys that the schema declares at the parameter level but silently drops**). Adding `--self-probe` injects these defect shapes in memory and requires the corresponding invariants to **turn red** while the unmutated control run **stays green** (5/5); the script itself additionally carries six self-checks X5–X8b (the comment scanner must recognize regex literals — including regexes **after a keyword** such as `return /…/` — `//` inside strings must be preserved, stripping comments must not change line structure, and the parse-level criterion that "the four sets of source, with comments stripped, must still parse under `node --check`")
|
package/README.md
CHANGED
|
@@ -628,7 +628,7 @@ v3 与 v2 同一开关、同一时序(严格收口;**在调度器停止之
|
|
|
628
628
|
- **四个预设的 persona 原文**:[`prompt-corpus-persona/persona-corpus.md`](prompt-corpus-persona/persona-corpus.md)(主代理实际收到的提示词:有哪些工具、哪些参数、哪些斜杠子命令;由 `audit-persona-surface.test.mjs` 生成,随包发布)
|
|
629
629
|
- **Lean 形式化验证(四架构共用契约)**:[`docs/formal-verification.md`](docs/formal-verification.md)
|
|
630
630
|
- **最终论文(四架构共用契约)**:[`docs/final-paper.md`](docs/final-paper.md)(五个参数与 v4/v5 的 `paperEditor`、收口顺序"先论文后完成"、`Paper/<id>/` 产物与 9 节骨架、LaTeX 检测顺序与"先修复后降级"、v4/v5 团队合写流程与 v5 所办咨询规则)
|
|
631
|
-
- **测试耗时基线与并行跑法**:[`docs/test-timing.md`](docs/test-timing.md)(`node tests/run-tests.mjs` 并行跑**全部套件 + 全部探针**:`TOTAL
|
|
631
|
+
- **测试耗时基线与并行跑法**:[`docs/test-timing.md`](docs/test-timing.md)(`node tests/run-tests.mjs` 并行跑**全部套件 + 全部探针**:`TOTAL 106`(44 套件 + 62 探针/变体,**作业计数**(job count),不是文件计数);**这些数字必须派生**:`node tests/run-tests.mjs --counts` 是权威来源(**作业计数**,不是文件计数),并由 [`tests/audit-readme-counts.mjs`](tests/audit-readme-counts.mjs) 校验本页与另两份文档;随包发布的是 `files[]` 里的 71 个 `tests/*.mjs`(18 个套件 + 53 个探针/脚本)(文件计数),其余仅开发检出可见,清单见该文档 §1.1;每个 runner 都会打印耗时/加速比供下次选策略)
|
|
632
632
|
- **静态提示词面一致性(persona ↔ 工具注册表 ↔ 斜杠命令 hint/usage)**:[`audit-persona-surface.test.mjs`](tests/audit-persona-surface.test.mjs)(260 条断言,并生成 [`prompt-corpus-persona/persona-corpus.md`](prompt-corpus-persona/persona-corpus.md) 供人工复核)+ [`audit-persona-sensitivity.mjs`](tests/audit-persona-sensitivity.mjs)(16 条灵敏度探针)——守"注册的工具必须在 persona 里出现 / persona 里的名字必须真的注册 / `prefix` 与 `text` 两块逐行一致 / hint、usage、实际分支三处必须一致"
|
|
633
633
|
- **全面检查必查清单**:[`AUDIT-CHECKLIST.md`](docs/AUDIT-CHECKLIST.md)(本仓库的强制审计流程;§1.9 专门查"工具参数 schema 收不收得下")
|
|
634
634
|
- **提示词/交互不变式(四套一起,可一键复核)**:[`audit-prompt-invariants.mjs`](tests/audit-prompt-invariants.mjs)(157 条断言)——把"历史上真实发生过的提示词/工具面缺陷类别"逐条编码成静态不变式(缩写工具名、把忠实性缺陷投成 0、`defect` 只写在提示词里没实现、回执契约缺 `defect`、无 note 放行、字段名错、`off` 档回执仍能写状态、语料不确定、探针缺失、**工具的封闭 schema 收不下它自己文档里的参数**、**schema 声明了参数层却静默丢弃的键**)。加 `--self-probe` 会在内存里注入这些缺陷形状,要求对应不变式**变红**、未变异的对照跑**仍为绿**(5/5);脚本自身另带 X5–X8b 六条自检(注释扫描器必须认正则字面量——包括 `return /…/ ` 这种**关键字后面**的正则——字符串里的 `//` 必须保留、抹注释不改变行结构,以及"四套源码抹掉注释后仍必须能被 `node --check` 解析"这条解析级判据)
|
package/docs/AUDIT-CHECKLIST.md
CHANGED
|
@@ -216,8 +216,8 @@ v2/v3/v4/v5 是**同构实现**(同一份契约、四份独立代码,刻意
|
|
|
216
216
|
② 建一个 `git worktree` 拿改动前的检出,同一套件在两种布局各跑一遍,归一化路径/临时目录/耗时后
|
|
217
217
|
**逐行比对**(`_oneoff/layout-invariance.mjs`);③ 相对链接扫描 0 失效(`_oneoff/scan-links.mjs`)。
|
|
218
218
|
- [ ] 讲"全套件 / 门禁 / 多少次全绿"时,是否区分了**随包发布面**与**仓库**?`package.json` 的 `files`
|
|
219
|
-
只发 `tests/` 的 **
|
|
220
|
-
当前 **
|
|
219
|
+
只发 `tests/` 的 **71** 项(**文件计数**;其中 `.test.mjs` **18** 个),完整门禁(`node tests/run-tests.mjs`,
|
|
220
|
+
当前 **106 项作业(job count)= 44 套件 + 62 探针/变体**,由 `node tests/run-tests.mjs --counts` 派生)只在开发检出里成立。两边的清单见
|
|
221
221
|
`docs/test-timing.md` §1.1;发布物里的 runner 会把缺失/跳过项**打印出来**(不会静默少跑),
|
|
222
222
|
所以"安装用户照文档跑得到全套件"这类说法必须避免。
|
|
223
223
|
- [ ] **runner 本身也要跑一遍**:直接跑套件通过 ≠ 并行 runner 通过(2.3.13 就出现过
|
|
@@ -545,6 +545,9 @@ DSH 的 **agent preset 交付方式**在 0.1.6 → 0.1.7 之间换过一次,
|
|
|
545
545
|
| **中断语义(SLV P3,task-14)** | "**宿主拒绝中断**"这条 `catch` 分支必须保持**具名失败**与**可行动指引**:`code='VIBE_MATH_INTERRUPT_FAILED'` + `next{tool='vibe_math_abort',hint}`(§19/D1 此前只覆盖"未知 child/空 childId/决策诊断")| **随包** `node tests/v2v3-interrupt.mutants.mjs`(**2/2** 具名红:v2/v3 各把产品里的 `VIBE_MATH_INTERRUPT_FAILED` 改名 ⇒ 对应 `★ [P3/v2]`/`★ [P3/v3]` 断言按名变红,实测 `"code":"VIBE_MATH_INTERRUPT_RENAMED"`);两套件各自 `node tests/math-computation-v2.test.mjs`/`-v3` 绿。**已知未覆盖**:P3 的"**调用方被丢弃**(中断后不再重试该调用)"属**宿主层行为**,随包套件不可观测 ⇒ 记为需真机 |
|
|
546
546
|
| **临时目录卫生(task-12)** | 被杀死的套件会泄漏临时目录(实测 `%TEMP%` 顶层 24 万项、其中我们 24.5 万项)⇒ 门禁启动时清扫**陈旧**目录:仅 `os.tmpdir()` **顶层** + 前缀**从套件自动提取**(88 个)+ `mtime` 早于阈值(默认 6 h);并把临时根**优先指向 `D:\_tmp`**(`os.tmpdir()` 自动跟随 ⇒ 零产品改动)| **随包** `node scripts/clean-temp.mjs --self-test`(四条:新鲜目录绝不清理/陈旧且前缀匹配才清理/只匹配精确前缀/前缀表来自套件)+ `node tests/temp-hygiene.mutants.mjs`(**2/2** 具名红:去掉年龄阈值 ⇒"新鲜目录不得被删"按名红、去掉前缀判断 ⇒"只匹配精确前缀"按名红);`--dry-run` 实测"扫 160,923 项 ⇒ 计划 144,404 项" |
|
|
547
547
|
|
|
548
|
+
| **发布前检查(可执行化)** | 版本**三处一致**(`package.json`/lock 顶层/`packages[""]`)、当前版本的中英两份发布说明存在且**章节顺序符合模板**、两份都登记进 `files[]`、`compatNote` 提到当前版本、tarball **声明了但缺失 = 0**、开发专用文件(`.gitattributes`)**不随包**、包内**无 CRLF** | **随包** `node scripts/release-check.mjs`(对 2.8.0 实跑 `ALL CHECKS PASSED`,扫 179 个文本文件)+ `--self-test`(**15** 条)+ `node tests/release-check.mutants.mjs`(**4/4** 具名红:分别去掉版本一致性/章节数量/章节顺序/CRLF 扫描 ⇒ 对应自检**按名**变红)。发布后加 `--registry` 比对线上 `dist.shasum` 与本地 tarball |
|
|
549
|
+
| **v4 交付物必须说明编译结果** | 论文正文在**编译之前**组装,因此"局限与未决"里那两条依赖 `ps.compile` 的 bullet 只在**重入**时才有值 ⇒ 交付的 `paper.md`/`paper.tex` 必须**各自写明**"未检测到引擎(只产出 tex+md)"或"PDF 编译失败",且**恰好一次**(finalize 可重入 ⇒ 幂等;`.tex` 以注释插在 `\end{document}` 之前)| **随包** `node tests/v4-final-paper.test.mjs`(`★ [deliverable/v4] … (once each; md=1 tex=1)`)+ `node tests/v4-final-paper.mutants.mjs`(**3/3** 具名红:把中文说明置空 ⇒ 该断言按名红,实测 `md=0 tex=0`)|
|
|
550
|
+
|
|
548
551
|
> **运行方式(本轮核实)**:`tests/run-tests.mjs:93-110` 收集 `tests/` 下**每一个** `.mjs`(仅跳过自身、`NEEDS_ARGS` 清单与 `replacedBare` 变体),因此 `audit-path-discipline.mjs` 是**门禁内的 probe**,不是"只能手动跑";未随包发布的守卫(见 `package.json#files` 的 tests 子集)在安装树里不可运行。
|
|
549
552
|
|
|
550
553
|
|
|
@@ -721,7 +724,7 @@ DSH 的 **agent preset 交付方式**在 0.1.6 → 0.1.7 之间换过一次,
|
|
|
721
724
|
### README/计数一致性(D1):数字必须**派生**,不许手打
|
|
722
725
|
|
|
723
726
|
- **权威来源**:`node tests/run-tests.mjs --counts`(门禁自己那份 job 列表)+ `package.json#files`(随包 `tests/*.mjs` 数)。文档里的数字由 **`node scripts/update-doc-counts.mjs`** 生成(`--check` 只检查不改);新增测试文件会让守卫变红,**修法是跑一次 updater**,因此数字再也不可能被手打。
|
|
724
|
-
- **计数(实测)**:`TOTAL
|
|
727
|
+
- **计数(实测)**:`TOTAL 106`(44 套件 + 62 探针/变体,**作业计数/job count**,不是文件计数);随包 `tests/*.mjs` = 71(**文件计数**:18 个套件 + 53 个探针/脚本)。
|
|
725
728
|
- **怎么让它红一次(in-repo)**:`tests/audit-readme-counts.mutants.mjs` —— 基线:守卫绿;**★ 篡改 README 里被引用的派生总数**(走 `COUNTS_README` seam,指向**绝对路径**的副本)⇒ 守卫**具名红**("README.md quotes the DERIVED totals")。守卫本体:`tests/audit-readme-counts.mjs`(**19+** 条断言:README/EN/test-timing/checklist 的派生形状、旧读数的消失,以及 **F-C 的全 `docs/**`(live 文档)`TOTAL <n>` 扫描** —— 每个 occurrence 必须等于 `--counts` 的派生值,冻结的 `docs/release-notes/**` 按名排除并在输出里报数)。
|
|
726
729
|
- **touch-anchor(claim-vs-tree 用)**:`--counts`、`update-doc-counts.mjs`、`README COUNTS:`、`README.md quotes the DERIVED totals`。
|
|
727
730
|
|
|
@@ -8,6 +8,7 @@ Chinese counterpart of this index is [`README.md`](./README.md) (中文索引).
|
|
|
8
8
|
|
|
9
9
|
| Version | Key change | Notes |
|
|
10
10
|
|---|---|---|
|
|
11
|
+
| 2.8.1 | Patch: v4's delivered `paper.md` and `paper.tex` now each state the compile outcome (no engine detected → tex+md only, or PDF compilation failed with tex/md kept), **exactly once** and idempotently — resolving known limitation #3 of the 2.8.0 notes; added `scripts/release-check.mjs`, which runs the pre-release checks as one command (version triple, section order of both notes, manifest registration, declared-but-missing = 0, repository-only files not shipped, no CRLF in the package). | [中文](./RELEASE-NOTES-2.8.1.md) · [English](./RELEASE-NOTES-2.8.1.en.md) |
|
|
11
12
|
| 2.8.0 | Feature: an explicit `paperLatexCommand` now means "use only this" (v4/v5 no longer fall back silently), the missing-engine warning is actionable and `triedPaths` is recorded, and all four presets share one byte-identical missing-engine guidance block; the test gate is steadier (default concurrency 2, stale scratch swept at start-up, read-only warning for suspected leftover processes); repository line endings are unified on LF (byte-reproducible npm package). | [中文](./RELEASE-NOTES-2.8.0.md) · [English](./RELEASE-NOTES-2.8.0.en.md) |
|
|
12
13
|
| 2.7.3 | Patch: the v5 mailbox acknowledges **only after a successful send** (unacknowledged mail is no longer lost), round numbering is correct on founding and on resume, a failed required paper-artifact write is named once with an idempotent refill, a refused task CAS leaves no trace, the member path contract and v4's Lean library-index write failure are named; eight stale "ack first" passages were corrected and a prose↔code pairing guard now prevents them from returning. | [中文](./RELEASE-NOTES-2.7.3.md) · [English](./RELEASE-NOTES-2.7.3.en.md) |
|
|
13
14
|
| 2.7.2 | Patch: completes engine discovery and evidence on real hosts (engines installed but not on `PATH` are found, no unrunnable install command, `probe` names the absent engines, complete timeout evidence, the constraint policy is stated, archive identity and participant-set semantics are checkable). | [中文](./RELEASE-NOTES-2.7.2.md) · [English](./RELEASE-NOTES-2.7.2.en.md) |
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
| 版本 | 关键变化 | 说明 |
|
|
9
9
|
|---|---|---|
|
|
10
|
+
| 2.8.1 | 补丁:v4 交付的 `paper.md`/`paper.tex` 现在各自写明编译结果(未检测到引擎 ⇒ 只产出 tex+md,或 PDF 编译失败并保留 tex/md),**恰好一次**且幂等 ⇒ 2.8.0 说明里的「已知限制」第 3 条已解决;新增 `scripts/release-check.mjs` 把发布前逐项核对做成一条命令(版本三处一致/两份说明章节顺序/清单登记/声明缺失=0/开发专用文件不随包/包内无 CRLF)。 | [中文](./RELEASE-NOTES-2.8.1.md) · [English](./RELEASE-NOTES-2.8.1.en.md) |
|
|
10
11
|
| 2.8.0 | 功能版:显式 `paperLatexCommand` 改为「指定即只用它」(v4/v5 不再静默回落)、检测不到引擎的警告可行动并新增 `triedPaths`、四套预设新增逐字一致的引擎缺失指引;测试门禁更稳(默认并发 2、自动清扫陈旧临时目录、疑似遗留进程只读告警);仓库行尾统一为 LF(npm 包逐字节可复现)。 | [中文](./RELEASE-NOTES-2.8.0.md) · [English](./RELEASE-NOTES-2.8.0.en.md) |
|
|
11
12
|
| 2.7.3 | 补丁:v5 邮袋改为「发送成功后才确认」(未确认消息不再丢失)、入会与恢复的轮次编号正确、论文必需产物写失败具名告警且补偿幂等、任务 CAS 被拒不留痕、成员库路径契约与 v4 Lean 索引写失败具名;文档中 8 处旧顺序已改正并新增文↔码配对守卫。 | [中文](./RELEASE-NOTES-2.7.3.md) · [English](./RELEASE-NOTES-2.7.3.en.md) |
|
|
12
13
|
| 2.7.2 | 补丁:真实引擎发现与证据补齐(不在 PATH 的引擎可发现、不给出跑不了的安装命令、`probe` 点名缺席引擎、超时证据完整、约束口径明说、归档身份与名册口径可核对)。 | [中文](./RELEASE-NOTES-2.7.2.md) · [English](./RELEASE-NOTES-2.7.2.en.md) |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# dsh-vibe-math 2.8.1 — Release Notes
|
|
2
|
+
|
|
3
|
+
> Previous release: 2.8.0. This is a **patch release**: it fixes the "known limitation" that 2.8.0 honestly disclosed — v4's final paper did **not** state that no LaTeX engine was detected, or that PDF compilation failed — and it turns the pre-release checklist into one command. Parameter names and defaults, data formats and the directory layout are **unchanged**, the DSH support window is unchanged, and **no migration is needed**.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
- v4's delivered `paper.md` and `paper.tex` now **each state the compile outcome**: no engine detected (tex+md only), or PDF compilation failed with tex/md kept.
|
|
8
|
+
- The note is written **exactly once** (finalisation can be re-entered, so it is idempotent) and does not change the paper's section structure or any other field.
|
|
9
|
+
- Known limitation #3 of the 2.8.0 notes (v4's finalisation note could be empty) is **resolved**.
|
|
10
|
+
- Added `scripts/release-check.mjs`: the pre-release checklist as a single command.
|
|
11
|
+
|
|
12
|
+
## Added
|
|
13
|
+
|
|
14
|
+
- `scripts/release-check.mjs`: checks the **version triple**, the **section order** of both release notes for the current version, that both are listed in `package.json#files`, that `dsh.compatNote` names the current version, that `npm pack` has **declared-but-missing = 0**, that repository-only files are **not** shipped, and that the packaged text contains **no CRLF**. Switches: `--skip-pack` (static checks only), `--registry` (after publishing: compare the registry `dist.shasum` with the local tarball), `--self-test` (prove the predicates are not vacuous).
|
|
15
|
+
|
|
16
|
+
## Changed
|
|
17
|
+
|
|
18
|
+
- v4's paper deliverables carry one extra outcome note (see "Fixed"). Apart from that note, the paper's section order, fixed wording and other fields are unchanged.
|
|
19
|
+
|
|
20
|
+
## Fixed
|
|
21
|
+
|
|
22
|
+
- Symptom: after reading `paper.md` / `paper.tex`, a v4 user still could not tell **why no PDF was produced** — only `paper.log.md` had a line. Cause: the paper body is composed **before** compilation, so the two body items that depend on the compile result were **always empty** in the normal flow. Finalisation now writes the outcome into the deliverables **after** compiling: a bullet at the end of `paper.md`, and a comment line (`% …`) inserted before `\end{document}` in `paper.tex`, **once each** (re-entry does not duplicate it). Guards: `tests/v4-final-paper.test.mjs` (`★ [deliverable/v4] … (once each; md=1 tex=1)`), `tests/v4-final-paper.mutants.mjs` (**3/3** named reds: blanking the Chinese note reddens that assertion by name, measured `md=0 tex=0`).
|
|
23
|
+
- Release-process defect (tooling): the pre-release checks had to be done by hand and were easy to miss. `scripts/release-check.mjs` now does them in one command, with **4** single-site mutations proving the checker is not vacuous. Guard: `tests/release-check.mutants.mjs` (**4/4** named reds).
|
|
24
|
+
|
|
25
|
+
## Compatibility & Migration
|
|
26
|
+
|
|
27
|
+
- Breaking changes: **none**.
|
|
28
|
+
- Additive only: one outcome line in the deliverables; one shipped script.
|
|
29
|
+
- Migration: none needed; existing `paper.meta.json` files and the directory layout are unchanged.
|
|
30
|
+
- The DSH support window is unchanged (see `engines.dsh` in `package.json`).
|
|
31
|
+
|
|
32
|
+
## Known Limitations
|
|
33
|
+
|
|
34
|
+
- **TeX installed in a non-standard location is still not auto-discovered**: the default probe covers PATH and the documented common install roots; use an absolute path in `paperLatexCommand` otherwise.
|
|
35
|
+
- The shipped suites **do not cover host-level behaviour** (for example "the caller is dropped after an interrupt"); that needs a real host.
|
|
36
|
+
- On machines without Lean / LaTeX, the related suites **skip loudly** rather than passing silently.
|
|
37
|
+
- This version did not re-run a full research workflow on a real host.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
- `node tests/run-tests.mjs` — all green (**106** items: 44 suites + 62 probes).
|
|
42
|
+
- `node scripts/update-doc-counts.mjs --check` — exits 0 (documented derived counts match the repository).
|
|
43
|
+
- `node scripts/release-check.mjs` — exits 0; after publishing, `node scripts/release-check.mjs --registry` also compares the registry shasum with the local tarball byte for byte.
|
|
44
|
+
- v4 deliverable note: `node tests/v4-final-paper.test.mjs`, `node tests/v4-final-paper.mutants.mjs` (3/3 named reds).
|
|
45
|
+
- The checker itself: `node scripts/release-check.mjs --self-test` (15 checks) and `node tests/release-check.mutants.mjs` (4/4 named reds).
|
|
46
|
+
|
|
47
|
+
## Dependencies
|
|
48
|
+
|
|
49
|
+
- No new runtime dependencies; the DSH support window is unchanged.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# dsh-vibe-math 2.8.1 — 发布说明
|
|
2
|
+
|
|
3
|
+
> 上一版本:2.8.0。本版是**补丁版**:修掉 2.8.0 说明里如实写出的那条「已知限制」——v4 的最终文稿此前**不会**说明「本机没有 LaTeX 引擎」或「PDF 编译失败」;并把发布前的逐项核对做成一条命令。参数名与默认值、数据格式与目录结构**未变**,DSH 支撑窗口不变,**无需迁移**。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
- v4 交付的 `paper.md` 与 `paper.tex` 现在**各自写明编译结果**:未检测到引擎(只产出 tex+md),或 PDF 编译失败并已保留 tex/md。
|
|
8
|
+
- 该说明**只写一次**(定稿可重入 ⇒ 幂等),不改变论文的章节结构与其它字段。
|
|
9
|
+
- 2.8.0 说明中的「已知限制」第 3 条(v4 收尾备注可能为空)**已解决**。
|
|
10
|
+
- 新增 `scripts/release-check.mjs`:把发布前必须逐项核对的检查做成一条命令。
|
|
11
|
+
|
|
12
|
+
## 新增
|
|
13
|
+
|
|
14
|
+
- `scripts/release-check.mjs`:核对版本**三处一致**、当前版本两份发布说明的**章节顺序**、两份是否登记进 `package.json#files`、`dsh.compatNote` 是否提到当前版本、`npm pack` 的**声明了但缺失 = 0**、开发专用文件是否**不随包**、包内文本是否**无 CRLF**;开关 `--skip-pack`(只做静态检查)、`--registry`(发布后比对线上 `dist.shasum` 与本地 tarball)、`--self-test`(自证判据非空转)。
|
|
15
|
+
|
|
16
|
+
## 变更
|
|
17
|
+
|
|
18
|
+
- v4 的论文交付物多一条结果说明(细节见「修复」)。除该说明外,论文的章节顺序、固定文案与其它字段均未变。
|
|
19
|
+
|
|
20
|
+
## 修复
|
|
21
|
+
|
|
22
|
+
- 症状:v4 的用户读完 `paper.md` / `paper.tex` 也不知道**PDF 为什么没有产出**——只有 `paper.log.md` 里留了一行。原因:论文正文在**编译之前**组装,因此正文里那两条依赖编译结果的条目在正常流程中**恒空**。现在定稿在编译之后把结果**补写**进交付物:`paper.md` 末尾增一条要点,`paper.tex` 在 `\end{document}` 之前插入一行注释(`% …`),两者**各恰好一次**(重入不重复)。守卫:`tests/v4-final-paper.test.mjs`(`★ [deliverable/v4] … (once each; md=1 tex=1)`)、`tests/v4-final-paper.mutants.mjs`(**3/3** 具名红:把中文说明置空 ⇒ 该断言按名变红,实测 `md=0 tex=0`)。
|
|
23
|
+
- 发布流程缺陷(工具面):发布前的逐项核对此前只能手工做、容易漏。现在由 `scripts/release-check.mjs` 一条命令完成,并由 **4** 条单点变异保证它本身不是空转。守卫:`tests/release-check.mutants.mjs`(**4/4** 具名红)。
|
|
24
|
+
|
|
25
|
+
## 兼容性与迁移
|
|
26
|
+
|
|
27
|
+
- 破坏性变更:**无**。
|
|
28
|
+
- 纯新增:交付物里的一行结果说明;一个随包脚本。
|
|
29
|
+
- 迁移:无需迁移;既有 `paper.meta.json` 与目录结构不变。
|
|
30
|
+
- DSH 支撑窗口不变(见 `package.json` 的 `engines.dsh`)。
|
|
31
|
+
|
|
32
|
+
## 已知限制
|
|
33
|
+
|
|
34
|
+
- **非标准位置的 TeX 仍不会被自动发现**:默认探测只覆盖 PATH 与文档化的常见安装根;装在其它目录请用 `paperLatexCommand` 指绝对路径。
|
|
35
|
+
- 随包套件**不覆盖宿主层行为**(例如「中断后调用方被丢弃」),这类行为需要真机才能观察。
|
|
36
|
+
- 缺少 Lean / LaTeX 等引擎的机器上,相关套件会**响亮跳过**,不会静默通过。
|
|
37
|
+
- 本版未在真机重跑完整研究流程。
|
|
38
|
+
|
|
39
|
+
## 验证方式
|
|
40
|
+
|
|
41
|
+
- `node tests/run-tests.mjs` —— 全部通过(**106** 项:44 套件 + 62 探针)。
|
|
42
|
+
- `node scripts/update-doc-counts.mjs --check` —— 退出 0(文档派生计数与仓库一致)。
|
|
43
|
+
- `node scripts/release-check.mjs` —— 退出 0;发布后 `node scripts/release-check.mjs --registry` 再比对线上 shasum 与本地 tarball 的字节一致性。
|
|
44
|
+
- v4 交付物说明:`node tests/v4-final-paper.test.mjs`、`node tests/v4-final-paper.mutants.mjs`(3/3 具名红)。
|
|
45
|
+
- 发布检查本身:`node scripts/release-check.mjs --self-test`(15 条)与 `node tests/release-check.mutants.mjs`(4/4 具名红)。
|
|
46
|
+
|
|
47
|
+
## 依赖
|
|
48
|
+
|
|
49
|
+
- 无新增运行时依赖;DSH 支撑窗口不变。
|
package/docs/release-playbook.md
CHANGED
|
@@ -40,8 +40,15 @@ node -e "const p=require('./package.json'),l=require('./package-lock.json');cons
|
|
|
40
40
|
node tests/run-tests.mjs # 必须全绿:TOTAL <n> PASS <n> FAIL 0
|
|
41
41
|
node scripts/update-doc-counts.mjs # 让文档里的派生计数收敛
|
|
42
42
|
node scripts/update-doc-counts.mjs --check # 退出 0 = 已收敛
|
|
43
|
+
node scripts/release-check.mjs # 版本三处一致/两份说明与章节顺序/files[] 登记/
|
|
44
|
+
# tarball 声明缺失=0/开发专用文件不随包/包内无 CRLF
|
|
43
45
|
```
|
|
44
46
|
|
|
47
|
+
- **`scripts/release-check.mjs` 是本手册的可执行化部分**:把 §1/§2/§6 的逐项核对做成一条命令(含一次真实
|
|
48
|
+
`npm pack` 与解包扫描)。发布前它必须退出 0;`--skip-pack` 只做静态检查,`--registry` 在发布后比对线上
|
|
49
|
+
`dist.shasum` 与本地 tarball 的字节一致性,`--self-test` 自证判据不是空转(配 `tests/release-check.mutants.mjs`
|
|
50
|
+
的 4 条单点变异,全部按名变红)。
|
|
51
|
+
|
|
45
52
|
- 任何**新增作业**(新套件/新变异族/新 `--self-probe` 变体)都会改变派生计数:先跑 updater,再跑门禁,否则文档计数守卫会按设计变红。
|
|
46
53
|
- **派生工件随源改动一起提交**:改了预设源/规范后,`cordis.patch.yml` 与随包语料等派生文件必须在**同一个提交**里重新生成(生成器脚本以仓库为准),不要拆成两个提交。
|
|
47
54
|
- 改动了文档/计数后**再跑一次** `--check`,确保最后一次改动也收敛。
|
|
@@ -79,6 +86,7 @@ gh release create v<版本> --title "v<版本>" --latest --verify-tag \
|
|
|
79
86
|
```bash
|
|
80
87
|
npm view dsh-vibe-math version dist-tags.latest dist.shasum
|
|
81
88
|
npm pack --dry-run # 或 npm pack 后解开,核对清单
|
|
89
|
+
node scripts/release-check.mjs --registry # 一条命令做完下面 1–4 项(含线上 shasum 与本地 tarball 的比对)
|
|
82
90
|
```
|
|
83
91
|
|
|
84
92
|
逐项确认:
|
package/docs/test-timing.md
CHANGED
|
@@ -51,7 +51,7 @@ node tests/run-tests.mjs --json # 机器可读汇总(含 skippedN
|
|
|
51
51
|
|
|
52
52
|
| 脚本 | 串行(sum) | 并行(wall) | 实测输出 |
|
|
53
53
|
|---|---|---|---|
|
|
54
|
-
| `tests/run-tests.mjs`(**
|
|
54
|
+
| `tests/run-tests.mjs`(**106 项作业**(job count)= 44 套件 + 62 探针/变体;**由 `--counts` 派生**) | ≈ 424 s(历史基线) | **≈ 121 s**(历史基线;当前实测墙钟 ≈ 200 s,并发 4) | `TOTAL 106 PASS 106 FAIL 0 (suites 44 · probes 62)`;实测 `wall 120.7s · sum 424.2s`,关键路径 = `e2e-v4-fixes`(≈ 98 s) |
|
|
55
55
|
| `tests/audit-formal-sensitivity.mjs`(49 探针,自带并发 4;v2 12 / v3 12 / v4 12 / v5 13) | ≈ 1035 s | **260.7 s**(实测,x3.97) | 关键路径 = v2/v3 的 `require` 探针(各 ≈75 s);全 49 条按预期变红、0 问题 |
|
|
56
56
|
| `tests/audit-v5-sensitivity.mjs`(39 探针,串行) | ≈ 101 s | — | 每条 = 一次被测套件重跑 |
|
|
57
57
|
| `tests/audit-persona-sensitivity.mjs`(16 探针) | ≈ 4.4 s | — | 本身很快,不需要并行 |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vibe-math",
|
|
3
3
|
"description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — FOUR agent presets in one install: vibe-math-v2 (probability-driven, classic: qs.json + Propos knowledge base + explorer→solver→review/debate verdict), vibe-math-v3 (THIRD-generation, classic: paper-style Markdown knowledge base with Problems/Progress/Propos/Methods/Verified + planner-agent scheduling that decides the next N actions + universal theory/method invention library + agents write their own Markdown directly via a per-file write lock), and vibe-math-v4 (FOURTH-generation: persistent self-organizing resident subagents that message & meet to decide all tasks, verify only by unanimous consensus, /compact at a context threshold, and stop only when all agree the problem is solved), and vibe-math-v5 (FIFTH-generation research institute: an academician as the organizational centre who decomposes and ASSIGNS work and chairs meetings; permanent researchers who hold the vote and may hire/fire their own temp workers; temp workers with no vote; a group chat and meetings; a durable per-recipient mailbox; a compare-and-set task DAG; and a boolean m-vote consensus rule where an object enters Verified/ only when at least m voting members agree AND every one of them returns exactly 1 or exactly 0). Installing this bundle auto-installs all four presets (v1 was removed at v2.0.0). Each preset also writes a FINAL PAPER by default when its run concludes — and does so BEFORE the run is marked complete: on v4/v5 the team co-authors its parts, merges them, cross-reviews, and the editor named by paperEditor finalises; the output is Paper/<run or institute id>/{paper.md,paper.tex,paper.pdf when a LaTeX engine is detected,paper.meta.json,paper.log.md} (params finalPaper/paperFormat/paperLanguage/paperCompilePdf/paperLatexCommand, plus paperEditor on v4/v5; see docs/final-paper.md).",
|
|
4
|
-
"version": "2.8.
|
|
4
|
+
"version": "2.8.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0",
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
"prompt-corpus-v5/prompt-corpus-v5.md",
|
|
115
115
|
"tests/prompt-v5-integrity.test.mjs",
|
|
116
116
|
"tests/run-tests.mjs",
|
|
117
|
+
"tests/v4-final-paper.test.mjs",
|
|
117
118
|
"tests/v4-final-paper.mutants.mjs",
|
|
118
119
|
"tests/run-tests.mutants.mjs",
|
|
119
120
|
"screenshots.json",
|
|
@@ -186,8 +187,10 @@
|
|
|
186
187
|
"scripts/build-preset-rows.mjs",
|
|
187
188
|
"scripts/update-doc-counts.mjs",
|
|
188
189
|
"scripts/clean-temp.mjs",
|
|
190
|
+
"scripts/release-check.mjs",
|
|
189
191
|
"tests/temp-hygiene.mutants.mjs",
|
|
190
192
|
"tests/v2v3-interrupt.mutants.mjs",
|
|
193
|
+
"tests/release-check.mutants.mjs",
|
|
191
194
|
"preset-declaration.js",
|
|
192
195
|
"tests/audit-preset-rows.test.mjs",
|
|
193
196
|
"docs/COMPAT-AUDIT-ROUND3.md",
|
|
@@ -201,7 +204,9 @@
|
|
|
201
204
|
"docs/release-notes/RELEASE-NOTES-2.7.3.md",
|
|
202
205
|
"docs/release-notes/RELEASE-NOTES-2.7.3.en.md",
|
|
203
206
|
"docs/release-notes/RELEASE-NOTES-2.8.0.md",
|
|
204
|
-
"docs/release-notes/RELEASE-NOTES-2.8.0.en.md"
|
|
207
|
+
"docs/release-notes/RELEASE-NOTES-2.8.0.en.md",
|
|
208
|
+
"docs/release-notes/RELEASE-NOTES-2.8.1.md",
|
|
209
|
+
"docs/release-notes/RELEASE-NOTES-2.8.1.en.md"
|
|
205
210
|
],
|
|
206
211
|
"license": "MIT",
|
|
207
212
|
"keywords": [
|
|
@@ -231,7 +236,7 @@
|
|
|
231
236
|
},
|
|
232
237
|
"minVersion": "0.1.2-alpha.4",
|
|
233
238
|
"testedVersion": "0.2.0-rc.2",
|
|
234
|
-
"compatNote": "v2.8.
|
|
239
|
+
"compatNote": "v2.8.1 补丁版:修掉上一版说明里如实写出的「已知限制」——v4 的最终文稿此前不说明引擎缺失/编译失败。① v4 交付的 paper.md 与 paper.tex 现在各自写明编译结果(未检测到引擎 ⇒ 只产出 tex+md;PDF 编译失败 ⇒ 已保留 tex/md),恰好一次且定稿重入不重复;论文的章节结构与其它字段未变。② 新增随包脚本 scripts/release-check.mjs:把发布前逐项核对做成一条命令(版本三处一致、当前版本两份发布说明的章节顺序与 files[] 登记、compatNote 是否提到当前版本、npm pack 声明缺失=0、开发专用文件不随包、包内无 CRLF),并配 4 条单点变异自证非空转。参数名与默认值、数据格式与目录结构均未变;DSH 支撑窗口不变;无需迁移。",
|
|
235
240
|
"compatibility": {
|
|
236
241
|
"dshReleases": {
|
|
237
242
|
"0.1.2-alpha.4": "compatible",
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* RELEASE CHECK (task-8 follow-up) — the checks a maintainer had to run BY HAND for 2.8.0, in one command.
|
|
4
|
+
*
|
|
5
|
+
* `docs/release-playbook.md` lists them as prose across §1/§2/§6; this script makes them mechanical, so the
|
|
6
|
+
* next release cannot ship a drifted version, a missing release note, an unregistered note, a manifest that
|
|
7
|
+
* lies about the tarball, or CRLF line endings inside the package.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/release-check.mjs # static checks + a real `npm pack` + tarball EOL scan
|
|
11
|
+
* node scripts/release-check.mjs --skip-pack # static checks only (no packing, no temp extraction)
|
|
12
|
+
* node scripts/release-check.mjs --registry # ALSO compare the published shasum (post-publish only)
|
|
13
|
+
* node scripts/release-check.mjs --self-test # prove the predicates below are not vacuous
|
|
14
|
+
*
|
|
15
|
+
* Exit 0 = every check passed. Exit 1 = at least one FAIL (printed with a `FAIL ` prefix).
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs'
|
|
18
|
+
import { execFileSync } from 'node:child_process'
|
|
19
|
+
import { createHash } from 'node:crypto'
|
|
20
|
+
import { dirname, join } from 'node:path'
|
|
21
|
+
import { fileURLToPath } from 'node:url'
|
|
22
|
+
|
|
23
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
24
|
+
const REPO = join(HERE, '..')
|
|
25
|
+
const TMP = process.env.RELEASE_CHECK_TMP || join(process.env.TEMP || process.env.TMPDIR || '/tmp', 'release-check')
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------- pure predicates (fixture-testable)
|
|
28
|
+
/** The version must agree in all three places the market guard reads (playbook §1). */
|
|
29
|
+
export function versionProblems({ pkgVersion, lockTop, lockEmpty }) {
|
|
30
|
+
const out = []
|
|
31
|
+
if (!pkgVersion) out.push('package.json has no version')
|
|
32
|
+
if (lockTop !== pkgVersion) out.push('package-lock.json top-level version ' + lockTop + ' != package.json ' + pkgVersion)
|
|
33
|
+
if (lockEmpty !== pkgVersion) out.push('package-lock.json packages[""].version ' + lockEmpty + ' != package.json ' + pkgVersion)
|
|
34
|
+
return out
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Release notes must carry the template's sections, in the template's order (playbook §1 / TEMPLATE.md). */
|
|
38
|
+
export const SECTIONS = {
|
|
39
|
+
zh: ['## 概述', '## 新增', '## 变更', '## 修复', '## 兼容性与迁移', '## 已知限制', '## 验证方式', '## 依赖'],
|
|
40
|
+
en: ['## Overview', '## Added', '## Changed', '## Fixed', '## Compatibility & Migration', '## Known Limitations', '## Verification', '## Dependencies'],
|
|
41
|
+
}
|
|
42
|
+
export function sectionProblems(headings, lang) {
|
|
43
|
+
const want = SECTIONS[lang]
|
|
44
|
+
if (!want) return ['unknown language ' + lang]
|
|
45
|
+
const got = (headings || []).map((h) => String(h).trim())
|
|
46
|
+
const out = []
|
|
47
|
+
if (got.length !== want.length) out.push('expected ' + want.length + ' sections, found ' + got.length)
|
|
48
|
+
for (let i = 0; i < want.length; i++) if (got[i] !== want[i]) out.push('section ' + (i + 1) + ' is ' + JSON.stringify(got[i]) + ', expected ' + JSON.stringify(want[i]))
|
|
49
|
+
return out
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Every declared file must exist in the packed tarball (playbook §2/§6.2). */
|
|
53
|
+
export function manifestProblems(declared, packed) {
|
|
54
|
+
const have = new Set(packed || [])
|
|
55
|
+
return (declared || []).filter((f) => !have.has(f)).map((f) => 'declared but missing from the tarball: ' + f)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Nothing may ship that is repository-only tooling (playbook §6.4). */
|
|
59
|
+
export function devOnlyProblems(packed, devOnly = ['.gitattributes']) {
|
|
60
|
+
const have = new Set(packed || [])
|
|
61
|
+
return devOnly.filter((f) => have.has(f)).map((f) => 'repository-only file shipped in the tarball: ' + f)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Packaged text must be LF-only so the tarball is byte-reproducible (playbook §7). */
|
|
65
|
+
export function crlfProblems(files) {
|
|
66
|
+
const out = []
|
|
67
|
+
for (const { path, crlf } of files || []) if (Number(crlf) > 0) out.push('CRLF in the packaged file: ' + path + ' (' + crlf + ' lines)')
|
|
68
|
+
return out
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `compatNote` is user-visible and must name the version being released (playbook §2). */
|
|
72
|
+
export function compatNoteProblems(compatNote, version) {
|
|
73
|
+
if (!compatNote) return ['dsh.compatNote is missing']
|
|
74
|
+
return String(compatNote).includes(version) ? [] : ['dsh.compatNote does not mention the current version ' + version]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------- self-test (the family mutates these)
|
|
78
|
+
if (process.argv.includes('--self-test')) {
|
|
79
|
+
const failures = []
|
|
80
|
+
const check = (cond, label) => { console.log((cond ? 'PASS ' : 'FAIL - ') + label); if (!cond) failures.push(label) }
|
|
81
|
+
|
|
82
|
+
check(versionProblems({ pkgVersion: '9.9.9', lockTop: '9.9.9', lockEmpty: '9.9.9' }).length === 0,
|
|
83
|
+
'RELEASE-CHECK: a consistent version triple passes')
|
|
84
|
+
check(versionProblems({ pkgVersion: '9.9.9', lockTop: '9.9.8', lockEmpty: '9.9.9' }).some((p) => /top-level/.test(p)),
|
|
85
|
+
'RELEASE-CHECK: a drifted lock top-level version is caught')
|
|
86
|
+
check(versionProblems({ pkgVersion: '9.9.9', lockTop: '9.9.9', lockEmpty: '9.9.8' }).some((p) => /packages/.test(p)),
|
|
87
|
+
'RELEASE-CHECK: a drifted packages[""] version is caught')
|
|
88
|
+
|
|
89
|
+
check(sectionProblems(SECTIONS.zh, 'zh').length === 0 && sectionProblems(SECTIONS.en, 'en').length === 0,
|
|
90
|
+
'RELEASE-CHECK: the canonical section lists pass their own check')
|
|
91
|
+
check(sectionProblems(['## 概述', '## 新增'], 'zh').some((p) => /expected 8 sections/.test(p)),
|
|
92
|
+
'RELEASE-CHECK: a truncated note is caught by its section count')
|
|
93
|
+
check(sectionProblems([...SECTIONS.en].reverse(), 'en').length > 0, 'RELEASE-CHECK: a reordered note is caught')
|
|
94
|
+
|
|
95
|
+
check(manifestProblems(['a.js', 'b.md'], ['a.js', 'b.md', 'package.json']).length === 0, 'RELEASE-CHECK: a fully packed manifest passes')
|
|
96
|
+
check(manifestProblems(['a.js', 'gone.md'], ['a.js']).length === 1, 'RELEASE-CHECK: a declared-but-missing file is caught')
|
|
97
|
+
|
|
98
|
+
check(devOnlyProblems(['README.md'], ['.gitattributes']).length === 0, 'RELEASE-CHECK: repository-only tooling stays out')
|
|
99
|
+
check(devOnlyProblems(['.gitattributes'], ['.gitattributes']).length === 1, 'RELEASE-CHECK: a shipped repository-only file is caught')
|
|
100
|
+
|
|
101
|
+
check(crlfProblems([{ path: 'a.js', crlf: 0 }]).length === 0, 'RELEASE-CHECK: LF-only content passes')
|
|
102
|
+
check(crlfProblems([{ path: 'a.js', crlf: 3 }]).length === 1, 'RELEASE-CHECK: CRLF inside the package is caught')
|
|
103
|
+
|
|
104
|
+
check(compatNoteProblems('v9.9.9 功能版:…', '9.9.9').length === 0, 'RELEASE-CHECK: a compatNote naming the version passes')
|
|
105
|
+
check(compatNoteProblems('v9.9.8 补丁版:…', '9.9.9').length === 1, 'RELEASE-CHECK: a stale compatNote is caught')
|
|
106
|
+
|
|
107
|
+
console.log('release-check self-test: ' + (failures.length ? failures.length + ' failure(s)' : 'all checks passed'))
|
|
108
|
+
process.exit(failures.length ? 1 : 0)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------- the real run
|
|
112
|
+
const argv = process.argv.slice(2)
|
|
113
|
+
const has = (n) => argv.includes('--' + n)
|
|
114
|
+
const problems = []
|
|
115
|
+
const fail = (p) => { for (const x of p) problems.push(x) }
|
|
116
|
+
const readJson = (rel) => JSON.parse(readFileSync(join(REPO, rel), 'utf8'))
|
|
117
|
+
|
|
118
|
+
const pkg = readJson('package.json')
|
|
119
|
+
const lock = readJson('package-lock.json')
|
|
120
|
+
const version = pkg.version
|
|
121
|
+
console.log('release-check: ' + pkg.name + '@' + version)
|
|
122
|
+
|
|
123
|
+
fail(versionProblems({ pkgVersion: version, lockTop: lock.version, lockEmpty: ((lock.packages || {})[''] || {}).version }))
|
|
124
|
+
fail(compatNoteProblems((pkg.dsh || {}).compatNote, version))
|
|
125
|
+
|
|
126
|
+
const notes = { zh: 'docs/release-notes/RELEASE-NOTES-' + version + '.md', en: 'docs/release-notes/RELEASE-NOTES-' + version + '.en.md' }
|
|
127
|
+
for (const [lang, rel] of Object.entries(notes)) {
|
|
128
|
+
if (!existsSync(join(REPO, rel))) { problems.push('missing release note (' + lang + '): ' + rel); continue }
|
|
129
|
+
const text = readFileSync(join(REPO, rel), 'utf8')
|
|
130
|
+
fail(sectionProblems((text.match(/^## .*$/gm) || []), lang).map((p) => rel + ': ' + p))
|
|
131
|
+
if (!(pkg.files || []).includes(rel)) problems.push('release note not listed in package.json#files: ' + rel)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (has('skip-pack')) {
|
|
135
|
+
console.log('release-check: --skip-pack, skipping the tarball checks')
|
|
136
|
+
} else {
|
|
137
|
+
rmSync(TMP, { recursive: true, force: true })
|
|
138
|
+
mkdirSync(TMP, { recursive: true })
|
|
139
|
+
const out = execFileSync('npm', ['pack', '--dry-run', '--json'], { cwd: REPO, encoding: 'utf8', shell: true })
|
|
140
|
+
const packed = JSON.parse(out)[0].files.map((f) => f.path)
|
|
141
|
+
console.log('release-check: tarball would contain ' + packed.length + ' files; declared ' + (pkg.files || []).length)
|
|
142
|
+
fail(manifestProblems(pkg.files, packed))
|
|
143
|
+
fail(devOnlyProblems(packed, ['.gitattributes']))
|
|
144
|
+
|
|
145
|
+
// Real pack + extract, so the EOL check reads the bytes a user would download.
|
|
146
|
+
const tgzName = String(execFileSync('npm', ['pack', '--pack-destination', TMP], { cwd: REPO, encoding: 'utf8', shell: true })).trim().split(/\r?\n/).pop()
|
|
147
|
+
const tgz = join(TMP, tgzName)
|
|
148
|
+
if (!existsSync(tgz)) {
|
|
149
|
+
problems.push('npm pack produced no tarball at ' + tgz)
|
|
150
|
+
} else {
|
|
151
|
+
execFileSync('tar', ['-xzf', tgz, '-C', TMP], { stdio: 'pipe' })
|
|
152
|
+
const root = join(TMP, 'package')
|
|
153
|
+
const eol = []
|
|
154
|
+
const walk = (d, prefix) => {
|
|
155
|
+
for (const e of readdirSync(d, { withFileTypes: true })) {
|
|
156
|
+
const p = join(d, e.name)
|
|
157
|
+
if (e.isDirectory()) { walk(p, prefix + e.name + '/'); continue }
|
|
158
|
+
if (!/\.(js|mjs|cjs|json|md|yml|yaml|txt|svg)$/.test(e.name)) continue // binaries are not text
|
|
159
|
+
const text = readFileSync(p, 'utf8')
|
|
160
|
+
eol.push({ path: prefix + e.name, crlf: (text.match(/\r\n/g) || []).length })
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
walk(root, '')
|
|
164
|
+
fail(crlfProblems(eol))
|
|
165
|
+
const sha1 = createHash('sha1').update(readFileSync(tgz)).digest('hex')
|
|
166
|
+
console.log('release-check: local tarball sha1 = ' + sha1 + ' (' + eol.length + ' text files scanned for CRLF)')
|
|
167
|
+
if (has('registry')) {
|
|
168
|
+
try {
|
|
169
|
+
const published = String(execFileSync('npm', ['view', pkg.name + '@' + version, 'dist.shasum'], { cwd: REPO, encoding: 'utf8', shell: true })).trim()
|
|
170
|
+
if (published !== sha1) problems.push('registry dist.shasum ' + published + ' != local tarball sha1 ' + sha1)
|
|
171
|
+
else console.log('release-check: registry shasum matches the local tarball (byte-reproducible)')
|
|
172
|
+
} catch (e) { problems.push('registry lookup failed: ' + String((e && e.message) || e).slice(0, 120)) }
|
|
173
|
+
}
|
|
174
|
+
rmSync(TMP, { recursive: true, force: true })
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log('')
|
|
179
|
+
for (const p of problems) console.log('FAIL ' + p)
|
|
180
|
+
console.log(problems.length ? 'release-check: ' + problems.length + ' problem(s)' : 'release-check: ALL CHECKS PASSED')
|
|
181
|
+
process.exit(problems.length ? 1 : 0)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Falsifiability driver for scripts/release-check.mjs: a SINGLE-SITE edit must turn its own guard
|
|
2
|
+
// (`--self-test`) NAMED-red, while the unmutated control stays green. The mutated copy is written as a
|
|
3
|
+
// dot-sibling inside scripts/ so relative paths still resolve, and it is always removed in `finally`.
|
|
4
|
+
import { spawnSync } from 'node:child_process'
|
|
5
|
+
import { readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
|
+
import { dirname, join } from 'node:path'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
|
|
9
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const REPO = join(HERE, '..')
|
|
11
|
+
const SRC = join(REPO, 'scripts', 'release-check.mjs')
|
|
12
|
+
|
|
13
|
+
const FAMILIES = [
|
|
14
|
+
{
|
|
15
|
+
// Without this, a lockfile that drifted away from package.json would ship and the market guard would
|
|
16
|
+
// be the only thing that notices.
|
|
17
|
+
name: 'the version-triple check is dropped (a drifted lockfile would ship)',
|
|
18
|
+
from: 'if (lockTop !== pkgVersion) out.push(',
|
|
19
|
+
to: 'if (false) out.push(',
|
|
20
|
+
expect: /drifted lock top-level version is caught/,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
// Without this, a release note missing half its sections would ship.
|
|
24
|
+
name: 'the section-count check is dropped (a truncated release note would ship)',
|
|
25
|
+
from: 'if (got.length !== want.length) out.push(',
|
|
26
|
+
to: 'if (false) out.push(',
|
|
27
|
+
expect: /truncated note is caught by its section count/,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
// Without this, a note whose sections are in the wrong order would ship.
|
|
31
|
+
name: 'the section-order check is dropped (a reordered release note would ship)',
|
|
32
|
+
from: 'if (got[i] !== want[i]) out.push(',
|
|
33
|
+
to: 'if (false) out.push(',
|
|
34
|
+
expect: /reordered note is caught/,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
// Without this, a package containing CRLF would ship - the exact defect this whole line of work fixed.
|
|
38
|
+
name: 'the CRLF scan is dropped (a CRLF package would ship)',
|
|
39
|
+
from: 'if (Number(crlf) > 0) out.push(',
|
|
40
|
+
to: 'if (false) out.push(',
|
|
41
|
+
expect: /CRLF inside the package is caught/,
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
function runSelftest(scriptPath) {
|
|
46
|
+
const r = spawnSync(process.execPath, [scriptPath, '--self-test'], { cwd: REPO, encoding: 'utf8', timeout: 60000 })
|
|
47
|
+
return { code: r.status, out: String(r.stdout || '') + String(r.stderr || '') }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const control = runSelftest(SRC)
|
|
51
|
+
console.log('control (unmutated release-check): exit=' + control.code + (control.code === 0 ? ' green, as required' : ' NOT GREEN'))
|
|
52
|
+
if (control.code !== 0) console.log(control.out.split(/\r?\n/).filter((l) => /FAIL/.test(l)).slice(0, 4).join('\n'))
|
|
53
|
+
|
|
54
|
+
let red = 0
|
|
55
|
+
for (const f of FAMILIES) {
|
|
56
|
+
const dest = join(REPO, 'scripts', '.tmp-release-check-' + Math.random().toString(36).slice(2, 10) + '.mjs')
|
|
57
|
+
let ok = false
|
|
58
|
+
try {
|
|
59
|
+
let text = readFileSync(SRC, 'utf8').replace(/\r\n?/g, '\n')
|
|
60
|
+
if (/\r/.test(text)) { console.error('SETUP-FAIL - the anchor source was not EOL-normalised (a lone CR leaked in)'); process.exit(1) }
|
|
61
|
+
const hits = text.split(f.from).length - 1
|
|
62
|
+
if (hits !== 1) { console.error('SETUP-FAIL - anchor ' + JSON.stringify(f.from) + ' matched ' + hits + ' time(s), expected exactly 1'); process.exit(1) }
|
|
63
|
+
writeFileSync(dest, text.replace(f.from, f.to), 'utf8')
|
|
64
|
+
const r = runSelftest(dest)
|
|
65
|
+
const named = r.out.split(/\r?\n/).filter((l) => /^\s*FAIL - /.test(l) && f.expect.test(l)).map((l) => l.trim())
|
|
66
|
+
ok = r.code !== 0 && named.length > 0
|
|
67
|
+
console.log((ok ? ' ok - ' : ' FAIL - ') + f.name + ' [exit=' + r.code + ']' + (named.length ? ' :: ' + named[0].slice(0, 140) : ' :: no named red (exit=' + r.code + ')'))
|
|
68
|
+
if (ok) red++
|
|
69
|
+
} finally {
|
|
70
|
+
try { rmSync(dest, { force: true }) } catch (e) { /* best effort */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log('')
|
|
75
|
+
console.log('mutant families reddening the release-check guard by name: ' + red + '/' + FAMILIES.length)
|
|
76
|
+
console.log('control green: ' + (control.code === 0))
|
|
77
|
+
if (red !== FAMILIES.length || control.code !== 0) process.exit(1)
|
|
78
|
+
console.log('ALL MUTANTS RED AS REQUIRED')
|
|
@@ -95,6 +95,14 @@ const FAMILIES = [
|
|
|
95
95
|
to: 'const order=(prefer?[prefer]:[]).concat(base.filter(x=>x!==prefer))',
|
|
96
96
|
expect: /\[task-10\/v4\]/,
|
|
97
97
|
},
|
|
98
|
+
{
|
|
99
|
+
// The delivered artifacts must state WHY no PDF was produced (the body is composed BEFORE compilation,
|
|
100
|
+
// so this note is written after it). Blunting the zh note must redden that guard BY NAME.
|
|
101
|
+
name: 'deliverable/v4: the delivered paper stops stating that no engine was detected',
|
|
102
|
+
from: "zh?'本机未检测到 LaTeX 引擎:只产出 tex+md'",
|
|
103
|
+
to: "zh?''",
|
|
104
|
+
expect: /\[deliverable\/v4\]/,
|
|
105
|
+
},
|
|
98
106
|
]
|
|
99
107
|
let red = 0
|
|
100
108
|
for (const f of FAMILIES) { const t0 = Date.now(); const ok = runFamily(f); const ms = Date.now() - t0; if (ok) red++ }
|