Skip to content

feat(lint): pre-commit + CI 检 .lvt 文件 ~ 误用 + 一次清存量#897

Merged
Liam0205 merged 10 commits into
masterfrom
feat/lint-test-files
Jun 26, 2026
Merged

feat(lint): pre-commit + CI 检 .lvt 文件 ~ 误用 + 一次清存量#897
Liam0205 merged 10 commits into
masterfrom
feat/lint-test-files

Conversation

@Liam0205

Copy link
Copy Markdown
Contributor

Closes #893

背景

issue #893 指出 .lvt 测试文件里在非 expl3 catcode scheme 时把 ~ 当空格用, 导致 .tlg baseline 里有不必要的字面 ~. 这次一并解决 + 加 lint 防新增.

改动

1. 加入 lint (防新增)

  • .githooks/check-test-tilde.sh: 共享脚本, 从 stdin 接受 diff, 仅检 + 行里 \TEST / \BEGINTEST / \TYPE 大括号内含 ~ 的命中. 用 group-depth-aware 状态机过滤 \ExplSyntaxOn / \ExplSyntaxOff —— 进入大括号 group (如 \sys_if_engine_luatex:F { \ExplSyntaxOff ... }) 后忽略内层 ExplSyntax 切换, 不误伤 expl3 内的合法 ~.
  • .githooks/pre-commit: 本地 commit 时跑 check (git diff --cached -U0).
  • .github/workflows/lint-test-files.yml: PR 触发同款检查 (PR base→head diff), 用 actions/checkout@v7 (最新主版本).
  • .githooks/README.md: 同步.

2. 一次清存量

  • scripts/fix-test-tilde.py: 用 .tlg 作 oracle. 找 .tlg 里 text~text 形态的 ~ (排除 LaTeX font tracing 的孤立 ~), 然后改对应 .lvt 的 \TEST / \BEGINTEST / \TYPE 大括号内 ~ 为空格. 比写 LaTeX catcode 状态机靠谱 —— LaTeX 自己已求过 catcode, .tlg 字面就是 ground truth.
  • 实际改: 45 .lvt 文件, ~280 处. 影响包: ctex, xeCJK.

3. 同步 .tlg baseline

  • ctex: 改了 verb01, verbatim01 两测试; .tlg 全套 (luatex/pdftex/uptex/xetex) 同步. 顺带带出 heading-name01.tlg 的 LaTeX kernel baseline 漂移 (Section~2Section 2), 是 LaTeX kernel 自身更新引起, 不是这次改动. 接受.
  • xeCJK: 改了 43 个测试, .tlg 同步. 全部 enabled:~10.0pt 类输出归一为 enabled: 10.0pt.

边界情况

verb01.lvt 行 7 \TYPE { Only~ tested~ with~ LuaTeX. } 在 expl3 group 内 (\sys_if_engine_luatex:F { ... }), oracle 自动跳过, 不动 — 在 expl3 catcode 下 ~ 是合法空格, 改成普通空格反而被 ignore (catcode 9) 导致单词粘连. 这是用 .tlg 作 oracle 的好处, 不需要静态分析 catcode/group.

verb01.luatex.tlg 改后仍残留 verb =~42.94794pt (= 后有 ~). 该 ~\widthVerb 展开时 xeCJK 在 LaTeX-active ~ mode 下的注入行为, 与 .lvt 内容无关. 接受.

Test plan

  • 本地跑 l3build save ctex (4 engine) 与 xeCJK (xetex) 通过, .tlg 已同步
  • hook 自测: ExplSyntaxOn 段跳过, ExplSyntaxOff 段命中, group 内嵌套正确处理
  • CI: lint-test-files workflow 在 PR 上跑通 (不该有命中, 因为存量已清完)
  • CI: ctex/xeCJK 主测试通过

issue #893 指出 .lvt 测试文件里在非 expl3 catcode scheme 时把 `~` 当
空格用, 导致 .tlg baseline 里有不必要的 `~` 字面字符. 本 commit 三件事:

1. 加入检查工具
   - .githooks/check-test-tilde.sh: 共享脚本, 从 stdin 接受 git diff,
     仅检 .lvt 文件 `+` 行里 \TEST/\BEGINTEST/\TYPE 大括号内含 `~`
     的命中. 用 group-depth-aware 状态机过滤 \ExplSyntaxOn/Off, 进入
     大括号 group (例 \sys_if_engine_luatex:F { \ExplSyntaxOff ... })
     后忽略内层 ExplSyntax 切换, 不误伤 expl3 内的合法 `~`.
   - .githooks/pre-commit: 本地提交时跑 check (git diff --cached -U0).
   - .github/workflows/lint-test-files.yml: PR 触发同款检查, 用
     actions/checkout@v7 (最新主版本).
   - .githooks/README.md 同步.

2. 一次性修存量
   - scripts/fix-test-tilde.py: 用 .tlg 作 oracle. 找 .tlg 里 'text~text'
     形态的 `~` (排除 LaTeX font tracing 的孤立 `~`), 然后改对应 .lvt
     的 \TEST/\BEGINTEST/\TYPE 大括号内 `~` 为空格. 比写 LaTeX catcode
     状态机靠谱 — LaTeX 自己已求过 catcode, .tlg 字面是 ground truth.
   - 实际改: 45 .lvt 文件, ~280 处. 影响包: ctex, xeCJK.

3. 跑 l3build save 更新 .tlg baseline
   - ctex: 改了 verb01, verbatim01, .tlg 全套 (luatex/pdftex/uptex/
     xetex) 同步. 顺带带出 heading-name01.tlg 的 LaTeX kernel baseline
     漂移 (Section~2 → Section 2 等), 是 LaTeX kernel 自身更新引起,
     不是我们改动. 接受.
   - xeCJK: 改了 43 个测试, .tlg 同步. 全部 'enabled:~10.0pt' 类
     输出归一为 'enabled: 10.0pt'.

注意: verb01.lvt 行 7 (\TYPE { Only~tested~with~LuaTeX. }) 在 expl3
group 内, 自动跳过 — 在 expl3 catcode 下 `~` 是合法空格, 普通空格
反而被 ignore. 这是 oracle 自动避开的好处.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 审查

项目 结果
结论 ✅ APPROVE
审查截止 873a2b279666a48b4f2dca3db79957753a4e68c3

代码良好,设计思路清晰,无阻塞问题。

审查要点

1. check-test-tilde.sh (lint 核心脚本)

  • group-depth-aware 状态机正确处理了 ExplSyntaxOn/Off 在嵌套 brace group 内的局部切换(如 sys_if_engine_luatex:F { ExplSyntaxOff ... }),避免误报。
  • 用 [^a-zA-Z] 替代 \b 做 word-boundary 检查是 POSIX awk 下的正确做法。
  • 两阶段架构(awk 提取候选 + 按文件缓存 line-state 表查询)性能合理,缓存避免重复解析。

2. CI workflow (lint-test-files.yml)

  • pull_request + paths 过滤 + fetch-depth: 0 配置正确,确保 merge-base diff 可用。
  • permissions: contents: read 最小权限,符合安全规范。
  • actions/checkout@v7 为当前最新主版本。

3. fix-test-tilde.py (一次性修复脚本)

  • 用 .tlg 作 oracle 的思路巧妙且可靠——LaTeX 已经完成了 catcode 求值,.tlg 字面输出即 ground truth,免去了静态分析 catcode/group 嵌套的复杂度。
  • TLG_TEXT_TILDE 正则正确排除了 LaTeX font tracing 中的孤立 tilde。
  • 用 bytes 模式处理文件避免了 GBK 编码 .lvt 文件的解码问题。

4. 存量清理 (.lvt + .tlg 批量改动)

  • 45 个 .lvt 文件、约 280 处替换全部为机械式 tilde 到空格,模式一致。
  • .tlg baseline 同步更新无遗漏。
  • verb01.luatex.tlg 中残留的 verb =~42.94794pt 已在 PR 描述中正确解释(xeCJK widthVerb 展开注入,非 .lvt 内容)。
  • heading-name01.tlg 的 Section~2Section 2 属 LaTeX kernel upstream 变更,合理接受。

5. 边界情况处理

  • verb01.lvt 第 7 行 expl3 group 内的 tilde 被 oracle 正确跳过。
  • pre-commit hook 在 CI 环境 ($CI / $GITHUB_ACTIONS) 自动短路,与 workflow 不冲突。

🟢 小建议 (1)

  • 文件: check-test-tilde.sh
  • 观察: awk 候选提取阶段的正则按行匹配且不跨嵌套大括号。如果未来出现 \TYPE{outer{inner~text}more} 这种嵌套写法,内层大括号内的 tilde 不会被匹配到(false negative)。不过 false negative 对 lint 来说是安全的(宁可漏报不误报),且目前测试文件中未见此模式,不影响当前功能。仅作记录。

PR #897 上 CI 揭示我本地跑 l3build save 时把 heading-name01.tlg 改成
"Section 2" (无 ~), 但 CI 上 LaTeX 内核输出仍是 "Section~2" (有 ~).
这是我本地 fmtutil-user 同步后内核状态与 CI 不一致带来的, 不是 ctex
本身改动. 之前误判为 "LaTeX kernel 自身漂移, 接受", 应改成 "本地环境
飘了, 不该 commit". 回到 origin/master baseline.

verb01.lvt / verbatim01.lvt 的 .tlg 改动保留 (那些是 .lvt 真的改了
带出来的合理 baseline 更新).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 审查

项目 结果
结论 ✅ APPROVE
审查截止 bc7c2c2 (2 commits)

整体设计清晰,oracle 策略巧妙,双层防线(pre-commit + CI)可靠。发现 1 个重要建议和 2 个小建议。

审查要点

1. check-test-tilde.sh — lint 核心脚本

  • group-depth-aware 状态机正确处理了 ExplSyntaxOn/Off 在嵌套 brace group 内的局部切换,不会误伤 expl3 内的合法 tilde。
  • [^a-zA-Z] 替代 \b 做 word boundary 是 POSIX awk 的正确做法。
  • 两阶段架构(awk 提取候选 + per-file 缓存 line-state 表)性能合理。
  • -U0 + @@ hunk header 的行号追踪逻辑正确。

2. CI workflow (lint-test-files.yml)

  • pull_request + paths 过滤 + fetch-depth: 0 配置正确。
  • permissions: contents: read 最小权限,符合安全规范。
  • BASE_SHA / HEAD_SHA 通过 env 传入而非直接拼入 run,无注入风险。
  • check-test-tilde CI check 已通过 ✅。

3. fix-test-tilde.py — 一次性修复脚本

  • 用 .tlg 作 oracle 免去了静态分析 catcode/group 嵌套的复杂度,LaTeX 自己的 catcode 求值结果就是 ground truth。
  • TLG_TEXT_TILDE 正则正确排除了 LaTeX font tracing 中的孤立 tilde。
  • bytes 模式处理文件避免了 GBK 编码 .lvt 文件的解码问题。

4. 存量清理 (.lvt + .tlg 批量改动)

  • 45 个 .lvt 文件、约 280 处替换全部为机械式 tilde 到空格,模式一致。
  • .tlg baseline 同步更新无遗漏。
  • verb01.luatex.tlg 残留 =~ 已在 PR 描述中正确解释(xeCJK widthVerb 展开注入)。

5. 第二个 commit (revert heading-name01.tlg)

  • 正确识别了 heading-name01.tlg 的 Section~2 到 Section 2 是本地 fmtutil-user 同步状态与 CI 不一致导致的 false positive,而非 LaTeX kernel 真实漂移。回滚到 origin/master baseline 是正确处理。

🟡 重要建议 (1)

declare -A 要求 bash 4+,macOS 默认 bash 3.2 不支持

  • 文件: check-test-tilde.sh:80
  • declare -A explsyntax_cache 使用了关联数组,这是 bash 4.0+ 特性。macOS 系统自带 /bin/bash 为 3.2(Apple 因 GPLv3 许可证不再升级)。
  • 如果贡献者在 macOS 上未通过 Homebrew 安装 bash 4+/5+,#!/usr/bin/env bash 会解析到系统 bash 3.2,导致 pre-commit hook 报 declare: -A: invalid option 并失败。
  • 建议: 在 check-test-tilde.sh 开头或 .githooks/README.md 中注明 bash 4+ 依赖;或者考虑在脚本头部加版本检查,在 bash < 4 时输出提示并 exit 0(不阻塞提交)。

🟢 小建议 (2)

  1. fix-test-tilde.py 文档字符串路径不一致

    • 文件: scripts/fix-test-tilde.py:36
    • docstring 中写的是 python3 tmp/fix-test-tilde.py,但文件实际路径为 scripts/fix-test-tilde.py。虽然是一次性脚本,建议修正以免混淆。
  2. brace depth 计数未排除 TeX 注释行

    • 文件: check-test-tilde.sh:107-111
    • awk 状态机计算 {/} 净差时,没有排除 % 后的注释部分。如果 .lvt 文件中出现 % } 或 % ExplSyntaxOn 这类注释行,可能导致 depth/state 偏移。目前测试文件中此模式极少见,实际影响很低,仅作记录。

🤖 Generated with Claude Code

agentic-pr-review 指出 declare -A 是 bash 4+ 特性, macOS 自带 bash 3.2
不支持. 改用 mktemp 临时目录做 per-source-file 缓存, 跨任意 bash 版本.

顺手修 fix-test-tilde.py docstring 里残留的旧路径 (tmp/ → scripts/).

第三条 nit (brace depth 没排除 % 注释行) 实际影响低, 留作 caveat 不动.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bc7c2c28..3d4a7c61

第三个 commit 正确处理了前次审查的两项建议,代码质量良好。

审查要点

1. declare -Amktemp 临时目录缓存 (check-test-tilde.sh)

  • mktemp -d + trap 'rm -rf "$cache_dir"' EXIT 替代 bash 4+ 的 declare -A,正确解决 macOS 自带 bash 3.2 不支持关联数组的兼容性问题。
  • cache_key()tr '/' '_' 将文件路径转为扁平 key,在本场景(.lvt 文件路径结构稳定)下不会产生碰撞。
  • awk 输出从变量赋值改为直接写文件(> "$cfile"),缓存查询也改为直接 awk 读文件,避免了 shell 变量持有大量文本的开销。
  • [ ! -s "$cfile" ] 检查比原来的 [ -z "${explsyntax_cache[$file]:-}" ] 语义一致且更健壮。

2. fix-test-tilde.py docstring 路径修正 (fix-test-tilde.py)

  • tmp/fix-test-tilde.pyscripts/fix-test-tilde.py,与文件实际位置一致。用法说明同步更新。

审查截止: 3d4a7c6

PR #897 CI 揭示我本地 save 出来的 verb01.luatex.tlg 含 'verb =~42.94794pt'
(=`后多了 ~), 而 CI 实际输出 'verb = 42.94794pt' (干净空格). 这是
我本地 fmtutil-user 同步后的环境与 CI 的细微差异, 不是真实行为.

之前 PR 描述里"\widthVerb 展开时 xeCJK 注入 ~"的猜测是错的, 实际 CI
跑出来就没有这个 ~. 用 CI 输出 (干净空格) 作 ground truth.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bc7c2c28..5835fd14

增量改动共 2 个 commit,均为对前次 review 建议的响应,改动干净无问题。

审查要点

1. check-test-tilde.sh — bash 3.2 兼容修复 (3d4a7c6)

  • declare -A(bash 4+ 关联数组)替换为 mktemp -d 临时目录 + 普通文件缓存,确保 macOS 自带 bash 3.2 可运行。
  • trap 'rm -rf "$cache_dir"' EXIT 正确清理临时目录。
  • cache_key()tr '/' '_' 做路径→文件名映射,对本仓库 .lvt 路径无冲突风险。
  • [ ! -s "$cfile" ] 替代原来的 [ -z "${explsyntax_cache[$file]:-}" ],语义等价。
  • awk 输出重定向到文件(> "$cfile")替代变量赋值,查询时直接 awk ... "$cfile" 替代 echo ... | awk,避免了 subshell 和大变量展开的开销。

2. fix-test-tilde.py — docstring 路径修正 (3d4a7c6)

  • tmp/fix-test-tilde.pyscripts/fix-test-tilde.py,与文件实际位置一致。

3. verb01.luatex.tlg — baseline 修正 (5835fd1)

  • verb =~42.94794ptverb = 42.94794pt:用 CI 实际输出作 ground truth,消除本地环境漂移引入的错误 baseline。之前 PR 描述中"xeCJK widthVerb 注入 ~"的猜测被 CI 输出证伪,处理正确。

审查截止: 5835fd1

之前 fix-test-tilde.py 用 .tlg 字面字符作 oracle, 有两个问题:

1. .tlg 漏检: .tlg 只记录实际执行到的输出, 死代码路径 / 未触发的
   \if 分支 / \showbox 之后中断的代码不出 .tlg, oracle 看不到. 实测
   仓库里有 9 处这种漏检 (5 个 xeCJK 测试).
2. 不对称: hook 用状态机判定, fix 用 oracle, 标准不同步, 维护 nightmare.

改用与 .githooks/check-test-tilde.sh 完全对称的 group-depth-aware
状态机, 扫所有 .lvt, 仅 ExplSyntaxOff 段内的 \TEST/\BEGINTEST/\TYPE
大括号内 `~` 替换为空格.

不顺手改 .tlg: .tlg 里的 `~` 可能来自其它命令 (例 ctex 的
\ctexset{section/name = {Section~}} 故意用 `~` 作 nobreakspace,
.tlg 输出 `Section~2` 正确, 不该改). 改完 .lvt 后由 maintainer 跑
l3build save 重 baseline.

实际改了 5 个 xeCJK .lvt (9 处):
- char-let01.lvt
- fntef-font01.lvt
- fntef-nest01.lvt
- fntef-space02.lvt
- fntef-underline01.lvt

本地 l3build check 这 5 个测试通过, .tlg 不需要改 (那些标题在 .tlg
里不出现, 死代码路径).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bc7c2c28..7882f17c
审查截止 7882f17cab8532eca6ee2692f31d174d662efb11

3 个增量 commit 质量良好:bash 3.2 兼容修复合理,fix 脚本从 oracle 切到状态机是正确的架构改进,补漏的 9 处 .lvt 修正验证了切换的必要性。

审查要点

1. 3d4a7c61 — bash 3.2 兼容 (check-test-tilde.sh)

  • declare -Amktemp -d 文件缓存:正确解决 macOS 自带 bash 3.2 不支持关联数组的问题。
  • trap 'rm -rf "$cache_dir"' EXIT 清理到位。
  • cache_key()tr '/' '_' 做路径→文件名映射,简单有效。
  • awk 输出从变量捕获改为重定向到文件,state 查询从 echo | awk 改为直接 awk "$cfile",减少了子进程开销。

2. 5835fd14 — verb01.luatex.tlg baseline 修正

  • verb =~42.94794ptverb = 42.94794pt:用 CI 实际输出替代本地环境的偏移结果,方向正确。与 commit 1 中 heading-name01.tlg 的 revert 同属"本地 fmtutil-user 环境漂移"问题,处理一致。

3. 7882f17c — fix-test-tilde.py 重写为状态机

  • 从 .tlg oracle 切到 group-depth-aware 状态机是关键改进
    • 动机充分:.tlg 只记录执行到的输出,死代码路径 / 未触发的 \if 分支不出 .tlg,oracle 漏检(实测漏了 9 处)。
    • 对称性:hook 和 fix 现在用完全相同的判定逻辑,维护成本降低。
    • 不改 .tlg 的决策正确:.tlg 中的 ~ 可能来自用户故意的 \nobreakspace(如 Section~2),应由 l3build save 重新生成。
  • EXPL_ON / EXPL_OFF 正则后跟 (?:[^a-zA-Z]|$) 防前缀误匹配,与 shell 端一致。
  • bytes 模式处理 GBK 编码 .lvt 安全。
  • 实际补漏 5 个 xeCJK .lvt(9 处),全在 \showbox 中断后的死代码路径,验证了 oracle 的盲区。

🟢 小问题 (2)

  • 文件: .githooks/check-test-tilde.sh
  • 问题: cache_key()tr '/' '_' 映射路径,理论上 a/b_c.lvta_b/c.lvt 会产生键冲突。当前仓库文件结构不会触发,仅作记录。
  • 建议: 如未来需要更健壮,可用 md5sumtr '/' '%'% 不出现在文件名中)。

  • 文件: scripts/fix-test-tilde.py
  • 问题: _sub() 闭包在 for line 循环体内每次迭代重新定义。功能正确,但略有冗余。
  • 建议: 可提到循环外层,通过参数或 nonlocal 控制状态。纯风格建议,不影响正确性。

之前文档只示例 fmtutil-user --byfmt xelatex, 但 ctex 默认跨 4 个
engine 测试 (pdftex / xelatex / lualatex / uplatex), 漏掉任一 engine
fmt rebuild 都会让该 engine 的全部测试 fail 同一种
'expl3.sty Mismatched LaTeX support files' 错误.

补充: pdftex / xelatex / lualatex / uplatex 四条都要跑.
PR #897 上本地 ctex check 49 个 uptex 测试全 fail, 实测原因即此.
@github-actions

Copy link
Copy Markdown
Contributor
PR #897 代码审查 — feat(lint): pre-commit + CI 检 .lvt 文件 ~ 误用 + 一次清存量

审查结论: APPROVE

整体质量很好。方案设计合理——hook 和 CI 联合防线防新增,fix 脚本一次清存量,.tlg 同步跟进。关键的 ExplSyntaxOn/Off + group-depth-aware 状态机设计避免了对 expl3 合法 ~ 的误伤。6 个 commit 的演进思路清晰,特别是从 oracle 方案切换到对称状态机的决策(commit 5)是正确的工程判断。


重要建议 (1)

1. Shell hook 与 Python fix 脚本的正则匹配行为不对称

check-test-tilde.sh L64 的 awk 使用 [^}]* (允许 {),而 fix-test-tilde.py L12 使用 [^{}]* (排除 {})。

效果差异:对于嵌套大括号的情况如 \TYPE{\foo{bar~baz}}

  • Shell hook 报告([^}] 匹配了内层 {
  • Python fix 不会修复([^{}] 排除了 {

当前存量已清完所以没有实际影响,但这意味着 hook 可能报出 fix 脚本修不了的 case。建议统一为 [^{}](更保守)或在 hook 中注明这个差异。


小建议 (2)

1. cache_key() 的路径冲突风险

check-test-tilde.sh L87 用 tr 把 / 替换为 _ 做缓存 key,理论上 a/b_c.lvta_b/c.lvt 会冲突。该仓库路径结构下概率极低,但若想彻底避免,可以用 percent-encoding 或 sha256sum 之类的方案。

2. 状态机的行级粒度限制

Shell 和 Python 的状态机都是按整行判定 ExplSyntaxOn/Off 状态,即同一行如果同时出现 \ExplSyntaxOff\TEST{...} 含 tilde,状态判定取决于 if 语句的执行顺序而非文本位置。当前 .lvt 文件中不太会出现这种情况,但可以在脚本注释中注明这个简化假设。


亮点

  • commit 5 从 .tlg oracle 切到扫 .lvt + 状态机的决策说明了好的工程直觉——hook 与 fix 标准对称、消除死代码漏检。
  • pre-commit 在 CI 环境自动短路 + CI workflow 独立跑同款检查,互不冲突。
  • 用 bytes 模式处理 GBK/iso-8859-1 编码的 .lvt 文件,细节到位。
  • 文档同步完整(README.md + llmdoc)。

Generated with Claude Code

agentic-pr-review 在多轮增量中提出 1 重要 + 4 小问题, 这个 commit
全部处理:

1. [重要] hook awk 正则 [^}]* 与 fix script CMD_PATTERN [^{}]* 不对称
   - hook 改 [^{}]*, 与 Python 端一致
   - 嵌套 \TYPE{\foo{bar~baz}} 这种两边都漏 (false negative 安全方向)
   - 实测仓库 0 处嵌套, 历史存量未受影响

2. [小] cache_key() 路径冲突
   - tr '/' '_' 会让 a/b_c.lvt 与 a_b/c.lvt 撞 key
   - 改用 sha1sum / shasum / od 三段 fallback, 跨 Linux+macOS

3. [小] brace depth 不排除 % 注释行
   - 行内 % \ExplSyntaxOff 或 % } 会让状态机错算
   - awk + Python 端都加 _strip_tex_comment 剥注释 (\% 转义保留)
   - 状态切换 + depth 计数都基于 stripped

4. [小] 状态机行级粒度的简化假设
   - awk + Python docstring 都加注: 同行 ExplSyntaxOff + \TEST{...}
     state 用切换**之后**的 (因为先切 state 再处理本行命中)
   - 实际 .lvt 不会这么写, 记录为未来 caveat

5. [小] python _sub 闭包在每次循环重新定义
   - 提取为模块级 _replace_tildes_in_match()
   - 用 CMD_PATTERN.subn 返回替换次数, 不再 nonlocal changes
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bc7c2c28..7228f363

增量 4 个 commit 全面回应了前两轮审查建议,质量很好。主要改进:

  1. fix script 从 .tlg oracle 改为状态机 — 设计正确,补漏 9 处死代码路径的 ~,且与 hook 完全对称
  2. bash 3.2 兼容declare -A → mktemp + sha1sum/shasum/od 三段 fallback,macOS 开箱可用
  3. TeX 注释剥离 — awk 和 Python 两端都加了 _strip_tex_comment% } / % \ExplSyntaxOff 不再误算 depth/state
  4. hook 正则对齐[^}]*[^{}]* 与 Python 端一致
  5. verb01.luatex.tlg baseline 修正 — 用 CI 实际输出替换本地环境漂移

🟢 小问题 (1)

  • 文件: .githooks/check-test-tilde.sh + scripts/fix-test-tilde.py
  • 问题: TeX 注释剥离逻辑只检查 % 前一个字符是否为 \。对于 \\%(字面反斜杠后跟注释),前一个字符是 \,会被误判为 \% 转义而不剥注释。awk 端和 Python 端 (_strip_tex_comment) 都有这个问题。
  • 影响: 极低。.lvt 测试文件中 \\% 模式基本不存在,且 false negative 是安全方向(漏报不误报)。仅作记录,不阻塞。

审查截止: 7228f36

PR #897 review 第 7 条 review 指出: 之前的 _strip_tex_comment 只看
% 前一个字符是否为 \\, 这判定不严谨. \\\\% 实际是 "字面反斜杠 + 注释
开始", 前面两个 \\ 互为转义对, % 本身不被转义, 应当作注释起点 — 但
我们脚本会误判为转义不剥.

修法 (awk + Python 都改): 数 % 前面**连续**的反斜杠个数, 奇数才是
\\% 转义.

测试 cases:
  %         普通注释        ⟹ 剥
  \\%       \\% 转义         ⟹ 不剥
  \\\\%     字面 \\ + 注释    ⟹ 剥 (前面是修复点)
  \\\\\\%   字面 \\ + \\% 转义 ⟹ 不剥
  \\\\\\\\% 2 字面 \\ + 注释  ⟹ 剥

awk 端 6/6, Python 端 6/6, 全 case 正确.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 bc7c2c28..7b55cc7b

6 个增量 commit 全部是对前两轮 review 建议的响应,质量良好,问题已妥善处理。

审查要点

1. bash 3.2 兼容性修复 (check-test-tilde.sh)

  • declare -A 替换为 mktemp -d + per-file 缓存文件,跨任意 bash 版本。
  • cache_key() 三级 fallback(sha1sum → shasum → od hex),避免路径 tr '/' '_' 冲突。设计合理。

2. awk 正则 [^}]*[^{}]* 对齐 Python 端

  • CMD_PATTERN 保持一致,消除 hook/fix 不对称。嵌套 \TYPE{\foo{bar~baz}} 仍不命中,但如注释所述这是 false negative 安全方向。

3. TeX 注释剥离 (_strip_tex_comment)

  • awk 端和 Python 端都新增了 % 前连续反斜杠奇偶判定:偶数个 \% 是注释起点;奇数个 → \% 转义。\\%(两个 \ + %)正确识别为注释开始。逻辑一致且正确。
  • GBK 安全:%{}\ 均 < 0x40,不会出现在 GBK 双字节的第二字节中。

4. fix-test-tilde.py 重写为状态机

  • 从 .tlg oracle 策略改为与 hook 对称的 group-depth-aware 状态机,补漏了 5 个 xeCJK .lvt 文件 9 处(死代码路径 oracle 看不到)。
  • 不再修改 .tlg,改由 maintainer 跑 l3build save 重 baseline——这是正确做法,避免误改用户故意使用 ~ 的场景(如 Section~2)。
  • _replace_tildes_in_match 提升为模块级函数,CMD_PATTERN.subn 返回替换计数,消除 nonlocal 闭包。

5. verb01.luatex.tlg baseline 修正

  • verb =~42.94794ptverb = 42.94794pt,以 CI 实际输出为 ground truth,正确。之前 PR 描述中的"xeCJK widthVerb 注入"猜测已在 commit message 中自我修正。

6. llmdoc 文档更新

  • 补充了四引擎 fmtutil-user --byfmt rebuild 说明,实用且避免了"只 rebuild 部分 engine"的常见坑。

🟢 小问题 (2)

  • 文件: scripts/fix-test-tilde.py
  • 观察: state 使用 b"off" / b"on" bytes 类型,虽功能正确但不如字符串直观(state 本身不参与 bytes 处理)。纯风格偏好,不影响正确性。

  • 文件: scripts/fix-test-tilde.py
  • 观察: find_lvts()"build/" in str(p) 的过滤方式在路径包含 rebuild/ 等目录时也会匹配。实际仓库中不存在这种路径,风险极低,仅记录。

审查截止: 7b55cc7b17932e6c965d83780c47acf95aa456ab

根因: agentic-pr-review bot 用 `gh pr comment` (issue comment API) 发
review, 不走 formal review API. 之前 hook 只查 .reviews[], 永远看不到
bot 的增量评审 — exit 75 从来没触发过, 一直 exit 0, 误导上层 agent
"review activity pending" 永远是 false.

修法: 新增 new_bot_comment_after_push, 查 .comments[] 里作者是
github-actions 且 createdAt > 头 commit 时间的. 报告 + exit 75 逻辑
同步把这条算进去.

副作用: race condition 仍存在 — bot 发评论可能比 CI 报 SUCCESS 慢一两
秒, hook 跑完时 bot 评论还没出. 但 99% 情况赶得上, 比之前 0% 强很多.

顺手处理 PR #897 最新一轮 review 的两个小建议:
- fix-test-tilde.py: state 用 b"off"/b"on" bytes 不直观, 改为 str
  (state 不参与 bytes 数据流, 没必要)
- fix-test-tilde.py: find_lvts 的 "build/" in str(p) 子串过滤会
  误匹配 rebuild/ 等路径, 改为 "build" in p.parts (路径分量精确匹配)

实测自测脚本 + 全仓库 scanner 0 命中, fix 重跑 Nothing to fix.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 7b55cc7b..e369c539

增量改动涉及 2 个文件,均为合理改进,无阻塞问题。

审查要点

1. check-pr-ci.sh — 新增 bot issue comment 检测

  • 根因正确:agentic-pr-review bot 用 gh pr comment(issue comment API)发评论,不走 formal review API(.reviews[]),旧逻辑永远看不到 bot 的增量审核。
  • jq 查询逻辑正确:按 .author.login == "github-actions" + createdAt > $t 过滤,排除人类日常评论噪声。
  • 输出增加了 URL 字段(\(.url)),便于调试定位。
  • || true 确保 API 调用失败时不影响脚本主流程。
  • 状态报告区分 "formal review" 和 "bot comment" 两类输出,语义清晰。
  • exit 75 逻辑正确地把 bot comment 也纳入了 pending review activity 判定。

2. fix-test-tilde.py — 两处小修正

  • stateb"off"/b"on" 改为 str:state 不参与 bytes 数据流,纯控制流变量用 str 更直观,改动正确。
  • "build/" in str(p) 改为 "build" in p.parts:用路径分量精确匹配,避免 rebuild/prebuild/ 等子串误命中。Path.parts 返回元组,in 检查按分量匹配,语义正确。

🟢 小问题 (1)

  • 文件: .githooks/check-pr-ci.sh
  • 观察: bot comment 检测硬编码了 github-actions 作为 bot login。如果未来使用其他 bot(如 Dependabot 或自定义 GitHub App)发 review comment,需要在此处扩展过滤条件。当前场景下仅有 github-actions 发 agentic-pr-review,不影响功能,仅作记录。

审查截止: e369c539836562f9cf7d79264f629420147349cd

PR #897 review 提到: 之前 bot comment 检测硬编码 author.login ==
\"github-actions\", 未来用 Dependabot 或自定义 GitHub App 会漏检.

改用 REST API 的 user.type == \"Bot\" 过滤. GraphQL 的 gh pr view
--json comments 不暴露 type 字段, 必须走 REST.

顺手把 gh repo view 拿 owner+name 那段提前到 1b 之前, 同一份数据
1b (bot comment) 和 2 (unresolved threads) 复用, 一次 API 调用.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 e369c539..cecbb890

增量 1 个 commit,改动干净合理,无阻塞问题。

审查要点

check-pr-ci.sh — bot 检测改用 user.type

  • author.login == "github-actions" 硬编码改为 REST API user.type == "Bot" 过滤,正确响应了上一轮 review 的建议。兼容 Dependabot、自定义 GitHub App 及未来其他 bot。
  • gh pr view --json comments(GraphQL,不暴露 user.type)切到 gh api repos/.../issues/.../comments(REST),是获取 user.type 的正确方式。
  • repo_owner_name 提取提前到 1b 之前,1b(bot comment 检测)和 section 2(unresolved threads)共用同一份数据,减少一次 API 调用。
  • jq 字段名从 GraphQL 风格(createdAt / author.login / .url)正确映射到 REST 风格(created_at / user.login / .html_url)。
  • [ -n "$repo_owner" ] 前置条件确保 gh repo view 失败时不会向 REST API 发送空路径请求。

审查截止: cecbb8903825cb1556b068c3c9f1941abb1a8b34


🤖 Generated with Claude Code

@Liam0205 Liam0205 merged commit ae7e88e into master Jun 26, 2026
5 checks passed
Liam0205 added a commit that referenced this pull request Jun 26, 2026
PR #897 上 CI 揭示我本地跑 l3build save 时把 heading-name01.tlg 改成
"Section 2" (无 ~), 但 CI 上 LaTeX 内核输出仍是 "Section~2" (有 ~).
这是我本地 fmtutil-user 同步后内核状态与 CI 不一致带来的, 不是 ctex
本身改动. 之前误判为 "LaTeX kernel 自身漂移, 接受", 应改成 "本地环境
飘了, 不该 commit". 回到 origin/master baseline.

verb01.lvt / verbatim01.lvt 的 .tlg 改动保留 (那些是 .lvt 真的改了
带出来的合理 baseline 更新).
Liam0205 added a commit that referenced this pull request Jun 26, 2026
agentic-pr-review 指出 declare -A 是 bash 4+ 特性, macOS 自带 bash 3.2
不支持. 改用 mktemp 临时目录做 per-source-file 缓存, 跨任意 bash 版本.

顺手修 fix-test-tilde.py docstring 里残留的旧路径 (tmp/ → scripts/).

第三条 nit (brace depth 没排除 % 注释行) 实际影响低, 留作 caveat 不动.
Liam0205 added a commit that referenced this pull request Jun 26, 2026
PR #897 CI 揭示我本地 save 出来的 verb01.luatex.tlg 含 'verb =~42.94794pt'
(=`后多了 ~), 而 CI 实际输出 'verb = 42.94794pt' (干净空格). 这是
我本地 fmtutil-user 同步后的环境与 CI 的细微差异, 不是真实行为.

之前 PR 描述里"\widthVerb 展开时 xeCJK 注入 ~"的猜测是错的, 实际 CI
跑出来就没有这个 ~. 用 CI 输出 (干净空格) 作 ground truth.
Liam0205 added a commit that referenced this pull request Jun 26, 2026
之前文档只示例 fmtutil-user --byfmt xelatex, 但 ctex 默认跨 4 个
engine 测试 (pdftex / xelatex / lualatex / uplatex), 漏掉任一 engine
fmt rebuild 都会让该 engine 的全部测试 fail 同一种
'expl3.sty Mismatched LaTeX support files' 错误.

补充: pdftex / xelatex / lualatex / uplatex 四条都要跑.
PR #897 上本地 ctex check 49 个 uptex 测试全 fail, 实测原因即此.
Liam0205 added a commit that referenced this pull request Jun 26, 2026
agentic-pr-review 在多轮增量中提出 1 重要 + 4 小问题, 这个 commit
全部处理:

1. [重要] hook awk 正则 [^}]* 与 fix script CMD_PATTERN [^{}]* 不对称
   - hook 改 [^{}]*, 与 Python 端一致
   - 嵌套 \TYPE{\foo{bar~baz}} 这种两边都漏 (false negative 安全方向)
   - 实测仓库 0 处嵌套, 历史存量未受影响

2. [小] cache_key() 路径冲突
   - tr '/' '_' 会让 a/b_c.lvt 与 a_b/c.lvt 撞 key
   - 改用 sha1sum / shasum / od 三段 fallback, 跨 Linux+macOS

3. [小] brace depth 不排除 % 注释行
   - 行内 % \ExplSyntaxOff 或 % } 会让状态机错算
   - awk + Python 端都加 _strip_tex_comment 剥注释 (\% 转义保留)
   - 状态切换 + depth 计数都基于 stripped

4. [小] 状态机行级粒度的简化假设
   - awk + Python docstring 都加注: 同行 ExplSyntaxOff + \TEST{...}
     state 用切换**之后**的 (因为先切 state 再处理本行命中)
   - 实际 .lvt 不会这么写, 记录为未来 caveat

5. [小] python _sub 闭包在每次循环重新定义
   - 提取为模块级 _replace_tildes_in_match()
   - 用 CMD_PATTERN.subn 返回替换次数, 不再 nonlocal changes
Liam0205 added a commit that referenced this pull request Jun 26, 2026
PR #897 review 第 7 条 review 指出: 之前的 _strip_tex_comment 只看
% 前一个字符是否为 \\, 这判定不严谨. \\\\% 实际是 "字面反斜杠 + 注释
开始", 前面两个 \\ 互为转义对, % 本身不被转义, 应当作注释起点 — 但
我们脚本会误判为转义不剥.

修法 (awk + Python 都改): 数 % 前面**连续**的反斜杠个数, 奇数才是
\\% 转义.

测试 cases:
  %         普通注释        ⟹ 剥
  \\%       \\% 转义         ⟹ 不剥
  \\\\%     字面 \\ + 注释    ⟹ 剥 (前面是修复点)
  \\\\\\%   字面 \\ + \\% 转义 ⟹ 不剥
  \\\\\\\\% 2 字面 \\ + 注释  ⟹ 剥

awk 端 6/6, Python 端 6/6, 全 case 正确.
Liam0205 added a commit that referenced this pull request Jun 26, 2026
根因: agentic-pr-review bot 用 `gh pr comment` (issue comment API) 发
review, 不走 formal review API. 之前 hook 只查 .reviews[], 永远看不到
bot 的增量评审 — exit 75 从来没触发过, 一直 exit 0, 误导上层 agent
"review activity pending" 永远是 false.

修法: 新增 new_bot_comment_after_push, 查 .comments[] 里作者是
github-actions 且 createdAt > 头 commit 时间的. 报告 + exit 75 逻辑
同步把这条算进去.

副作用: race condition 仍存在 — bot 发评论可能比 CI 报 SUCCESS 慢一两
秒, hook 跑完时 bot 评论还没出. 但 99% 情况赶得上, 比之前 0% 强很多.

顺手处理 PR #897 最新一轮 review 的两个小建议:
- fix-test-tilde.py: state 用 b"off"/b"on" bytes 不直观, 改为 str
  (state 不参与 bytes 数据流, 没必要)
- fix-test-tilde.py: find_lvts 的 "build/" in str(p) 子串过滤会
  误匹配 rebuild/ 等路径, 改为 "build" in p.parts (路径分量精确匹配)

实测自测脚本 + 全仓库 scanner 0 命中, fix 重跑 Nothing to fix.
@Liam0205 Liam0205 deleted the feat/lint-test-files branch June 26, 2026 14:27
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.

测试文件:只在必要时使用 ~ 插入空格 (2)

1 participant