dsh-vibe-math 2.8.2 → 2.8.4
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/docs/final-paper.md +9 -0
- package/docs/release-notes/README.en.md +2 -0
- package/docs/release-notes/README.md +2 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.3.en.md +50 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.3.md +50 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.4.en.md +48 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.4.md +48 -0
- package/package.json +7 -3
- package/tests/e2e-v5-round2.test.mjs +77 -0
- package/tests/v5-institute-fixes.mutants.mjs +24 -0
- package/vibe-math-v5/vibe-math-v5.js +37 -2
package/docs/final-paper.md
CHANGED
|
@@ -121,6 +121,15 @@
|
|
|
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`),不再让人靠猜。
|
|
128
|
+
- **会议何时会被放弃(看门狗)**:会议停滞超过 `2 × activityTimeoutMs`(默认 **240 s**)**且**没有任何被点名的成员
|
|
129
|
+
仍在进行中时,才由看门狗放弃 ✗;若仍有被点名的成员在飞,则宽限到 **3 倍**(默认 720 s)再放弃 —— 成员的**一整轮**
|
|
130
|
+
在真机上很容易超过软阈值,若此时就放弃,其发言只能退化成「会后补记」✗(实测一次真机运行出现 **1067** 次补记、
|
|
131
|
+
**863** 次定稿被拒 ✓)。放弃会写进纪要(`⚠ 本次会议因长时间无新发言而被放弃`)并在群聊具名一行 ✓;放弃**之后**
|
|
132
|
+
到达的发言不会被丢弃,而是作为「**会后补记**」追加进纪要 ✓(见 §7 上一条)。
|
|
124
133
|
|
|
125
134
|
## 8. LaTeX 检测、编译与降级
|
|
126
135
|
|
|
@@ -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.4 | Patch: v5's **meeting watchdog** no longer abandons a meeting while an ASKED member is still in flight (exempt within the soft bound; the 3× hard bound still closes it), so ordinary speeches return to the ordinary sections and "late notes" become an insurance policy again — before the fix one real run showed **1067** late notes and **863** refused finalisations. | [中文](./RELEASE-NOTES-2.8.4.md) · [English](./RELEASE-NOTES-2.8.4.en.md) |
|
|
12
|
+
| 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) |
|
|
11
13
|
| 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) |
|
|
12
14
|
| 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) |
|
|
13
15
|
| 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) |
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
| 版本 | 关键变化 | 说明 |
|
|
9
9
|
|---|---|---|
|
|
10
|
+
| 2.8.4 | 补丁:v5 **会议看门狗**不再在"被点名的成员仍在进行中"时放弃会议(软界内豁免、硬界 3 倍预算仍会放弃)⇒ 正常发言回到正常小节,「会后补记」退回为保险;修复前真机实测一次运行 **1067** 次补记、**863** 次定稿被拒。 | [中文](./RELEASE-NOTES-2.8.4.md) · [English](./RELEASE-NOTES-2.8.4.en.md) |
|
|
11
|
+
| 2.8.3 | 补丁:会议**收束后到达的发言**不再静默丢弃(作为具名「会后补记」写入纪要、只增不覆盖);`force` 重启后 `paperEditor="office"` 的定稿拒绝**说明原因**(新增 `restarted:true` + `forceReason`,并写明"计数随新论文重置、需重新征询");文档明确「`force` = 一张新论文」。 | [中文](./RELEASE-NOTES-2.8.3.md) · [English](./RELEASE-NOTES-2.8.3.en.md) |
|
|
10
12
|
| 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) |
|
|
11
13
|
| 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) |
|
|
12
14
|
| 2.8.0 | 功能版:显式 `paperLatexCommand` 改为「指定即只用它」(v4/v5 不再静默回落)、检测不到引擎的警告可行动并新增 `triedPaths`、四套预设新增逐字一致的引擎缺失指引;测试门禁更稳(默认并发 2、自动清扫陈旧临时目录、疑似遗留进程只读告警);仓库行尾统一为 LF(npm 包逐字节可复现)。 | [中文](./RELEASE-NOTES-2.8.0.md) · [English](./RELEASE-NOTES-2.8.0.en.md) |
|
|
@@ -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 支撑窗口不变。
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# dsh-vibe-math 2.8.4 — Release Notes
|
|
2
|
+
|
|
3
|
+
> Previous release: 2.8.3. This is a **patch release**: it fixes v5's **meeting watchdog**, which used to abandon a meeting while an ASKED member was still working — turning ordinary speeches into "late notes" and making `paperEditor="office"` finalisations fail again and again. 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 meeting is **no longer** abandoned before an ASKED member still in flight hands its speech in: such members may hold the meeting open up to **3×** the stall budget, after which the hard bound still closes it (a genuinely wedged turn must not wedge the whole meeting).
|
|
8
|
+
- The practical effect is that **ordinary speeches land in the ordinary sections again**: one real run before the fix showed **1067** "late notes" and **863** refused finalisations; with the fix, meetings actually collect their speakers and the "late note" path goes back to being an **insurance policy** rather than the norm.
|
|
9
|
+
- The `paperEditor="office"` gate therefore becomes satisfiable in practice (it requires at least one office message and one **recorded** meeting for this paper).
|
|
10
|
+
- The documentation now states when a meeting is abandoned, how the abandonment is named, and what happens to speeches that arrive **after** it.
|
|
11
|
+
|
|
12
|
+
## Added
|
|
13
|
+
|
|
14
|
+
- None. (Patch release: no new parameters, fields or tools.)
|
|
15
|
+
|
|
16
|
+
## Changed
|
|
17
|
+
|
|
18
|
+
- Meeting stall decision: within the soft bound (`2 × activityTimeoutMs`, default 240 s) the meeting is kept open as long as an **asked member is still in flight**; past the **3×** hard bound (default 720 s) it is still abandoned.
|
|
19
|
+
- `docs/final-paper.md` §7 now documents "when a meeting is abandoned" (soft/hard bounds and their relation to the "late note" path).
|
|
20
|
+
|
|
21
|
+
## Fixed
|
|
22
|
+
|
|
23
|
+
- Symptom: after members (including the **convener**) spoke in a meeting, the minutes only gained "late note" entries — one real run reached **1067** of them, and `paperEditor="office"` finalisations were refused by `V5_PAPER_CONSULT_REQUIRED` **863** times. Cause: the stall check measured only the time since the last INPUT and **ignored whether an asked member was still busy**; a member's turn on a real host frequently outlives the 240 s budget, so the meeting was abandoned by the watchdog and later speeches could only take the late-note path. Members that were asked and are still busy now hold the meeting open until the hard bound (3× the budget), so ordinary speeches return to the ordinary sections. Guards: `tests/e2e-v5-round2.test.mjs` (`★ [real1004-stall]`, a **source-level invariant** — this branch cannot be triggered deterministically inside the shipped suites, so the criterion is "source invariant + real-host re-test"), `tests/v5-institute-fixes.mutants.mjs` (**19/19** named reds).
|
|
24
|
+
|
|
25
|
+
## Compatibility & Migration
|
|
26
|
+
|
|
27
|
+
- Breaking changes: **none**.
|
|
28
|
+
- Additive only: no new fields (behaviour and documentation only).
|
|
29
|
+
- Migration: none needed.
|
|
30
|
+
- The DSH support window is unchanged (see `engines.dsh` in `package.json`).
|
|
31
|
+
|
|
32
|
+
## Known Limitations
|
|
33
|
+
|
|
34
|
+
- The **real-host** behaviour of the meeting watchdog can only be confirmed by a real-host re-test (inside the shipped suites the heartbeat delay is not controllable, so the branch cannot be triggered deterministically; the guard is therefore a **source-level invariant**, as stated in the test comments and the commit message).
|
|
35
|
+
- **TeX installed in a non-standard location is still not auto-discovered**: the default probe covers PATH and the documented common install roots.
|
|
36
|
+
- The shipped suites **do not cover host-level behaviour** (for example "the caller is dropped after an interrupt").
|
|
37
|
+
- **A root-agent question blocks the whole institute** (operational note, product unchanged): state the problem and acceptance criteria up front for unattended runs. See [`slv-playbook.md`](./slv-playbook.md) §5.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
- `node tests/run-tests.mjs` — all green (**106** items: 44 suites + 62 probes).
|
|
42
|
+
- `node scripts/release-check.mjs` — exits 0; after publishing, add `--registry` to compare the registry shasum with the local tarball.
|
|
43
|
+
- This release's fix: `node tests/e2e-v5-round2.test.mjs` (`★ [real1004-stall]`, measured `passed=436 failed=0`) and `node tests/v5-institute-fixes.mutants.mjs` (**19/19** named reds).
|
|
44
|
+
- Real-host re-check recipe: install the exact published version (`-Pin <version>`) and run a **trimmed** meeting + paper task (no research), then count "late notes" (1067 before the fix) and refused finalisations (863 before the fix) — both should drop sharply.
|
|
45
|
+
|
|
46
|
+
## Dependencies
|
|
47
|
+
|
|
48
|
+
- No new runtime dependencies; the DSH support window is unchanged.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# dsh-vibe-math 2.8.4 — 发布说明
|
|
2
|
+
|
|
3
|
+
> 上一版本:2.8.3。本版是**补丁版**:修正 v5 的**会议看门狗**——它此前会在"已被点名的成员仍在进行中"时就把会议放弃,导致成员的正常发言退化成「会后补记」,并让 `paperEditor="office"` 的定稿被反复拒绝。参数名与默认值、数据格式与目录结构**未变**,DSH 支撑窗口不变,**无需迁移**。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
- 会议**不再**在"被点名、且仍在进行中"的成员交回发言之前被放弃:这类成员最多把会议续命到停滞预算的 **3 倍**,超过硬界才放弃(真正卡死的一轮仍不能拖住整场会议)。
|
|
8
|
+
- 结果是**正常发言回到正常小节**:真机实测修复前一次运行出现 **1067** 次「会后补记」、**863** 次定稿被拒;修复后会议能真正收齐发言,「会后补记」退回为**保险**而非常态。
|
|
9
|
+
- `paperEditor="office"` 的定稿闸门因此更容易被真正满足(它要求本张论文至少有 1 条所办消息与 1 次**记录在案**的会议)。
|
|
10
|
+
- 文档写明会议何时会被放弃、放弃如何具名、以及放弃**之后**到达的发言如何处置。
|
|
11
|
+
|
|
12
|
+
## 新增
|
|
13
|
+
|
|
14
|
+
- 无。(本版为修复版:没有新增参数、字段或工具。)
|
|
15
|
+
|
|
16
|
+
## 变更
|
|
17
|
+
|
|
18
|
+
- 会议停滞判定:软界(`2 × activityTimeoutMs`,默认 240 s)内,只要仍有**被点名的成员在进行中**就不放弃;超过 **3 倍**(默认 720 s)的硬界则照旧放弃。
|
|
19
|
+
- `docs/final-paper.md` §7 增补"会议何时会被放弃"的口径(含软界/硬界与「会后补记」的关系)。
|
|
20
|
+
|
|
21
|
+
## 修复
|
|
22
|
+
|
|
23
|
+
- 症状:成员(含**召集人**)在会议里发言后,纪要里却只留下「会后补记」条目;一次真机运行累计 **1067** 次补记,并且 `paperEditor="office"` 的定稿被 `V5_PAPER_CONSULT_REQUIRED` 拒绝 **863** 次。原因:会议停滞只按"最后一次发言"计时,**不看被点名的成员是否仍在忙**;真机上成员的一整轮常常超过 240 s 的预算,于是会议在看门狗手里被放弃,随后到达的发言只能走补记通道。现在"被点名且仍在忙"的成员会把放弃推迟到硬界(3 倍预算),正常发言因此回到会议的正常小节。守卫:`tests/e2e-v5-round2.test.mjs`(`★ [real1004-stall]`,**源码级不变量**:该分支的真机行为无法在随包套件里确定性触发,故判据是"源码不变量 + 真机复测")、`tests/v5-institute-fixes.mutants.mjs`(**19/19** 具名红)。
|
|
24
|
+
|
|
25
|
+
## 兼容性与迁移
|
|
26
|
+
|
|
27
|
+
- 破坏性变更:**无**。
|
|
28
|
+
- 纯新增:无新增字段(仅行为与文档变化)。
|
|
29
|
+
- 迁移:无需迁移。
|
|
30
|
+
- DSH 支撑窗口不变(见 `package.json` 的 `engines.dsh`)。
|
|
31
|
+
|
|
32
|
+
## 已知限制
|
|
33
|
+
|
|
34
|
+
- 会议看门狗分支的**真机**行为只能由真机复测确认(随包套件里心跳延时不可控 ⇒ 该分支不能确定性触发;因此守卫是**源码不变量**,这一点已在测试注释与提交信息里写明)。
|
|
35
|
+
- **非标准位置的 TeX 仍不会被自动发现**:默认探测只覆盖 PATH 与文档化的常见安装根。
|
|
36
|
+
- 随包套件**不覆盖宿主层行为**(例如"中断后调用方被丢弃")。
|
|
37
|
+
- **根代理提问会阻塞整所流程**(运维注意项,不改产品):长跑时请把问题与验收条件一次说清。详见 [`slv-playbook.md`](./slv-playbook.md) §5。
|
|
38
|
+
|
|
39
|
+
## 验证方式
|
|
40
|
+
|
|
41
|
+
- `node tests/run-tests.mjs` —— 全部通过(**106** 项:44 套件 + 62 探针)。
|
|
42
|
+
- `node scripts/release-check.mjs` —— 退出 0;发布后加 `--registry` 比对线上 shasum 与本地 tarball。
|
|
43
|
+
- 本版修复:`node tests/e2e-v5-round2.test.mjs`(`★ [real1004-stall]`,实测 `passed=436 failed=0`)与 `node tests/v5-institute-fixes.mutants.mjs`(**19/19** 具名红)。
|
|
44
|
+
- 真机复测配方:用 registry 上的精确版本(`-Pin <version>`)跑一个**精简**会议+论文任务(跳过研究),统计「会后补记」次数(修复前 1067 次)与定稿被拒次数(修复前 863 次)应显著下降。
|
|
45
|
+
|
|
46
|
+
## 依赖
|
|
47
|
+
|
|
48
|
+
- 无新增运行时依赖;DSH 支撑窗口不变。
|
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.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0",
|
|
@@ -209,7 +209,11 @@
|
|
|
209
209
|
"docs/release-notes/RELEASE-NOTES-2.8.1.md",
|
|
210
210
|
"docs/release-notes/RELEASE-NOTES-2.8.1.en.md",
|
|
211
211
|
"docs/release-notes/RELEASE-NOTES-2.8.2.md",
|
|
212
|
-
"docs/release-notes/RELEASE-NOTES-2.8.2.en.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",
|
|
215
|
+
"docs/release-notes/RELEASE-NOTES-2.8.4.md",
|
|
216
|
+
"docs/release-notes/RELEASE-NOTES-2.8.4.en.md"
|
|
213
217
|
],
|
|
214
218
|
"license": "MIT",
|
|
215
219
|
"keywords": [
|
|
@@ -239,7 +243,7 @@
|
|
|
239
243
|
},
|
|
240
244
|
"minVersion": "0.1.2-alpha.4",
|
|
241
245
|
"testedVersion": "0.2.0-rc.2",
|
|
242
|
-
"compatNote": "v2.8.
|
|
246
|
+
"compatNote": "v2.8.4 补丁版:修 v5 的会议看门狗。① 停滞判定不再只看\"最后一次发言\":软界(2 × activityTimeoutMs,默认 240 s)内只要仍有**被点名的成员在进行中**就不放弃会议,超过硬界(3 倍,默认 720 s)照旧放弃 —— 看门狗保留牙齿,真正卡死的一轮不会拖住整场会议。② 效果:成员(含召集人)的正常发言回到会议正常小节,「会后补记」退回为保险;修复前真机实测一次运行出现 1067 次补记、863 次 paperEditor=\"office\" 定稿被 V5_PAPER_CONSULT_REQUIRED 拒绝。③ docs/final-paper.md §7 写明会议何时被放弃、如何具名、以及放弃后到达的发言如何处置。参数名与默认值、数据格式与目录结构均未变;DSH 支撑窗口不变;无需迁移。",
|
|
243
247
|
"compatibility": {
|
|
244
248
|
"dshReleases": {
|
|
245
249
|
"0.1.2-alpha.4": "compatible",
|
|
@@ -1350,6 +1350,20 @@ console.log('\n[25] paper idempotency: repeated triggers only fill missing artif
|
|
|
1350
1350
|
const stF = await h.callTool('vibe_v5_status', {})
|
|
1351
1351
|
assert(stF.paper.status === 'writing' && stF.paper.round === 1, 'the forced run restarted at round 1')
|
|
1352
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
|
+
}
|
|
1353
1367
|
|
|
1354
1368
|
// ---------- 26. fake LaTeX: the success path produces paper.pdf ----------
|
|
1355
1369
|
console.log('\n[26] fake LaTeX compiler: success path produces paper.pdf')
|
|
@@ -1452,6 +1466,69 @@ console.log('\n[27] fake LaTeX compiler: repair path and persistent-failure degr
|
|
|
1452
1466
|
assert(tp.length >= 1 && joined.length === 0,
|
|
1453
1467
|
'★ [task-17/v5] an ABSOLUTE explicit paperLatexCommand is probed ALONE — triedPaths never joins it onto a known TeX root (triedPaths=' + JSON.stringify(tp) + ')')
|
|
1454
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)')
|
|
1497
|
+
}
|
|
1498
|
+
// real1004-stall (found on a real host with 2.8.3): the meeting watchdog measured "stall" from the last INPUT and
|
|
1499
|
+
// ignored whether an ASKED member was still busy. A member's turn easily outlives the budget on a real host, so
|
|
1500
|
+
// meetings were abandoned mid-turn and the speeches could only land as "late notes" (that run: 1067 late notes
|
|
1501
|
+
// and 863 refused office finalisations). A meeting must survive while an asked speaker is in flight.
|
|
1502
|
+
{
|
|
1503
|
+
const hs = makeHost({ pluginModule })
|
|
1504
|
+
await hs.callTool('vibe_v5_start', { problem: PROBLEM, researcherCount: 1 })
|
|
1505
|
+
await hs.settleSpawns()
|
|
1506
|
+
await hs.callTool('vibe_v5_set', { activityTimeoutMs: 300 }) // soft stall = 600 ms, hard bound = 1800 ms
|
|
1507
|
+
const mts = await hs.callTool('vibe_v5_meeting', { agenda: 'real1004-stall:被点名的成员仍在忙', kind: 'sync' })
|
|
1508
|
+
const midS = mts && mts.meeting
|
|
1509
|
+
assert(!!midS, 'precondition: the meeting is convened (' + JSON.stringify(mts).slice(0, 110) + ')')
|
|
1510
|
+
// acad is asked and NOT answered, so it stays BUSY. Nobody submits an input, so `lastInputAt` keeps ageing.
|
|
1511
|
+
// NOTE (why this is a SOURCE-level invariant): the watchdog branch runs from a heartbeat whose armed delay is
|
|
1512
|
+
// not controllable from this harness (the plugin arms `ctx.timeout` with delays up to tens of seconds), so a
|
|
1513
|
+
// runtime assertion here cannot be made to FAIL on the unfixed code — it would be a vacuous guard. The
|
|
1514
|
+
// behaviour is instead pinned by (a) this source invariant and (b) the real-host re-test, which is the
|
|
1515
|
+
// acceptance criterion of the board task (measured there: 1067 late notes before the fix).
|
|
1516
|
+
const wA = await hs.peekWakeOf('acad', 3000)
|
|
1517
|
+
assert(!!wA, 'precondition: a member was asked to speak (so it is busy) while no input has arrived')
|
|
1518
|
+
await sleep(900) // past the SOFT bound, inside the hard one
|
|
1519
|
+
const mpathS = join(hs.WS, 'VibeMath', 'Projects', 'default', 'Institutes', 'institute', 'Shared', 'Meetings', midS + '.md')
|
|
1520
|
+
assert(!readFileSync(mpathS, 'utf8').includes('因长时间无新发言而被放弃'),
|
|
1521
|
+
'no input has arrived yet, so nothing may close this meeting: ' + readFileSync(mpathS, 'utf8').slice(0, 160))
|
|
1522
|
+
hs.fireEnd(wA.childId, { input: 'real1004-stall:我被点名后仍在忙,现在交回我的发言。', contextPct: 20 })
|
|
1523
|
+
await sleep(300)
|
|
1524
|
+
const docS = readFileSync(mpathS, 'utf8')
|
|
1525
|
+
assert(docS.includes('### acad') && !docS.includes('(会后补记'),
|
|
1526
|
+
'a speech from an ASKED member lands in the NORMAL section (never as a late note)')
|
|
1527
|
+
const v5src = readFileSync(PLUGIN, 'utf8') // PLUGIN points at the mutated COPY when a family runs this suite
|
|
1528
|
+
assert(v5src.includes('const inFlight = meeting.order.some((id) => busy.has(id))') &&
|
|
1529
|
+
v5src.includes('if (stale >= recoverStallMs() && (!inFlight || stale >= recoverStallMs() * 3)) {'),
|
|
1530
|
+
'★ [real1004-stall] the meeting watchdog exempts ASKED-but-busy speakers up to a hard bound (a real host abandoned meetings mid-turn: 1067 late notes, 863 refused finalisations)')
|
|
1531
|
+
}
|
|
1455
1532
|
|
|
1456
1533
|
// ---------- 28. office-only surface + id normalisation ----------
|
|
1457
1534
|
console.log('\n[28] the final-paper surface is office-only and its directory id cannot escape Paper/')
|
|
@@ -219,6 +219,30 @@ const FAMILIES = [
|
|
|
219
219
|
to: ": ''",
|
|
220
220
|
expect: /\[task-18\/v5\] the DELIVERED paper.md and paper.tex/,
|
|
221
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/,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
// real1004-stall: the meeting must NOT be abandoned while an asked speaker is still in flight — doing so is
|
|
240
|
+
// what turned ordinary speeches into "late notes" on a real host (1067 of them).
|
|
241
|
+
name: 'real1004-stall: the meeting is abandoned while an asked member is still busy',
|
|
242
|
+
from: 'if (stale >= recoverStallMs() && (!inFlight || stale >= recoverStallMs() * 3)) {',
|
|
243
|
+
to: 'if (stale >= recoverStallMs()) {',
|
|
244
|
+
expect: /\[real1004-stall\] the meeting watchdog exempts ASKED-but-busy speakers/,
|
|
245
|
+
},
|
|
222
246
|
]
|
|
223
247
|
let red = 0
|
|
224
248
|
for (const f of FAMILIES) { const ok = runFamily(f); if (ok) red++ }
|
|
@@ -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(),
|
|
@@ -5034,7 +5040,13 @@ export function apply(ctx) {
|
|
|
5034
5040
|
// without a heartbeat nothing would retry it once the lock clears.
|
|
5035
5041
|
if (finalizeLock) { armHeartbeat(); return }
|
|
5036
5042
|
const stale = now() - Number(meeting.lastInputAt || meeting.startedAt || now())
|
|
5037
|
-
|
|
5043
|
+
// real1004-stall: a member's TURN can outlive the stall budget on a real host, and the old check ignored
|
|
5044
|
+
// that — the meeting was abandoned while an ASKED member was still busy, so its speech could only land as
|
|
5045
|
+
// a "late note" (measured on one real run: 1067 late notes and 863 refused office finalisations). Never
|
|
5046
|
+
// abandon while an asked speaker is in flight — but keep the watchdog's teeth: past 3× the budget even an
|
|
5047
|
+
// in-flight member no longer holds the meeting open (a genuinely wedged turn must not wedge the meeting).
|
|
5048
|
+
const inFlight = meeting.order.some((id) => busy.has(id))
|
|
5049
|
+
if (stale >= recoverStallMs() && (!inFlight || stale >= recoverStallMs() * 3)) {
|
|
5038
5050
|
const abandoned = meeting
|
|
5039
5051
|
meeting = null
|
|
5040
5052
|
await appendMeetingTail(abandoned, '⚠ 本次会议因长时间无新发言而被放弃(看门狗);团队回到自组织推进。')
|
|
@@ -5623,11 +5635,20 @@ export function apply(ctx) {
|
|
|
5623
5635
|
}
|
|
5624
5636
|
const consult = Object.assign({ messages: 0, meetings: 0 }, p.consult || {})
|
|
5625
5637
|
if (!(Number(consult.messages) > 0) || !(Number(consult.meetings) > 0)) {
|
|
5638
|
+
// real1004-consult: `force` REWRITES the paper (docs/final-paper.md: "force 重写已定稿的论文"), so the
|
|
5639
|
+
// new paper starts with consult={0,0}. That is intended — but the refusal used to say nothing about it:
|
|
5640
|
+
// on a real host the office was rejected ELEVEN times and only the members could infer why their
|
|
5641
|
+
// earlier meeting had stopped counting. Name the reset, its reason, and what to do about it.
|
|
5642
|
+
const restarted = p.forced === true
|
|
5626
5643
|
return {
|
|
5627
5644
|
ok: false, code: 'V5_PAPER_CONSULT_REQUIRED',
|
|
5628
5645
|
message: 'paperEditor="office" 要求所办先与全所交流、商讨、优化、审查:至少 1 条所办消息(vibe_v5_message)+ 至少 1 次会议(vibe_v5_meeting),然后才能定稿。当前:messages=' +
|
|
5629
|
-
Number(consult.messages || 0) + ', meetings=' + Number(consult.meetings || 0)
|
|
5646
|
+
Number(consult.messages || 0) + ', meetings=' + Number(consult.meetings || 0) +
|
|
5647
|
+
(restarted
|
|
5648
|
+
? '。注意:本论文由 force 重启(forceReason=' + String(p.forceReason || '') + ')⇒ consult 计数随新论文重置为 0,重启前的消息/会议不再计入,请**重新**征询(两个计数记录在 paper.meta.json)'
|
|
5649
|
+
: ''),
|
|
5630
5650
|
consult: { messages: Number(consult.messages || 0), meetings: Number(consult.meetings || 0) },
|
|
5651
|
+
restarted,
|
|
5631
5652
|
}
|
|
5632
5653
|
}
|
|
5633
5654
|
const fin = {
|
|
@@ -6808,6 +6829,20 @@ export function apply(ctx) {
|
|
|
6808
6829
|
const rel = 'Shared/Meetings/' + meeting.id + '.md'
|
|
6809
6830
|
const prev = (await readTextRel(rel)) || ('# 会议纪要|' + meeting.id + '\n\n')
|
|
6810
6831
|
await writeTextRel(rel, prev + '### ' + member.id + '\n' + (text || '(无发言)') + '\n\n')
|
|
6832
|
+
} else if (lastMeetingId && typeof p.input === 'string' && p.input.trim()) {
|
|
6833
|
+
// real1004-minutes: no live meeting, yet the member submitted a speech (a real host showed exactly
|
|
6834
|
+
// this: `input` kept arriving after the meeting had been closed/abandoned). It used to be dropped
|
|
6835
|
+
// SILENTLY — the minutes then declared a speaking order that never appeared in the file (`- 发言顺序:
|
|
6836
|
+
// r-1 → acad` with only `### r-1` present). Append it as a clearly marked late note instead, keyed on
|
|
6837
|
+
// ANY wake (not only meeting wakes: the observed submissions arrived on later rounds), de-duplicated by
|
|
6838
|
+
// text so repeated submissions do not pile up.
|
|
6839
|
+
const late = String(p.input).trim()
|
|
6840
|
+
const rel = 'Shared/Meetings/' + lastMeetingId + '.md'
|
|
6841
|
+
const prev = (await readTextRel(rel)) || ('# 会议纪要|' + lastMeetingId + '\n\n')
|
|
6842
|
+
if (prev.indexOf(late) === -1) {
|
|
6843
|
+
await writeTextRel(rel, prev + '### ' + member.id + '(会后补记:该发言到达时会议已收束)\n' + late + '\n\n')
|
|
6844
|
+
await saveChatLine('【会议 ' + lastMeetingId + '】' + member.id + ' 的发言在会议收束后到达,已作为补记写入纪要(不再静默丢弃)。')
|
|
6845
|
+
}
|
|
6811
6846
|
}
|
|
6812
6847
|
if (p.solved !== undefined) {
|
|
6813
6848
|
await writeTextRel('Shared/State-of-institute.md', [
|