Skip to content

v0.1.19

Choose a tag to compare

@github-actions github-actions released this 31 Jul 05:39
· 17 commits to main since this release
ae67a18

一次三层递进的修复,同一条因果链:起点是「HTML 注释里的链接被当成真链接」;修它引入的过滤
规则第二次错在相反方向(把真断链抹白,写门禁静默放行坏库);第三次收窄让原始 \r 第一次真的
流进扫描器,掀出四个确定性写点共有的「CRLF 静默转 LF」。三条按发生时序排,后两条里的「上一条」
即指前一条。无新功能、无接口变更;全量 1186 passed / 1 skipped。

修复

  • HTML 注释里的链接不再被当成真链接(check / lint / graph / health / reindex --prune / remove
    —— 各链接扫描器此前对 <!-- … --> 一视同仁,于是注释里的示例链接被当作生效引用。影响面比表象大,
    按严重度排:① reindex --prune 把 init 模板 index.md 里那四行 <!-- ingest 自动追加:- [<名称>](entities/<Name>.md) — <一句话> --> 整行删掉——被删的正是告诉 Agent 追加格式的说明;
    check 对注释掉的 [[X]]wikilink.broken退出码 3,即"随手注掉一段草稿"会让写门禁判库坏了;
    graph 造幽灵边/幽灵断链,lint.broken_linkhealmissing_entity 因派生自该图跟着误报;
    ④ 全新 guanlan init 的库跑 health 立刻报 4 条 index_dangling(首次体检就是四条假告警);
    remove 会连带删掉注释里提到该源的行。修法是 pages.strip_html_comments 一处归口,扫描前抹掉
    闭合注释,四个扫描点(check 断链、graph 建边、index_md_linksreindex._scan_lines)共用。
    两条刻意的设计:保留行数(注释替换为等量换行,按 str.splitlines 口径数行,reindex --prune
    原行与判定行逐位对齐来剪枝,行数一变就错删);未闭合的 <!-- 原样保留、不吃到文末(那会把后文
    真断链静默吞掉,把漏报伪装成通过——门禁宁可多报不可少报)。回归网 14 例,其中 11 例对旧行为变异验证
    失败;另加一条 test_freshly_initialized_kb_is_health_clean 直接走 run_init 真模板体检——
    这个 bug 一直没被测试看见,正是因为 test_reindex 的 index 夹具用的是不带链接
    <!-- ingest 自动追加 -->,与出厂模板漂移了。经 Codex 评审三项收敛:① 注释抹成等长空白
    而非删空——删空会把两侧字符粘起来,凭空造出原文没有的链接([名]<!--注-->(entities/X.md) 粘成
    合法 markdown 链接、[[Mis<!--注-->sing]] 粘成可能真解析到某页的 [[Missing]]);② 行分隔符
    逐字保留而非统一补 \n(裸 \r 文本里补出的 \n 会与前一个 \r 合成一个 CRLF 边界、行数少一,
    于是 _prune_dangling 的 strict zip 抛——这条是纵深防御,两条调用路径今天喂进来的文本已被
    read_text 通用换行归一过);③ Web 渲染同步对齐——render.py 此前仍会把注释里的 [[X]] 渲成
    可点链接,与 check/graph 打架;改为落在注释区间内即拒绝成链。Web 与扫描器的失活方式刻意不同
    扫描器抹成空白,Web 保留注释原文按转义文本显示(决策P4-4 关了原始 HTML 透传),视图不该让页面上
    凭空少一段字。
  • 注释剥离收窄为「只认独占整行的注释」,堵掉它自己引入的门禁漏报(xhigh 评审 8 条)
    —— 上一条的实现用裸 <!--.*?--> 匹配,非贪婪只保证止于第一个 -->、不保证那个 --> 属于
    同一段注释。后果比原 bug 更重:① 正文或行内 code 里的字面 <!--(讲注释写法时很常见)会与后文
    任意一个 --> 配对,把中间的真链接整段抹白——check 于是对真断链退 0,写门禁静默放行坏库,
    graph/lint/heal 一并失明;② init 模板自带四段注释,任何一个忘闭合的 <!-- 都能找到配对,
    于是 health 误报 index_missing_pagereindex 追加第二条重复登记行、remove 留下悬空项;
    ③ 抹成空格并不能阻止粘连——下游 link_stem/index_md_links 自己会 .strip()[[Foo<!--旧名-->]]
    被重新粘成有效引用 [[Foo]],断链伪装成通过。修法是把匹配收窄为行锚定 + 回火
    ^[ \t]*<!--(?:(?!<!--|-->).)*?-->[ \t]*$):行中间的标记永不构成注释开头,未闭合的 <!--
    也不与后一段注释的 --> 配对。代价是行尾的行内注释不再被识别、其中链接照常参与扫描——宁可
    多报,不可少报
  • reindex --prune 不再铰碎注释块strip_html_comments 既然只认独占整行的注释,行尾开块的
    写法(- [死页](…) 悬空 <!-- + 若干行 + -->)就不被它保护,照删会把块拆开——块内被注掉的
    行「转正」,下一轮 --prune 再把它当真悬空删掉,两步内静默蒸发。新增 _comment_touched_lines
    凡沾注释的行一律不删。它与 strip_html_comments 分工相反、各自保守——严格口径决定「忽略
    什么」(放宽会漏报),宽松口径决定「保护什么不删」(收紧会毁数据),互不继承对方的失效模式。
  • Web 渲染改在解析前抹注释,真正与扫描器同口径:上一条只在行内 [[…]] 处理器设了守卫,既漏
    又散——_CodePathLinkTreeprocessor(反引号页面引用)与 _RawPathTreeprocessor(裸 raw/<slug>.md
    跑在元素树上、拿不到原文偏移,照常成链;而 inline 处理器只看得到当前块,含空行的注释被切成
    普通段落后守卫直接失效(正是「注掉一段多段落草稿」这个动机场景)。改为 _StripCommentsPreprocessor
    在整篇行列表上抹一次,所有下游自然干净。行为变化:注释文本不再显示在 Web 页面上——与
    Obsidian/GitHub 一致;此前能看见只是决策P4-4 关掉原始 HTML 透传后注释被转义成字面文本的副产物。
    被抹的行置空串而非等长空格(4 个以上前导空格在 markdown 里是缩进代码块,会渲染出一个空的
    <pre><code>)。
  • _prune_dangling(text, dangling) 自己切行:原三参签名把「两列逐位等长」外包给三个调用方,
    少传/传错就会让「模板提示行被整行剪掉」静默复发,而 strict=True 挡不住(两列等长、只是判定错)。
    契约收回函数内部,_scan_lines 这个跨模块私有符号一并消失。
  • CRLF 行尾不再被静默改成 LF(reindex / remove / ingest+audit 的指纹 stamp)
    —— 四个确定性写点都是「Path.read_text 读 → 改几行 → rawio.atomic_write_text 写」。读侧走通用
    换行(\r\n、裸 \r 一律归一成 \n),写侧却是逐字节的,一读一写净效果就是把整份文件
    行尾改掉:Windows 用户跑一次 guanlan reindex,index.md 27 行 CRLF 全变 LF,git 里炸出一屏与本次
    操作无关的 diff;remove 摘一个 slug、audit 打一次指纹,同样把整张内容页/摘要页重写一遍。
    reindex._split_lines 里那句探测 CRLF 的代码因此从来没生效过(读侧早已归一,探到的永远是 \n),
    atomic_write_text 的文档还在承诺「自管 EOL 的调用方原样保真」——三处说法互相矛盾。修法是给
    逐字节的写补上逐字节的读:rawio 新增 read_text_verbatimatomic_write_text 的读侧对偶)、
    detect_eol / split_eol_lines(拆行改行再拼回时保原 EOL)、dump_frontmatter(重序列化
    frontmatter 的单一归口,块按原 EOL 出、body 逐字不动),四个写点全部改用。fmrepair 早就为同一个
    理由走 read_bytes + atomic_write_bytes,这次是把那条纪律推广到文本层。三处顺带收敛:
    detect_eol首个出现的行尾而非「含 CRLF 即判 CRLF」——后者会让 LF 文件里混进的一个 CRLF
    把整份重写成 CRLF,修 CRLF 丢失反而制造 LF 丢失;② split_eol_lines 只认 CRLF/CR/LF,不像
    str.splitlines\v/\f/\x1c/\u2028 也切(切开再按统一 EOL 拼回去 = 把行内字符静默换成
    换行,同属"重写时悄悄改用户字节");③ apply_origin 改走 dump_frontmatter,与另两处 frontmatter
    重写共用「绝不裸拼」的同一实现(零行为变更,有用例逐字比对旧字面量)。
  • 注释与链接正则改为行尾中立:上一条让原始 \r 第一次真的流进 strip_html_comments,而它的
    行锚定用的是 ^/$+MULTILINE——只认 \n,CRLF 文本里 -->\n 之间隔着 \r$ 匹配不上,
    整行注释当场不再被识别(前一条 CHANGELOG 里「这条是纵深防御、调用路径喂进来的文本已被 read_text
    归一过」的前提,至此失效)。改用「前后不是非换行符」的环视 (?<![^\r\n])…(?![^\r\n]),对 LF /
    CRLF / 裸 CR 一视同仁(LF 下与旧式逐字等价);WIKILINK_RE / _MD_LINK_RE 一并把 \r\n 同等
    排除,杜绝引用跨行粘连。边界说清、不夸大reindex --prune 今天并不会因此删掉 CRLF 库的注释行
    ——那层由 _comment_touched_lines 的宽松口径兜着(实测:把正则退回只认 \n,端到端用例仍绿)。
    这里修的是严格口径自己不能因行尾失灵——strip_html_commentscheck/graph/index_md_links/
    Web 渲染共用的公共原语,让它对 CRLF 静默换一套含义,是给下一个消费方埋雷。回归网 16 例,逐条对旧
    实现变异验证:读侧、frontmatter 重出、注释正则、链接正则各自的用例都能在退回旧写法时失败;其中
    两条是反方向守卫——CRLF 下真断链仍被扫出、真悬空行仍被剪掉,免得「兼容行尾」写成「CRLF 库一律
    不报不剪」。

Full Changelog: v0.1.18...v0.1.19