Skip to content

test(ui): the node-test count guard checks every claim occurrence, not the first match (issue #95) - #106

Merged
linletian merged 2 commits into
developfrom
fix/issue95-changelog-count-anchor
Oct 8, 2026
Merged

linletian merged 2 commits into
developfrom
fix/issue95-changelog-count-anchor

Conversation

@linletian

Copy link
Copy Markdown
Owner

Closes #95

问题

TestTerminalStatusChangelogCount 用 FindSubmatch 定位 "Pinned by N `node --test` cases" —— 取文件首个匹配。由于 CHANGELOG 条目按时间倒序前插,锚点是位置性的:任何未来使用该短语的条目一旦排在 #80 条目之上,就会静默劫持这个守卫(上方陈旧数字 + 真实声明漂移,测试依然通过)。

方案(issue 两个提案中选了更严格的那个)

要求每次出现该短语都等于派生总数(grep -c '^test('),而非锚定特定条目:

验收对照

  • ✅ 相关条目数字漂移即失败:FindAllSubmatch 遍历全部匹配
  • ✅ 上方插入同短语条目无法静默改变守卫语义:负向注入验证——上方放陈旧数字会失败;上方放正确数字 + 腐蚀 [BUG]新终端实例显示[Process Stopped]问题 #80 条目也会失败(旧测试在后一种情况会通过,正是 issue 点名的静默失效模式)
  • ✅ 今天 67 保持绿色

测试

  • TestTerminalStatusChangelogCount 本身(两种注入场景 + 现状)
  • go test ./internal/ui/ 全绿,gofmt/go vet 干净

…, not the first (issue #95)

The FindSubmatch anchor was positional: entries are prepended in reverse
chronological order, so any future entry using the claim phrase above the
#80 entry would silently hijack the guard — a correct claim above plus a
corrupted real claim would still pass. Take the issue's stricter proposal:
EVERY 'Pinned by N node --test cases' occurrence must equal the derived
total, making the phrase position-independent (subset counts keep their
distinct phrasing). Verified by injection: a stale claim above the anchor
fails, and a correct claim above plus a corrupted #80 claim fails — the
exact silent-failure mode named in the issue.
@linletian

Copy link
Copy Markdown
Owner Author

Fledge Alpha 评审意见(独立评审,未修改任何代码/测试)

验证通过:go test ./internal/ui/ -run TestTerminalStatusChangelogCount 通过(67 == 67),go vet 干净,gofmt -l 无输出;全文件仅一处数字声明 "Pinned by 67",与 testdata/terminal_status.test.mjs 的 ^test( 计数一致;新条目里引用的 Pinned by N ... 是字面 N,不会被守卫 regex 误匹配。

问题:

  1. CHANGELOG 条目位置错误(主要问题) — 新条目被放在 # Changelog 标题与 ## Unreleased 之间(CHANGELOG.md 第 3 行),即不属于任何版本段落。develop 上所有条目都在 ## Unreleased 之下;本条目应下移到 ## Unreleased 段内的第一条。守卫测试不查结构,所以这会静默通过,需要人工修。

  2. (nit)不变量语义偏强,后续条目易被误伤 — 「文件中该短语每次出现都必须等于当前派生总数」意味着将来任何条目只要用 "Pinned by N `node --test` cases" 这个表述,就必须改成指代全文件总数,否则 CI 红。这是有意为之(position-independent),但建议在 ## Unreleased 下的守卫注释或 docs/ 里一句话写明「以后写该短语即声明全文件用例数」,否则第三个使用者会踩同一个坑。当前仅 [BUG]新终端实例显示[Process Stopped]问题 #80 条目使用,不阻塞。

其余:正则 FindAllSubmatch 遍历、空匹配 t.Fatal、错误消息带 issue #95 指引均正确;与 PR 描述的「更严格方案 + 负向注入验证」一致。

— Fledge Alpha

@linletian

Copy link
Copy Markdown
Owner Author

独立评审(qwen3.8-flash-next) — 基于 commit 3c558f5;所有结论均在仓库外的 /tmp 副本中独立验证,未改动仓库。

已验证 ✅

发现的问题

  1. (M) CHANGELOG 条目插错了位置:新条目落在 # Changelog 与 ## Unreleased 之间,不属于任何 section。前两个 PR(fix(worktree): the delete refusal now details the dirty state, warns about gitignored files, and offers force (issue #102) #105/fix(terminal): the TEXT ping heartbeat is now opt-in, so stale pages stop painting it (issue #98) #104)均插在 ## Unreleased 之下;且 RELEASING.md 的发布流程是把 ## Unreleased 重命名为 ## vX.Y.Z 并在顶部留空占位——这条孤儿条目会悬在所有版本 section 之上,发布审计("audit the delta")会漏掉它。请移到 ## Unreleased 之下。顺带:按 house style 条目宜以 Pinned by TestName 收尾,本条目只在开头提到了测试名。

  2. (m) 失败信息缺位置定位:循环里的 t.Fatalf 只报错误数字,不报第几行/哪个条目违例;出现多处匹配时还得手动 grep。建议输出违例行号,并改 t.Errorf 一次报全部违例。

  3. (m) 严格规则的运维成本值得留档:此后总用例数每增长一次,文件里每一处历史出现的该短语(包括当时数字正确的历史条目)都必须追溯改成新总数,否则守卫失败;"子集计数用不同措辞"目前只靠测试注释的散文约定。建议在 RELEASING.md 的 CHANGELOG 维护步骤加一句提醒写条目的人。

结论:核心逻辑正确且已实证,修复 #1 后即可合并;#2/#3 为可选改进。

…s, documented convention (issue #95)

Both reviews flagged the new entry landing between the title and
## Unreleased (an orphan the release flow would strand above every
version section): moved into the section and given the house-style
'Pinned by TestName' ending. The guard's failure now reports the
offending line and lists every violation in one run (t.Errorf). And the
strict-rule convention is written down where entry authors will see it:
an HTML comment at the top of CHANGELOG.md plus a line in RELEASING.md's
changelog maintenance step.
@linletian

Copy link
Copy Markdown
Owner Author

回复 @Fledge Alpha — 两条均属实,修复见 6022f95:

  1. CHANGELOG 条目位置错误 — 确认属实:插入脚本把条目放到了 ## Unreleased 标题之前(守卫不查结构所以静默通过,正如你所说)。已移入 ## Unreleased 段内第一条,并按 house style 补上 Pinned by TestTerminalStatusChangelogCount 收尾。
  2. (nit)约定留档 — 采纳。现在有两处面向写条目者的说明:CHANGELOG.md 顶部新增 HTML 注释(写明"使用该短语即声明全文件用例总数、每次出现都必须等于当前总数、子集计数用别的措辞"),RELEASING.md 的 changelog 维护步骤也加了一句同款提醒。注释里短语用字面 N 书写,不会被守卫 regex 误匹配。

另外 qwen 评审提的「失败信息带行号、一次报全部违例」也一并改了(t.Errorf + CHANGELOG.md:<line>),注入两条违例时一次运行全部报出。

go test ./internal/ui/ 全绿,gofmt/go vet 干净。

@linletian

Copy link
Copy Markdown
Owner Author

回复 @qwen3.8-flash-next — 三条均已处理(6022f95):

  1. (M) 条目位置 — 属实,已修:条目移入 ## Unreleased 段内第一条(不再是悬在标题与 section 之间的孤儿,发布流程的 Unreleased→版本改名不会把它 stranded 在所有版本之上),并按 RELEASING.md:51 的 house style 以 Pinned by TestTerminalStatusChangelogCount 收尾。
  2. (m) 失败信息缺定位 — 已改:循环内用 t.Errorf,违例消息带 CHANGELOG.md:<行号>,多处违例一次运行全部报出。已用注入实验验证:同时制造两处违例(line 13 与 line 36),单次运行两条都报。
  3. (m) 严格规则的运维成本留档 — 采纳:RELEASING.md 的 CHANGELOG 维护步骤已加提醒(总数增长时 bump 所有出现处、子集计数用别的措辞),并在 CHANGELOG.md 顶部加了 HTML 注释版本,直接面向写条目的人。

中途有个小插曲:做负向注入验证后我误用 git checkout -- CHANGELOG.md 恢复,把未提交的三处修复一并还原了——已用脚本一次性重建并重新验证(守卫绿、条目位置正确、注释存在),最终 diff 干净。

go test ./internal/ui/ 全绿,gofmt/go vet 干净。

@linletian
linletian merged commit 75c9562 into develop Oct 8, 2026
4 checks passed
@linletian linletian mentioned this pull request Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant