dsh-vibe-math 2.8.1 → 2.8.3

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 CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.md)
9
9
  > A set of **agent presets** running inside **DeepSeek Harness** (`vibe-math-v2` / `vibe-math-v3` / `vibe-math-v4` / `vibe-math-v5`),
10
10
  > which use multi-agent collaboration to automatically solve mathematical problems and perform multi-agent cross-verification of the conclusions. All four presets share the foundational capabilities of "**checkpoint resume**,
11
11
  > **mid-run manual intervention**, **progress reporting**, and **natural-language driving**", but adopt four generations of different solving architectures:
12
- > **💡 All four architectures are peers** — `vibe-math-v2` and `vibe-math-v3` are the **classic** architectures (mature, usable, actively maintained); `vibe-math-v4` is the "resident self-organizing collaborative research" architecture and `vibe-math-v5` is the latest "institute system" (both labelled experimental); choose according to your actual needs (see "How to choose" below).
12
+ > **💡 v4/v5 first** — `vibe-math-v4` (the "resident self-organizing collaborative research" architecture) and `vibe-math-v5` (the latest "institute system") are the **current focus of maintenance**; `vibe-math-v2` and `vibe-math-v3` are the **classic / early** architectures, now **architecturally frozen** and receiving **compatibility-only** maintenance (new capabilities land in v4/v5). All four still install and work; see [`docs/maintenance-policy.md`](docs/maintenance-policy.md). Choose according to your actual needs (see "How to choose" below).
13
13
  >
14
14
  > - **`vibe-math-v2` (probability-driven · JSON data layer) · classic**: `qs.json` problem list + `Propos/` proposition library + probability-driven scheduling + code heuristic scheduling;
15
15
  > - **`vibe-math-v3` (third generation · paper-style md + planner agent + method library) · classic**: all knowledge is stored and extended in **Markdown paper/research-report form** (`Problems/` problem list + dependencies + source motivation, `Progress/` research log, `Propos/` proposition library, `Methods/` general theory invention library, `Verified/` absolutely trustworthy); before scheduling, the **planner agent** autonomously draws up a plan for the next N steps; theories/frameworks/tools/methods/ideas invented during solving are distilled by the **Method Keeper** into a reusable method system (as in inventing group theory or functional analysis).
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  > 运行在 **DeepSeek Harness** 内的一组 **agent preset**(`vibe-math-v2` / `vibe-math-v3` / `vibe-math-v4` / `vibe-math-v5`),
10
10
  > 用多代理协作自动求解数学问题并对结论做多代理交叉验证。四个预设共享「**断点续跑**、
11
11
  > **中途人工干预**、**进度汇报**、**自然语言驱动**」底座能力,但采用四代不同的求解架构:
12
- > **💡 四个架构同级**——`vibe-math-v2` 与 `vibe-math-v3` 是**经典**架构(成熟可用、持续维护),`vibe-math-v4` 是「常驻自组织合作研究」架构、`vibe-math-v5` 是最新的「研究所体系」(两者均标注为实验性);根据你的实际需求自行选择(详见下方「怎么选」)。
12
+ > **💡 v4/v5 优先**——`vibe-math-v4`(「常驻自组织合作研究」架构)与 `vibe-math-v5`(最新的「研究所体系」)是**当前主要维护方向**;`vibe-math-v2` 与 `vibe-math-v3` 是**经典/早期架构**,已**成熟定型**,今后只做**适配兼容**维护(新能力只进 v4/v5)。四套都随包安装、可正常使用;详见 [`docs/maintenance-policy.md`](docs/maintenance-policy.md)。根据你的实际需求自行选择(详见下方「怎么选」)。
13
13
  >
14
14
  > - **`vibe-math-v2`(概率驱动 · JSON 数据层)· 经典**:`qs.json` 问题清单 + `Propos/` 命题库 + 概率驱动调度 + 代码启发式调度;
15
15
  > - **`vibe-math-v3`(第三代 · 论文式 md + 规划代理 + 方法库)· 经典**:全部知识以 **Markdown 论文/研究报告式** 存储与续写(`Problems/` 问题清单+依赖+来源动机、`Progress/` 研究日志、`Propos/` 命题库、`Methods/` 通用理论发明库、`Verified/` 绝对可信);调度前由**规划代理**自主制定接下来 N 步计划;解决过程中发明的理论/框架/工具/方法/思想由 **Method Keeper** 沉淀为可复用方法体系(如发明群论、泛函分析那样)。
@@ -548,6 +548,8 @@ DSH 的 **agent preset 交付方式**在 0.1.6 → 0.1.7 之间换过一次,
548
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
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
550
 
551
+ | **维护方针(文档)** | `vibe-math-v4`/`vibe-math-v5` 是**主要维护方向**(新能力、新守卫、提示词强化、重构只落这两套);`vibe-math-v2`/`vibe-math-v3` 已**成熟定型**,只接受**适配兼容**改动:宿主适配、共享模块的正确性/安全修复、行为不变的回归修复。共享模块(`vibe-math-v{2,3,4,5}/math-computation.js` 等)是**四份复制型** ⇒ 修复必须**同批传播**且四份**字节一致**,否则 v2/v3 会在宿主升级后腐坏;**明显的正确性/安全缺陷无论落在哪套都修**("冻结"= 停止功能投资,不是放任缺陷)| **随包文档** `docs/maintenance-policy.md`(中英同页)+ `README.md`/`README.en.md` 各一行指引;`node scripts/release-check.mjs` 核对随包清单与 `files[]` 一致(声明了但缺失 = 0)|
552
+
551
553
  > **运行方式(本轮核实)**:`tests/run-tests.mjs:93-110` 收集 `tests/` 下**每一个** `.mjs`(仅跳过自身、`NEEDS_ARGS` 清单与 `replacedBare` 变体),因此 `audit-path-discipline.mjs` 是**门禁内的 probe**,不是"只能手动跑";未随包发布的守卫(见 `package.json#files` 的 tests 子集)在安装树里不可运行。
552
554
 
553
555
 
@@ -121,6 +121,10 @@
121
121
  **所办必须先与全所交流、商讨、优化、审查**:至少 1 条所办消息(`vibe_v5_message`)**且**至少 1 次会议
122
122
  (`vibe_v5_meeting`),结论写进定稿说明,然后调用 `vibe_v5_finalize_paper`。
123
123
  两个计数记在 `paper.meta.json`;缺一即 `V5_PAPER_CONSULT_REQUIRED`(不是静默跳过)。
124
+ - **`force` = 一张新论文**:`/vN paper … force` **重写**已定稿的论文 ⇒ 新的 `paper` 状态以
125
+ `consult = {messages: 0, meetings: 0}` 开始,**重启前的消息与会议不再计入**(这是设计,不是丢状态 ✗)。
126
+ 因此 `paperEditor="office"` 在 `force` 之后必须**重新**征询;`V5_PAPER_CONSULT_REQUIRED` 的响应会**具名**说明
127
+ 这一点(`restarted: true` + `forceReason`),不再让人靠猜。
124
128
 
125
129
  ## 8. LaTeX 检测、编译与降级
126
130
 
@@ -0,0 +1,31 @@
1
+ # 维护方针:v4/v5 优先,v2/v3 冻结为「适配兼容」
2
+
3
+ > 本文件说明四套预设的**维护优先级**,供使用者与维护者参考。策略由维护者决定,可能随版本调整。
4
+
5
+ ## 中文
6
+
7
+ **投资方向:`vibe-math-v4` 与 `vibe-math-v5`。** 新能力、新守卫(测试/审计)、提示词强化与重构都只落这两套。
8
+
9
+ **`vibe-math-v2` 与 `vibe-math-v3` 已成熟定型(architecturally frozen)。** 它们仍随包安装、可正常使用,但**只接受「适配兼容」类改动**:
10
+
11
+ - 宿主(DeepSeek Harness)升级带来的适配修复;
12
+ - **共享模块的正确性/安全修复**:`vibe-math-v{2,3,4,5}/math-computation.js` 等是四份**复制型共享** ⇒ 修复必须**同批传播**且四份保持**字节一致**,否则 v2/v3 会在宿主升级后悄悄腐坏;
13
+ - 让既有行为**保持不变**的回归修复。
14
+
15
+ **因此**:v2/v3 的参数名、默认值与数据格式**不再演进**(新参数、新字段只出现在 v4/v5);v2/v3 的测试套件保留为**兼容回归守卫**(保持通过即可),不再为它们新增作业。
16
+
17
+ **例外(不可扩大解释)**:**明显的正确性/安全缺陷**无论落在哪一套都会被修复 —— 「冻结」指停止**功能投资**,不等于放任已发布代码里的缺陷。
18
+
19
+ ## English
20
+
21
+ **Investment goes to `vibe-math-v4` and `vibe-math-v5`.** New capabilities, new guards (tests/audits), prompt strengthening and refactors land in these two only.
22
+
23
+ **`vibe-math-v2` and `vibe-math-v3` are architecturally frozen.** They still install and work, but accept **compatibility-only** changes:
24
+
25
+ - adaptations required by a DeepSeek Harness upgrade;
26
+ - **correctness/security fixes in shared modules**: `vibe-math-v{2,3,4,5}/math-computation.js` and friends are **copied** into each preset, so a fix must be propagated in the same change and the four copies must stay **byte-identical** — otherwise v2/v3 rot silently on the next host upgrade;
27
+ - regression fixes that keep their current behaviour unchanged.
28
+
29
+ **Consequently** v2/v3 parameter names, defaults and data formats **no longer evolve** (new parameters and fields appear in v4/v5 only), and their test suites remain **compatibility regression guards** (kept green) without new jobs.
30
+
31
+ **Exception (not to be stretched)**: an outright correctness/security defect is fixed wherever it lives — "frozen" means no further **feature** investment, not tolerating defects in shipped code.
@@ -8,6 +8,8 @@ Chinese counterpart of this index is [`README.md`](./README.md) (中文索引).
8
8
 
9
9
  | Version | Key change | Notes |
10
10
  |---|---|---|
11
+ | 2.8.3 | Patch: a speech arriving **after the meeting closed** is no longer dropped silently (it becomes a named "late note" in the minutes, which stay append-only); an office finalisation refused after a `force` restart now **explains why** (`restarted: true` + `forceReason`, stating that the counters restarted with the new paper and that a fresh consultation is required); the docs now state "`force` = a new paper". | [中文](./RELEASE-NOTES-2.8.3.md) · [English](./RELEASE-NOTES-2.8.3.en.md) |
12
+ | 2.8.2 | Patch: an explicit `paperLatexCommand` is now probed **alone**, so `triedPaths` no longer reports impossible paths of the form "known TeX root + absolute path"; v5's delivered `paper.md` / `paper.tex` now also **each state** the compile outcome (no engine detected / compilation failed), exactly once and without duplication on re-entry — matching v4 since 2.8.1. Both have real-host reproductions and named-red guards. | [中文](./RELEASE-NOTES-2.8.2.md) · [English](./RELEASE-NOTES-2.8.2.en.md) |
11
13
  | 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) |
12
14
  | 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) |
13
15
  | 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) |
@@ -7,6 +7,8 @@
7
7
 
8
8
  | 版本 | 关键变化 | 说明 |
9
9
  |---|---|---|
10
+ | 2.8.3 | 补丁:会议**收束后到达的发言**不再静默丢弃(作为具名「会后补记」写入纪要、只增不覆盖);`force` 重启后 `paperEditor="office"` 的定稿拒绝**说明原因**(新增 `restarted:true` + `forceReason`,并写明"计数随新论文重置、需重新征询");文档明确「`force` = 一张新论文」。 | [中文](./RELEASE-NOTES-2.8.3.md) · [English](./RELEASE-NOTES-2.8.3.en.md) |
11
+ | 2.8.2 | 补丁:显式 `paperLatexCommand` 改为**单独探测** ⇒ `triedPaths` 不再出现「已知 TeX 根 + 绝对路径」这类**不可能路径**;v5 交付的 `paper.md`/`paper.tex` 也**各自写明**编译结果(未检测到引擎/编译失败),恰好一次且重入不重复 ⇒ 与 2.8.1 的 v4 对齐。两处均有真机复现读数与具名红守卫。 | [中文](./RELEASE-NOTES-2.8.2.md) · [English](./RELEASE-NOTES-2.8.2.en.md) |
10
12
  | 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) |
11
13
  | 2.8.0 | 功能版:显式 `paperLatexCommand` 改为「指定即只用它」(v4/v5 不再静默回落)、检测不到引擎的警告可行动并新增 `triedPaths`、四套预设新增逐字一致的引擎缺失指引;测试门禁更稳(默认并发 2、自动清扫陈旧临时目录、疑似遗留进程只读告警);仓库行尾统一为 LF(npm 包逐字节可复现)。 | [中文](./RELEASE-NOTES-2.8.0.md) · [English](./RELEASE-NOTES-2.8.0.en.md) |
12
14
  | 2.7.3 | 补丁:v5 邮袋改为「发送成功后才确认」(未确认消息不再丢失)、入会与恢复的轮次编号正确、论文必需产物写失败具名告警且补偿幂等、任务 CAS 被拒不留痕、成员库路径契约与 v4 Lean 索引写失败具名;文档中 8 处旧顺序已改正并新增文↔码配对守卫。 | [中文](./RELEASE-NOTES-2.7.3.md) · [English](./RELEASE-NOTES-2.7.3.en.md) |
@@ -0,0 +1,47 @@
1
+ # dsh-vibe-math 2.8.2 — Release Notes
2
+
3
+ > Previous release: 2.8.1. This is a **patch release** fixing two v5 issues around diagnostics and deliverables: `triedPaths` no longer reports **impossible paths**, and v5's delivered `paper.md` / `paper.tex` now state "no engine detected / compilation failed" (2.8.1 did this for v4 only). 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
+ - An explicit `paperLatexCommand` is now probed **alone**: `compile.triedPaths` in `paper.meta.json` no longer contains impossible paths of the form "known TeX root + the absolute path you supplied".
8
+ - v5's delivered `paper.md` and `paper.tex` now **each state** the compile outcome (no engine detected → tex+md only; compilation failed → tex/md kept), **exactly once** and idempotently on re-entry, matching what v4 has done since 2.8.1.
9
+ - Nothing else changed: with TeX in a non-standard location, keep pointing `paperLatexCommand` at an absolute path.
10
+
11
+ ## Added
12
+
13
+ - None. (Patch release: no new parameters, fields or tools.)
14
+
15
+ ## Changed
16
+
17
+ - v5's `paper.md` / `paper.tex` carry one extra outcome line (in the language of `paperLanguage`). Apart from that line, the paper's section order, fixed wording and other fields are unchanged.
18
+
19
+ ## Fixed
20
+
21
+ - Symptom: after setting `paperLatexCommand` to an **absolute** path, `compile.triedPaths` in `paper.meta.json` contained impossible paths such as `…\MiKTeX\miktex\bin\x64\Z:/no/such/xelatex.exe` — and `triedPaths` is precisely the "where did it look" field, so it misled the reader. Cause: the explicit value was treated as a **command name** and joined onto every known install root. It is now probed **alone**, so `triedPaths` records only candidates that were really probed. Measured on a real host (reproduced with a build pinned to 2.8.1): bogus path → `status="not-detected"`, `triedPaths=["Z:/…"]`; real engine → `status="compiled"`, `paper.pdf` 150,044 bytes. Guards: `tests/e2e-v5-round2.test.mjs` (`★ [task-17/v5]`), `tests/v5-institute-fixes.mutants.mjs`.
22
+ - Symptom: v5's delivered `paper.md` / `paper.tex` did **not** say why no PDF was produced (only the `compile` metadata and the log did). Cause: the paper body is composed **before** compilation. Finalisation now writes the outcome into the deliverables **after** compiling (a bullet at the end of `paper.md`; a `%` comment before `\end{document}` in `paper.tex`), **once each** and without duplication on re-entry. Guards: `tests/e2e-v5-round2.test.mjs` (`★ [task-18/v5]`), `tests/v5-institute-fixes.mutants.mjs` (**16/16** named reds).
23
+
24
+ ## Compatibility & Migration
25
+
26
+ - Breaking changes: **none**.
27
+ - Additive only: one outcome line inside the deliverables (v5 only).
28
+ - Migration: none needed; existing `paper.meta.json` files remain readable.
29
+ - The DSH support window is unchanged (see `engines.dsh` in `package.json`).
30
+
31
+ ## Known Limitations
32
+
33
+ - **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.
34
+ - The shipped suites **do not cover host-level behaviour** (for example "the caller is dropped after an interrupt"); that needs a real host.
35
+ - **A root-agent question blocks the whole institute** (operational note, product unchanged): nothing else proceeds until the answer arrives — state the problem and acceptance criteria up front for unattended runs. See [`slv-playbook.md`](./slv-playbook.md) §5.
36
+ - On machines without Lean / LaTeX, the related suites **skip loudly** rather than passing silently.
37
+
38
+ ## Verification
39
+
40
+ - `node tests/run-tests.mjs` — all green (**106** items: 44 suites + 62 probes).
41
+ - `node scripts/release-check.mjs` — exits 0 (version triple, section order of both notes, `files[]` registration, no CRLF in the package); after publishing, `node scripts/release-check.mjs --registry` also compares the registry shasum with the local tarball.
42
+ - The two fixes in this release: `node tests/e2e-v5-round2.test.mjs` (`★ [task-17/v5]`, `★ [task-18/v5]`) and `node tests/v5-institute-fixes.mutants.mjs` (**16/16** named reds).
43
+ - Real-host re-check recipe: install the exact published version (`-Pin <version>`) and run a **trimmed** paper task (no research) — about **8 minutes** — then inspect the `compile` block and whether `paper.pdf` exists.
44
+
45
+ ## Dependencies
46
+
47
+ - No new runtime dependencies; the DSH support window is unchanged.
@@ -0,0 +1,47 @@
1
+ # dsh-vibe-math 2.8.2 — 发布说明
2
+
3
+ > 上一版本:2.8.1。本版是**补丁版**,修两处 v5 的「交付物与诊断」问题:`triedPaths` 不再出现**不可能路径**;v5 交付的 `paper.md`/`paper.tex` 也会写明「未检测到引擎/编译失败」(2.8.1 只做了 v4,本版补齐)。参数名与默认值、数据格式与目录结构**未变**,DSH 支撑窗口不变,**无需迁移**。
4
+
5
+ ## 概述
6
+
7
+ - 显式 `paperLatexCommand` 现在被**单独探测**:`paper.meta.json` 的 `compile.triedPaths` 不再出现「已知 TeX 根 + 你给的绝对路径」这种不可能路径。
8
+ - v5 交付的 `paper.md` 与 `paper.tex` 现在**各自写明**编译结果(未检测到引擎 ⇒ 只产出 tex+md;编译失败 ⇒ 已保留 tex/md),**恰好一次**且定稿重入不重复 ⇒ 与 2.8.1 的 v4 行为**对齐**。
9
+ - 其余行为不变:本机 TeX 装在非标准位置时,仍请用 `paperLatexCommand` 指定绝对路径。
10
+
11
+ ## 新增
12
+
13
+ - 无。(本版为修复版:没有新增参数、字段或工具。)
14
+
15
+ ## 变更
16
+
17
+ - v5 的 `paper.md`/`paper.tex` 多一行结果说明(随 `paperLanguage` 中英各一)。除此之外,论文的章节顺序、固定文案与其它字段均未变。
18
+
19
+ ## 修复
20
+
21
+ - 症状:把 `paperLatexCommand` 设为**绝对**路径后,`paper.meta.json` 的 `compile.triedPaths` 里出现 `…\MiKTeX\miktex\bin\x64\Z:/no/such/xelatex.exe` 这类**不可能路径**——而 `triedPaths` 正是「找过哪里」的字段,会误导用户。原因:显式值被当作**命令名**拿去和已知安装根拼候选。现在显式值被**单独探测**,`triedPaths` 只记录真正探过的候选。真机实测(pin 到 2.8.1 后复现):坏路径 ⇒ `status="not-detected"`、`triedPaths=["Z:/…"]`;真引擎 ⇒ `status="compiled"`、`paper.pdf` 150,044 B。守卫:`tests/e2e-v5-round2.test.mjs`(`★ [task-17/v5]`)、`tests/v5-institute-fixes.mutants.mjs`。
22
+ - 症状:v5 交付的 `paper.md`/`paper.tex` **不说** PDF 为什么没有产出(此前只有 `compile` 元数据与日志里有)。原因:论文正文在**编译之前**组装。现在定稿在编译之后把结果**补写**进交付物(`paper.md` 末尾一条要点;`paper.tex` 在 `\end{document}` 之前插一行 `%` 注释),**各恰好一次**、重入不重复。守卫:`tests/e2e-v5-round2.test.mjs`(`★ [task-18/v5]`)、`tests/v5-institute-fixes.mutants.mjs`(**16/16** 具名红)。
23
+
24
+ ## 兼容性与迁移
25
+
26
+ - 破坏性变更:**无**。
27
+ - 纯新增:交付物里的一行结果说明(仅 v5)。
28
+ - 迁移:无需迁移;既有 `paper.meta.json` 仍可读。
29
+ - DSH 支撑窗口不变(见 `package.json` 的 `engines.dsh`)。
30
+
31
+ ## 已知限制
32
+
33
+ - **非标准位置的 TeX 仍不会被自动发现**:默认探测只覆盖 PATH 与文档化的常见安装根;装在其它目录请用 `paperLatexCommand` 指绝对路径。
34
+ - 随包套件**不覆盖宿主层行为**(例如「中断后调用方被丢弃」),这类行为需要真机才能观察。
35
+ - **根代理提问会阻塞整所流程**(运维注意项,不改产品):答复到达前其余流程停摆;长跑时请把问题与验收条件一次说清。详见 [`slv-playbook.md`](./slv-playbook.md) §5。
36
+ - 缺少 Lean / LaTeX 等引擎的机器上,相关套件会**响亮跳过**,不会静默通过。
37
+
38
+ ## 验证方式
39
+
40
+ - `node tests/run-tests.mjs` —— 全部通过(**106** 项:44 套件 + 62 探针)。
41
+ - `node scripts/release-check.mjs` —— 退出 0(版本三处一致/两份说明章节顺序/`files[]` 登记/包内无 CRLF);发布后 `node scripts/release-check.mjs --registry` 再比对线上 `dist.shasum` 与本地 tarball。
42
+ - 本版两处修复:`node tests/e2e-v5-round2.test.mjs`(`★ [task-17/v5]`、`★ [task-18/v5]`)与 `node tests/v5-institute-fixes.mutants.mjs`(**16/16** 具名红)。
43
+ - 真机复测方式:用 registry 上的精确版本(`-Pin <version>`)跑一个**精简**论文任务(跳过研究),约 **8 分钟**,核对 `compile` 段与 `paper.pdf` 是否存在。
44
+
45
+ ## 依赖
46
+
47
+ - 无新增运行时依赖;DSH 支撑窗口不变。
@@ -0,0 +1,50 @@
1
+ # dsh-vibe-math 2.8.3 — Release Notes
2
+
3
+ > Previous release: 2.8.2. This is a **patch release** fixing two v5 issues around meetings and finalisation: a speech that arrives **after the meeting closed** is no longer dropped silently (it becomes a clearly-marked late note), and an office finalisation refused after a `force` restart now **explains why** (the counters restarted with the new paper, so a fresh consultation is required). 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
+ - A member's meeting speech **no longer disappears silently**: even when it arrives **after** the meeting closed (the watchdog abandons a stalled meeting; the office may finalise early) it is written into the minutes as a clearly-marked "late note", with one named line in the group chat.
8
+ - Meeting minutes are **append-only**: earlier speakers are always preserved.
9
+ - A refusal after a `force` restart is now **explainable**: the response carries `restarted: true` plus the `forceReason`, and the message states that the counters restarted with the new paper, that earlier messages/meetings no longer count, and that a fresh consultation is required.
10
+ - The documentation now states it plainly: **`force` means a new paper** (both `consult` counters start at 0), so a restart requires a **new** consultation.
11
+
12
+ ## Added
13
+
14
+ - When `vibe_v5_finalize_paper` is refused for insufficient consultation, the response now carries **`restarted`** (boolean: was this paper started by a `force` restart?).
15
+
16
+ ## Changed
17
+
18
+ - Meeting minutes gained one kind of entry: `### <member>(会后补记:该发言到达时会议已收束)` — used for speeches that arrive **after** the meeting closed; identical text submitted again is recorded only once.
19
+ - The `V5_PAPER_CONSULT_REQUIRED` message appends the reset explanation when `restarted` (**behaviour is unchanged**; it only states the cause).
20
+ - `docs/final-paper.md` §7 gained the "`force` = a new paper" rule.
21
+
22
+ ## Fixed
23
+
24
+ - Symptom: a member's speech — on a real host, the **convener's own** speech — never appeared in the minutes: the header said `- 发言顺序: r-1 → acad` while the body contained only `### r-1`; the paper's `consult.meetings` stayed 0 and finalisation was refused repeatedly. Cause: a member's end-block `input` was only consumed when a meeting was **live**, and a stalled meeting is closed by the watchdog — speeches arriving later had nowhere to go and were **dropped silently**. Such speeches are now appended as "late notes" (named, de-duplicated, appended rather than overwritten). Guards: `tests/e2e-v5-round2.test.mjs` (`★ [real1004-minutes]`), `tests/v5-institute-fixes.mutants.mjs`.
25
+ - Symptom: after a `force` restart of the paper, the office's usual finalisation was refused by `V5_PAPER_CONSULT_REQUIRED` with **no hint** that the restart had reset the counters (on a real host: **11** consecutive refusals, leaving the members to infer it). Cause: that semantics existed **only in the code** — neither the docs nor the refusal message said it. The refusal now names the reset and asks for a fresh consultation, and the docs state "`force` = a new paper". Guards: `tests/e2e-v5-round2.test.mjs` (`★ [real1004-consult]`), `tests/v5-institute-fixes.mutants.mjs` (**18/18** named reds).
26
+
27
+ ## Compatibility & Migration
28
+
29
+ - Breaking changes: **none**.
30
+ - Additive only: the `restarted` response field; one new kind of minutes entry.
31
+ - Migration: none needed; existing `paper.meta.json` files, meeting minutes and the directory layout are unchanged.
32
+ - The DSH support window is unchanged (see `engines.dsh` in `package.json`).
33
+
34
+ ## Known Limitations
35
+
36
+ - **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.
37
+ - The shipped suites **do not cover host-level behaviour** (for example "the caller is dropped after an interrupt"); that needs a real host.
38
+ - **A root-agent question blocks the whole institute** (operational note, product unchanged): nothing else proceeds until the answer arrives — state the problem and acceptance criteria up front for unattended runs. See [`slv-playbook.md`](./slv-playbook.md) §5.
39
+ - After a meeting is abandoned by the watchdog, the minutes keep the abandonment marker and **all earlier speeches**; speeches that arrive later land in the "late note" section.
40
+
41
+ ## Verification
42
+
43
+ - `node tests/run-tests.mjs` — all green (**106** items: 44 suites + 62 probes).
44
+ - `node scripts/release-check.mjs` — exits 0 (version triple, section order of both notes, `files[]` registration, no CRLF in the package); after publishing, add `--registry` to compare the registry shasum with the local tarball.
45
+ - The two fixes in this release: `node tests/e2e-v5-round2.test.mjs` (`★ [real1004-minutes]`, `★ [real1004-consult]`) and `node tests/v5-institute-fixes.mutants.mjs` (**18/18** named reds).
46
+ - Real-host re-check recipe: install the exact published version (`-Pin <version>`) and run a **trimmed** meeting + paper task (no research), then check that the minutes contain both speakers and that a refusal after a `force` restart is named.
47
+
48
+ ## Dependencies
49
+
50
+ - No new runtime dependencies; the DSH support window is unchanged.
@@ -0,0 +1,50 @@
1
+ # dsh-vibe-math 2.8.3 — 发布说明
2
+
3
+ > 上一版本:2.8.2。本版是**补丁版**,修两处 v5 的会议与定稿问题:**会议收束后到达的发言不再被静默丢弃**(改成具名的"会后补记");`force` 重启后 `paperEditor="office"` 的定稿拒绝会**说明原因**(计数已随新论文重置、需重新征询)。参数名与默认值、数据格式与目录结构**未变**,DSH 支撑窗口不变,**无需迁移**。
4
+
5
+ ## 概述
6
+
7
+ - 成员的会议发言**不会再无声消失**:即使发言在会议收束(看门狗放弃停滞会议,或所办提前定稿)**之后**才到达,也会作为「**会后补记**」写进会议纪要,并在群聊里具名说明一次。
8
+ - 会议纪要**只增不覆盖**:先前发言者的内容一律保留。
9
+ - `force` 重启后的定稿拒绝**可解释**:响应新增 `restarted: true` 并写明 `forceReason`,消息直接告诉你"计数随新论文重置、重启前的消息/会议不再计入、请重新征询"。
10
+ - 文档明确:**`force` = 一张新论文**(`consult` 的两个计数随之从 0 开始),因此重启后必须**重新**征询。
11
+
12
+ ## 新增
13
+
14
+ - `vibe_v5_finalize_paper` 在因征询不足被拒时,响应新增 **`restarted`** 字段(布尔;该论文是否由 `force` 重启而来)。
15
+
16
+ ## 变更
17
+
18
+ - 会议纪要新增一种条目:「会后补记」(`### <成员>(会后补记:该发言到达时会议已收束)`)——针对**会议已收束后**到达的发言;同一段文本重复提交只记一次。
19
+ - `V5_PAPER_CONSULT_REQUIRED` 的文案在 `restarted` 时追加重置说明(**行为逻辑不变**,只是把原因说清)。
20
+ - `docs/final-paper.md` §7 增补一条「`force` = 一张新论文」的口径。
21
+
22
+ ## 修复
23
+
24
+ - 症状:某位成员(真机实测中正是**会议的召集人**)的发言没有出现在会议纪要里——纪要在头部写着 `- 发言顺序: r-1 → acad`,正文却**只有 `### r-1`**;同一张论文的 `consult.meetings` 一直为 0,定稿被连续拒绝。原因:成员结束块里的 `input` 只认「**有活会议**」这一条路径,而会议一旦停滞会被看门狗收束,之后到达的发言**无处可去、静默丢弃**。现在这类发言会作为「会后补记」写入纪要(具名、去重、追加而非覆盖)。守卫:`tests/e2e-v5-round2.test.mjs`(`★ [real1004-minutes]`)、`tests/v5-institute-fixes.mutants.mjs`。
25
+ - 症状:`force` 重启论文后,所办按老流程定稿会被 `V5_PAPER_CONSULT_REQUIRED` 反复拒绝,而**提示里没有任何线索**说明"刚刚重启导致计数清零"(真机实测连续 **11** 次、只能靠成员自行推断)。原因:这一语义**只存在于代码里**,文档与拒绝文案都没写。现在拒绝会具名说明重置与"请重新征询",文档也把「`force` = 新论文」写清楚。守卫:`tests/e2e-v5-round2.test.mjs`(`★ [real1004-consult]`)、`tests/v5-institute-fixes.mutants.mjs`(**18/18** 具名红)。
26
+
27
+ ## 兼容性与迁移
28
+
29
+ - 破坏性变更:**无**。
30
+ - 纯新增:`restarted` 响应字段;纪要里一种新的条目类型。
31
+ - 迁移:无需迁移;既有 `paper.meta.json`、会议纪要与目录结构不变。
32
+ - DSH 支撑窗口不变(见 `package.json` 的 `engines.dsh`)。
33
+
34
+ ## 已知限制
35
+
36
+ - **非标准位置的 TeX 仍不会被自动发现**:默认探测只覆盖 PATH 与文档化的常见安装根;装在其它目录请用 `paperLatexCommand` 指绝对路径。
37
+ - 随包套件**不覆盖宿主层行为**(例如「中断后调用方被丢弃」),这类行为需要真机才能观察。
38
+ - **根代理提问会阻塞整所流程**(运维注意项,不改产品):答复到达前其余流程停摆;长跑时请把问题与验收条件一次说清。详见 [`slv-playbook.md`](./slv-playbook.md) §5。
39
+ - 会议「因长时间无新发言被看门狗放弃」之后,纪要里会留下放弃标记;**放弃前的发言仍保留**,之后到达的发言进「会后补记」。
40
+
41
+ ## 验证方式
42
+
43
+ - `node tests/run-tests.mjs` —— 全部通过(**106** 项:44 套件 + 62 探针)。
44
+ - `node scripts/release-check.mjs` —— 退出 0(版本三处一致/两份说明章节顺序/`files[]` 登记/包内无 CRLF);发布后加 `--registry` 比对线上 shasum 与本地 tarball。
45
+ - 本版两处修复:`node tests/e2e-v5-round2.test.mjs`(`★ [real1004-minutes]`、`★ [real1004-consult]`)与 `node tests/v5-institute-fixes.mutants.mjs`(**18/18** 具名红)。
46
+ - 真机复测方式:用 registry 上的精确版本(`-Pin <version>`)跑一个**精简**会议+论文任务(跳过研究),核对纪要是否含两位发言人、以及 force 重启后拒绝是否具名。
47
+
48
+ ## 依赖
49
+
50
+ - 无新增运行时依赖;DSH 支撑窗口不变。
@@ -37,6 +37,7 @@ node -e "const p=require('./package.json'),l=require('./package-lock.json');cons
37
37
  ## 3. 提交前:门禁与派生计数
38
38
 
39
39
  ```bash
40
+ node scripts/clean-temp.mjs --ps # 先看有没有遗留进程(只读;--kill 需显式)
40
41
  node tests/run-tests.mjs # 必须全绿:TOTAL <n> PASS <n> FAIL 0
41
42
  node scripts/update-doc-counts.mjs # 让文档里的派生计数收敛
42
43
  node scripts/update-doc-counts.mjs --check # 退出 0 = 已收敛
@@ -44,6 +45,11 @@ node scripts/release-check.mjs # 版本三处一致/两份说
44
45
  # tarball 声明缺失=0/开发专用文件不随包/包内无 CRLF
45
46
  ```
46
47
 
48
+ - **先查遗留进程,再谈"时序抖动"**(实测教训):一只**两天前**启动、父进程已消失的宿主累计烧掉 **197,410 秒 CPU**,
49
+ 在 4 核机上等于长期占满一个核 ⇒ 同一批作业被放慢 **1.3–4.5×**、门禁耗时总和从 **2822 s 涨到 7875 s**,于是
50
+ "单跑全绿"的套件在门禁里偶发报红。`scripts/clean-temp.mjs --ps` 只读列出来(判据:命令行属于本工程/团队**且**
51
+ 父进程已消失),`--kill` 才动手;门禁启动时也会打印一行只读告警。
52
+
47
53
  - **`scripts/release-check.mjs` 是本手册的可执行化部分**:把 §1/§2/§6 的逐项核对做成一条命令(含一次真实
48
54
  `npm pack` 与解包扫描)。发布前它必须退出 0;`--skip-pack` 只做静态检查,`--registry` 在发布后比对线上
49
55
  `dist.shasum` 与本地 tarball 的字节一致性,`--self-test` 自证判据不是空转(配 `tests/release-check.mutants.mjs`
@@ -75,7 +75,11 @@ dsh --profile <clone> --port <自有端口> --no-open
75
75
 
76
76
  - **没有任何引擎**(Octave/Julia/MATLAB/Maple/Wolfram 等)⇒ 这些面只能核对**诚实文本**:`configured`(配置启用)与 `absent[]`(每项带 `why`),这是**部分替代**,不是执行验证;
77
77
  - **单客户端** ⇒ 并发要靠两个克隆 + 两个端口模拟,否则如实声明超出范围;
78
- - **有界运行只能建立"落位与可观测性"**,**不能**建立"收敛/收口"结论——收敛结论必须来自随包套件。
78
+ - **有界运行只能建立"落位与可观测性"**,**不能**建立"收敛/收口"结论——收敛结论必须来自随包套件;
79
+ - **根代理提问会阻塞整所流程(实测观察到,属既有行为,不改产品)**:当**根/主代理**(而非某个成员)调用 `ask_user_question`
80
+ 这类"等待人工答复"的工具时,在答复到达之前**其余流程会停摆**(转录里可看到停在这一步、后续事件不再推进)。
81
+ 影响面:把研究所当成**无人值守**长跑时会卡住;有界影响、**不主张缺陷**。使用建议:长跑时避免让根代理走到提问分支
82
+ (把问题、范围与验收条件在任务里一次说清),或准备好及时答复;这也是把"**有界运行**"当结论必须慎重的另一条理由。
79
83
 
80
84
  ## 6. 中止触发与处置
81
85
 
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.1",
4
+ "version": "2.8.3",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0",
@@ -24,6 +24,7 @@
24
24
  "README.en.md",
25
25
  "docs/COMPAT-AUDIT-ROUND2.md",
26
26
  "docs/AUDIT-CHECKLIST.md",
27
+ "docs/maintenance-policy.md",
27
28
  "tests/audit-formal-sensitivity.mjs",
28
29
  "tests/audit-persona-sensitivity.mjs",
29
30
  "tests/audit-persona-surface.test.mjs",
@@ -206,7 +207,11 @@
206
207
  "docs/release-notes/RELEASE-NOTES-2.8.0.md",
207
208
  "docs/release-notes/RELEASE-NOTES-2.8.0.en.md",
208
209
  "docs/release-notes/RELEASE-NOTES-2.8.1.md",
209
- "docs/release-notes/RELEASE-NOTES-2.8.1.en.md"
210
+ "docs/release-notes/RELEASE-NOTES-2.8.1.en.md",
211
+ "docs/release-notes/RELEASE-NOTES-2.8.2.md",
212
+ "docs/release-notes/RELEASE-NOTES-2.8.2.en.md",
213
+ "docs/release-notes/RELEASE-NOTES-2.8.3.md",
214
+ "docs/release-notes/RELEASE-NOTES-2.8.3.en.md"
210
215
  ],
211
216
  "license": "MIT",
212
217
  "keywords": [
@@ -236,7 +241,7 @@
236
241
  },
237
242
  "minVersion": "0.1.2-alpha.4",
238
243
  "testedVersion": "0.2.0-rc.2",
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 支撑窗口不变;无需迁移。",
244
+ "compatNote": "v2.8.3 补丁版:修 v5 的会议与定稿两处。① 会议收束后到达的成员发言不再静默丢弃 —— 看门狗放弃停滞会议(或所办提前定稿)后到达的 input 现在作为具名「会后补记」追加进纪要(按文本去重、先前发言只增不覆盖);真机实测原症状是纪要在头部写着「发言顺序: r-1 → acad」而正文只有 ### r-1。② force 重启后 paperEditor=\"office\" 的定稿拒绝现在说明原因:响应新增 restarted 字段并在文案里写明 forceReason 与「计数随新论文重置、重启前的消息/会议不再计入、请重新征询」;docs/final-paper.md §7 明确「force = 一张新论文」。参数名与默认值、数据格式与目录结构均未变;DSH 支撑窗口不变;无需迁移。",
240
245
  "compatibility": {
241
246
  "dshReleases": {
242
247
  "0.1.2-alpha.4": "compatible",
@@ -1299,6 +1299,13 @@ console.log('\n[24] manual paper, office editor: parts → cross-review → cons
1299
1299
  console.log(' [task-9] v5 detection evidence: compile=' + String(meta && meta.compile && meta.compile.status) + ' triedPaths=' + JSON.stringify((meta && meta.compile && meta.compile.triedPaths) || null))
1300
1300
  assert(Array.isArray(meta && meta.compile && meta.compile.triedPaths) && meta.compile.triedPaths.length > 0,
1301
1301
  '★ [task-9] the durable meta records the probed TeX paths (triedPaths)')
1302
+ // task-18 (v4 got this in 2.8.1): the DELIVERED artifacts must state why no PDF was produced — the body is
1303
+ // composed before the compile runs, so the note is written afterwards, exactly once each (idempotent).
1304
+ // The note follows paperLanguage, so the check is language-agnostic and still requires EXACTLY one.
1305
+ const noteHits = (t) => t.split('本机未检测到 LaTeX 引擎:只产出 tex+md').length - 1
1306
+ + t.split('No LaTeX engine detected: tex+md only').length - 1
1307
+ assert(noteHits(md) === 1 && noteHits(tex) === 1,
1308
+ '★ [task-18/v5] the DELIVERED paper.md and paper.tex state that no engine was detected (once each; md=' + noteHits(md) + ' tex=' + noteHits(tex) + ')')
1302
1309
  const warnText = (meta && Array.isArray(meta.warnings) ? meta.warnings : []).join(' | ')
1303
1310
  assert(warnText.indexOf('只交付 paper.tex 与 paper.md。') !== -1 && warnText.indexOf('已探测 PATH 与文档化的常见 TeX 根;可用 paperLatexCommand 指定绝对路径') !== -1,
1304
1311
  '★ [task-9] the not-detected warning keeps its tail AND says where it looked and how to pin an engine')
@@ -1343,6 +1350,20 @@ console.log('\n[25] paper idempotency: repeated triggers only fill missing artif
1343
1350
  const stF = await h.callTool('vibe_v5_status', {})
1344
1351
  assert(stF.paper.status === 'writing' && stF.paper.round === 1, 'the forced run restarted at round 1')
1345
1352
  }
1353
+ // real1004-consult (found on a real host with 2.8.2): `force` rewrites the paper, so the NEW paper starts with
1354
+ // consult={0,0} — intended — but the office was rejected ELEVEN times with no hint that its earlier meeting had
1355
+ // stopped counting (only the members could infer it). The refusal must NAME the reset and say what to do.
1356
+ {
1357
+ const hq = makeHost({ pluginModule })
1358
+ await hq.callTool('vibe_v5_start', { problem: PROBLEM, researcherCount: 1 })
1359
+ await hq.settleSpawns()
1360
+ const fq = await hq.callTool('vibe_v5_paper', { force: true, editor: 'office', reason: 'real1004 复测' })
1361
+ assert(fq.ok === true && fq.started === true, 'precondition: a forced office-editor paper starts (' + JSON.stringify(fq).slice(0, 130) + ')')
1362
+ await drivePaper(hq) // parts + cross-reviews land, so the office's finalisation reaches the CONSULT gate
1363
+ const rq = await hq.callTool('vibe_v5_finalize_paper', { decision: 'deliverable', note: 'real1004:未重新征询,预期被具名拒绝' })
1364
+ assert(rq.ok === false && rq.code === 'V5_PAPER_CONSULT_REQUIRED' && rq.restarted === true && /force/.test(String(rq.message)) && /重置/.test(String(rq.message)) && /重新/.test(String(rq.message)),
1365
+ '★ [real1004-consult] an office finalisation after a force restart NAMES the reset (restarted=true, message explains the required re-consultation): ' + JSON.stringify(rq).slice(0, 300))
1366
+ }
1346
1367
 
1347
1368
  // ---------- 26. fake LaTeX: the success path produces paper.pdf ----------
1348
1369
  console.log('\n[26] fake LaTeX compiler: success path produces paper.pdf')
@@ -1437,6 +1458,42 @@ console.log('\n[27] fake LaTeX compiler: repair path and persistent-failure degr
1437
1458
  const metap = JSON.parse(readFileSync(join(dp, 'paper.meta.json'), 'utf8'))
1438
1459
  assert(metap.compile.status === 'not-detected',
1439
1460
  '★ [task-10/v5] an unresolvable explicit paperLatexCommand degrades (never falls through to another engine) — got ' + JSON.stringify(metap.compile.status))
1461
+ // task-17 (found on a real host with 2.8.1): the explicit value must be probed ALONE. Probing it as a bare
1462
+ // command name let the known-root stage build impossible paths like `<MiKTeX root>/Z:/…/xelatex.exe`, which
1463
+ // made `triedPaths` — the very field that says WHERE it looked — report paths that cannot exist.
1464
+ const tp = metap.compile.triedPaths || []
1465
+ const joined = tp.filter((p) => /[\\/][A-Za-z]:[\\/]/.test(String(p).replace(/^[A-Za-z]:[\\/]/, '')))
1466
+ assert(tp.length >= 1 && joined.length === 0,
1467
+ '★ [task-17/v5] an ABSOLUTE explicit paperLatexCommand is probed ALONE — triedPaths never joins it onto a known TeX root (triedPaths=' + JSON.stringify(tp) + ')')
1468
+ }
1469
+ // real1004-minutes (found on a real host with 2.8.2): a speech that arrives AFTER the meeting closed — the
1470
+ // watchdog abandons a stalled meeting and the office may finalise early — used to be dropped SILENTLY, so the
1471
+ // minutes declared a speaking order ("r-1 → acad") that never appeared in the file (only "### r-1"). It must
1472
+ // be appended as a clearly-marked late note, and the earlier speeches must survive (append-only).
1473
+ {
1474
+ const hm = makeHost({ pluginModule })
1475
+ await hm.callTool('vibe_v5_start', { problem: PROBLEM, researcherCount: 1 })
1476
+ await hm.settleSpawns()
1477
+ const mt = await hm.callTool('vibe_v5_meeting', { agenda: 'real1004 晚到发言:会议收束后仍有人发言', kind: 'sync' })
1478
+ const mid = mt && mt.meeting
1479
+ assert(!!mid, 'precondition: the meeting is convened (' + JSON.stringify(mt).slice(0, 120) + ')')
1480
+ await hm.drain(40) // everyone speaks -> the meeting finalises, meeting = null
1481
+ const mpath = join(hm.WS, 'VibeMath', 'Projects', 'default', 'Institutes', 'institute', 'Shared', 'Meetings', mid + '.md')
1482
+ const before = readFileSync(mpath, 'utf8')
1483
+ assert(/### /.test(before), 'precondition: the closed minutes already contain at least one speech: ' + before.slice(0, 140))
1484
+ const stc = await hm.callTool('vibe_v5_status', {})
1485
+ assert(!stc.meeting, 'precondition: the meeting is CLOSED (meeting=' + JSON.stringify(stc.meeting) + ') so a later speech has no live meeting')
1486
+ // A LATE speech: the member submits `input` on a LATER wake while no meeting is running (exactly what a
1487
+ // real host showed — the member kept submitting after the meeting was already closed).
1488
+ await hm.callTool('vibe_v5_message', { to: 'acad', content: 'real1004:请再确认一次你的意见。' })
1489
+ const wlate = await hm.peekWakeOf('acad', 8000)
1490
+ assert(!!wlate, 'precondition: a member is woken again after the meeting closed (so the end block is processed)')
1491
+ hm.fireEnd(wlate.childId, { input: 'real1004 晚到发言:会议收束之后我仍提交了意见。', contextPct: 20 })
1492
+ await sleep(200)
1493
+ const after = readFileSync(mpath, 'utf8')
1494
+ assert(after.includes('real1004 晚到发言') && after.includes('会后补记'),
1495
+ '★ [real1004-minutes] a speech arriving after the meeting closed is appended as a late note, never dropped silently')
1496
+ assert(after.includes('### '), 'the earlier speeches are still in the minutes (append-only, never clobbered)')
1440
1497
  }
1441
1498
 
1442
1499
  // ---------- 28. office-only surface + id normalisation ----------
@@ -194,13 +194,46 @@ const FAMILIES = [
194
194
  to: '只交付 paper.tex 与 paper.md。',
195
195
  expect: /\[task-9\] the not-detected warning keeps its tail AND says where it looked/,
196
196
  },
197
+ // RETIRED (task-17, 2.8.1 real-host fix): the former entry here mutated `return [forced]` into the merged
198
+ // `[forced].concat(order…)`, which used to let an unresolvable explicit command fall through to a real
199
+ // engine. After task-17 the explicit value is ALSO handed to resolveKnownTool as `explicit` (the explicit
200
+ // branch probes only it and returns early), so the property is now enforced at TWO independent sites and
201
+ // NO single-site mutation can defeat it — the merged list merely probes dead candidates. The assertion
202
+ // itself stays in tests/e2e-v5-round2.test.mjs (`★ [task-10/v5] …degrades`); only the falsifier is retired
203
+ // (and the task-17 entry below covers the hand-off single-site).
197
204
  {
198
- // task-10: restoring the merged candidate list lets an unresolvable explicit command fall through
199
- // to another engine (the documented rule is "explicit ⇒ only it").
200
- name: 'task-10: the explicit LaTeX command falls through to another engine again',
201
- from: 'return [forced]',
202
- to: 'return [forced].concat(order.filter((n) => n !== forced))',
203
- expect: /\[task-10\/v5\] an unresolvable explicit paperLatexCommand degrades/,
205
+ // task-17 (real host, 2.8.1): probing the explicit value as a BARE COMMAND NAME lets the known-root stage
206
+ // join it onto every TeX root, so `triedPaths` reports impossible paths. Reverting the hand-off must
207
+ // redden the guarding assertion by name.
208
+ name: 'task-17: the explicit paperLatexCommand is probed as a bare command name again (triedPaths gets fake joins)',
209
+ from: "explicit: forcedTex, kind: 'tex'",
210
+ to: "explicit: '', kind: 'tex'",
211
+ expect: /\[task-17\/v5\] an ABSOLUTE explicit paperLatexCommand is probed ALONE/,
212
+ },
213
+ {
214
+ // task-18: the DELIVERED artifacts must say why no PDF was produced (the body is composed before the
215
+ // compile runs, so the note is written after it). Blunting the zh note must redden that guard BY NAME.
216
+ // The scenario that guards this runs with lang='en', so the ENGLISH branch is the one to blunt.
217
+ name: 'task-18: the delivered paper stops stating that no engine was detected',
218
+ from: ": 'No LaTeX engine detected: tex+md only'",
219
+ to: ": ''",
220
+ expect: /\[task-18\/v5\] the DELIVERED paper.md and paper.tex/,
221
+ },
222
+ {
223
+ // real1004-minutes: a speech arriving after the meeting closed must be appended as a late note; dropping
224
+ // it silently is exactly what a real host showed (minutes declared "r-1 → acad" with only "### r-1").
225
+ name: 'real1004-minutes: a speech arriving after the meeting closed is dropped silently again',
226
+ from: "} else if (lastMeetingId && typeof p.input === 'string' && p.input.trim()) {",
227
+ to: '} else if (false) {',
228
+ expect: /\[real1004-minutes\] a speech arriving after the meeting closed/,
229
+ },
230
+ {
231
+ // real1004-consult: the office refusal must NAME the force reset (observed: 11 unnamed rejections on a real
232
+ // host). Dropping the clause must redden the guarding assertion by name.
233
+ name: 'real1004-consult: the office refusal stops naming the force restart',
234
+ from: 'const restarted = p.forced === true',
235
+ to: 'const restarted = false',
236
+ expect: /\[real1004-consult\] an office finalisation after a force restart NAMES the reset/,
204
237
  },
205
238
  ]
206
239
  let red = 0
@@ -990,6 +990,11 @@ export function apply(ctx) {
990
990
  const inflight = new Map() // childId -> turn token (dedupes duplicate subagent/end)
991
991
  let heartbeatDisposer = null
992
992
  let meeting = null // in-flight meeting round state
993
+ // real1004-minutes: the id of the MOST RECENT meeting. A speech can arrive AFTER the meeting was
994
+ // closed (the watchdog abandons a stalled meeting, and the office may finalise early); such a late
995
+ // submission used to be dropped SILENTLY, so the minutes claimed a speaking order that never appeared
996
+ // in the file. Keep the pointer so a late speech can still be appended as a clearly-marked note.
997
+ let lastMeetingId = ''
993
998
  let pendingMeeting = null // parked meeting (never preempts verification)
994
999
  let digestTimer = null
995
1000
  let lastProgressAt = now()
@@ -4971,6 +4976,7 @@ export function apply(ctx) {
4971
4976
  return idx
4972
4977
  } })
4973
4978
  const id = idx.id
4979
+ lastMeetingId = id // real1004-minutes: late speeches are appended to the most recent minutes
4974
4980
  meeting = {
4975
4981
  id, agenda: opts.agenda, kind: opts.kind || 'sync', target: opts.target || '',
4976
4982
  by: opts.by || 'office', order, inputs: {}, extras: {}, lastInputAt: now(), startedAt: now(),
@@ -5623,11 +5629,20 @@ export function apply(ctx) {
5623
5629
  }
5624
5630
  const consult = Object.assign({ messages: 0, meetings: 0 }, p.consult || {})
5625
5631
  if (!(Number(consult.messages) > 0) || !(Number(consult.meetings) > 0)) {
5632
+ // real1004-consult: `force` REWRITES the paper (docs/final-paper.md: "force 重写已定稿的论文"), so the
5633
+ // new paper starts with consult={0,0}. That is intended — but the refusal used to say nothing about it:
5634
+ // on a real host the office was rejected ELEVEN times and only the members could infer why their
5635
+ // earlier meeting had stopped counting. Name the reset, its reason, and what to do about it.
5636
+ const restarted = p.forced === true
5626
5637
  return {
5627
5638
  ok: false, code: 'V5_PAPER_CONSULT_REQUIRED',
5628
5639
  message: 'paperEditor="office" 要求所办先与全所交流、商讨、优化、审查:至少 1 条所办消息(vibe_v5_message)+ 至少 1 次会议(vibe_v5_meeting),然后才能定稿。当前:messages=' +
5629
- Number(consult.messages || 0) + ', meetings=' + Number(consult.meetings || 0),
5640
+ Number(consult.messages || 0) + ', meetings=' + Number(consult.meetings || 0) +
5641
+ (restarted
5642
+ ? '。注意:本论文由 force 重启(forceReason=' + String(p.forceReason || '') + ')⇒ consult 计数随新论文重置为 0,重启前的消息/会议不再计入,请**重新**征询(两个计数记录在 paper.meta.json)'
5643
+ : ''),
5630
5644
  consult: { messages: Number(consult.messages || 0), meetings: Number(consult.meetings || 0) },
5645
+ restarted,
5631
5646
  }
5632
5647
  }
5633
5648
  const fin = {
@@ -5893,8 +5908,13 @@ export function apply(ctx) {
5893
5908
  }
5894
5909
  const engines = []
5895
5910
  const triedPaths = [] // task-9: union of the known-root candidates probed across THIS detection
5911
+ // task-17: when paperLatexCommand is set, paperEngineCandidates() returns THAT VALUE ALONE. Probing it as
5912
+ // a bare command name made resolveKnownTool join it onto every known TeX root, so `triedPaths` reported
5913
+ // impossible paths like `<MiKTeX root>/Z:/no/such/xelatex.exe` (observed on a real host with 2.8.1).
5914
+ // Handing it over as the EXPLICIT value probes only it (docs/final-paper.md §B); '' keeps auto-detection.
5915
+ const forcedTex = String(params.paperLatexCommand || '').trim()
5896
5916
  for (const name of paperEngineCandidates(lang)) {
5897
- try { const _vr = await resolveKnownTool(sub, { name, explicit: '', kind: 'tex' }); triedPaths.push(..._vr.tried); const exe = _vr.exe; if (exe) engines.push({ name: String(name), exe: String(exe), via: _vr.via }) } catch (e) { /* not installed */ }
5917
+ try { const _vr = await resolveKnownTool(sub, { name, explicit: forcedTex, kind: 'tex' }); triedPaths.push(..._vr.tried); const exe = _vr.exe; if (exe) engines.push({ name: String(name), exe: String(exe), via: _vr.via }) } catch (e) { /* not installed */ }
5898
5918
  }
5899
5919
  return { engines, triedPaths, reason: engines.length ? '' : ('未检测到 LaTeX 引擎(' + paperEngineCandidates(lang).join('/') + ')') }
5900
5920
  }
@@ -6065,6 +6085,27 @@ export function apply(ctx) {
6065
6085
  // broken engine. Whatever the compile outcome, the DELIVERED tex must be the real,
6066
6086
  // fully-generated one (the attempts are recorded in the meta either way).
6067
6087
  if (p.format !== 'md') await writeTextRel(PAPER_FILE(id, 'paper.tex'), tex)
6088
+ // task-18: the body is composed BEFORE compilation runs, so the outcome cannot live in the sections.
6089
+ // v4 states it in the delivered artifacts since 2.8.1; v5 must too — ONCE and idempotently (finalisation
6090
+ // can be re-entered). The marker sentence is the same family as the warning below, and the presence
6091
+ // check keeps the two paths from ever duplicating it.
6092
+ if (compile.status === 'failed' || compile.status === 'not-detected') {
6093
+ const zhNote = p.lang !== 'en'
6094
+ const note = compile.status === 'failed'
6095
+ ? (zhNote ? 'PDF 编译失败:已保留 paper.tex 与 paper.md 并上报' : 'PDF compilation failed: paper.tex and paper.md were kept and reported')
6096
+ : (zhNote ? '本机未检测到 LaTeX 引擎:只产出 tex+md' : 'No LaTeX engine detected: tex+md only')
6097
+ const annotate = async (rel, file, isTex) => {
6098
+ const cur = await readTextAbs(paperAbs(id, file))
6099
+ if (typeof cur !== 'string' || cur.indexOf(note) !== -1) return
6100
+ const line = (isTex ? '% ' : '- ') + note
6101
+ const next = isTex && cur.indexOf('\\end{document}') !== -1
6102
+ ? cur.replace('\\end{document}', line + '\n\\end{document}')
6103
+ : cur.replace(/\s*$/, '') + '\n' + line + '\n'
6104
+ await writeTextRel(rel, next)
6105
+ }
6106
+ if (p.format !== 'tex') await annotate(PAPER_FILE(id, 'paper.md'), 'paper.md', false)
6107
+ if (p.format !== 'md') await annotate(PAPER_FILE(id, 'paper.tex'), 'paper.tex', true)
6108
+ }
6068
6109
  if (await fileExistsAbs(paperAbs(id, 'paper.pdf'))) files.push('paper.pdf')
6069
6110
  const warnings = (p.warnings || []).slice()
6070
6111
  if (compile.status === 'failed') warnings.push('LaTeX 编译失败(已保留 paper.tex 与 paper.md,不阻塞定稿):' + (compile.attempts || []).map((a) => a.engine + '/' + a.label + ' exit=' + a.exitCode + (a.message ? '(' + a.message + ')' : '')).join(';'))
@@ -6782,6 +6823,20 @@ export function apply(ctx) {
6782
6823
  const rel = 'Shared/Meetings/' + meeting.id + '.md'
6783
6824
  const prev = (await readTextRel(rel)) || ('# 会议纪要|' + meeting.id + '\n\n')
6784
6825
  await writeTextRel(rel, prev + '### ' + member.id + '\n' + (text || '(无发言)') + '\n\n')
6826
+ } else if (lastMeetingId && typeof p.input === 'string' && p.input.trim()) {
6827
+ // real1004-minutes: no live meeting, yet the member submitted a speech (a real host showed exactly
6828
+ // this: `input` kept arriving after the meeting had been closed/abandoned). It used to be dropped
6829
+ // SILENTLY — the minutes then declared a speaking order that never appeared in the file (`- 发言顺序:
6830
+ // r-1 → acad` with only `### r-1` present). Append it as a clearly marked late note instead, keyed on
6831
+ // ANY wake (not only meeting wakes: the observed submissions arrived on later rounds), de-duplicated by
6832
+ // text so repeated submissions do not pile up.
6833
+ const late = String(p.input).trim()
6834
+ const rel = 'Shared/Meetings/' + lastMeetingId + '.md'
6835
+ const prev = (await readTextRel(rel)) || ('# 会议纪要|' + lastMeetingId + '\n\n')
6836
+ if (prev.indexOf(late) === -1) {
6837
+ await writeTextRel(rel, prev + '### ' + member.id + '(会后补记:该发言到达时会议已收束)\n' + late + '\n\n')
6838
+ await saveChatLine('【会议 ' + lastMeetingId + '】' + member.id + ' 的发言在会议收束后到达,已作为补记写入纪要(不再静默丢弃)。')
6839
+ }
6785
6840
  }
6786
6841
  if (p.solved !== undefined) {
6787
6842
  await writeTextRel('Shared/State-of-institute.md', [