docs: changelog 2026-07-11 splitTreeAfter 攀升對稱守衛(opaque 中間祖先克隆肢解修復)

新增 changelog 條目+md-twin 同步。下游 .selection-bar-message 病例:垂懸字段末實義字
落於 opaque `<button>`(巢於可切 `<span>`),splitTreeAfter 攀升克隆「文本節點→top」祖先
鏈時中間 opaque button 從未複查即被克隆肢解。修復:切分前先驗整條祖先鏈皆 canSplit(下潛
+攀升雙向守衛),任一層 opaque/pill/isolate/帶 id 即退化整綁。changelog HTML 經
build.sh 重建。
This commit is contained in:
2026-07-11 13:35:46 +08:00
parent 738c917a5e
commit cda0903089
2 changed files with 37 additions and 1 deletions
+19 -1
View File
@@ -1,4 +1,4 @@
<!-- built with theme=dracula, mode=report, at=2026-07-10T17:48:40Z -->
<!-- built with theme=dracula, mode=report, at=2026-07-11T05:34:59Z -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
@@ -2545,6 +2545,15 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
<!-- BODY -->
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
<h2>splitTreeAfter 攀升對稱守衛(opaque 中間祖先不再被克隆肢解) <span class="badge accent">2026-07-11</span></h2>
<p>下游 <code>.selection-bar-message</code> 病例:垂懸字(orphan)之段末實義字落於 opaque <code>&lt;button&gt;</code>(巢於可切 <code>&lt;span&gt;</code>)時,<code>splitElementAt</code> 僅驗頂層 span 可切;<code>splitTreeAfter</code> 逐層克隆「文本節點→top」祖先鏈時,中間 opaque button 從未複查即被克隆肢解(複製出 <code>splitoff</code> button、破壞互動元素)。<code>firstText</code><code>lastText</code> 守下潛(遇非可切回 <code>null</code>),攀升卻無對稱守衛——此即真正病根,下游 per-instance 迴避僅治標。</p>
<h3>修復</h3>
<ul>
<li><strong>攀升守衛</strong><code>splitTreeAfter</code> 切分前先驗「<code>textNode.parentNode</code>…top(含 top)」整條祖先鏈皆 <code>canSplit</code>,任一層 opaquepillisolate/帶 id 即放棄、回退整綁(退化但安全,與下潛守衛對稱)。純新增守衛、無新克隆邏輯;jinze 之 <code>isolateBoundaryToken</code> 因下潛已守,行為不變。附帶亦杜絕中間祖先帶 <code>id</code> 之克隆重複 id。</li>
<li><strong>不變量</strong><code>canSplit</code> 須對「切點 textNode 至 top 之<strong>整條</strong>祖先鏈」成立、非僅 top——下潛(<code>firstText</code><code>lastText</code>)+攀升(<code>splitTreeAfter</code> 守衛)雙向守。</li>
<li>單元測試 127→129 全綠(+2):末實義字在 opaque button(巢於可切 span)→ 綁整 span、無 <code>splitoff</code>、button 完整;退化路徑 render→revert 還原一致。亲驗——暫禁守衛則該測試於「無 splitoff」斷言失敗(證明非空通過、病根屬實)。</li>
</ul>
<h2>逐元素標點樣式覆寫(data-jz-stylestylePolicy <span class="badge accent">2026-07-11</span></h2>
<p>下游需求:非段落/特定範圍元素用半角式(banjiao),段落用全角/開明。原標點樣式僅由 <code>lang</code> 決定(<code>lang.policy</code><code>lang.style</code>),無逐元素覆寫。新增與 <code>data-jz-level</code><code>level.text</code> 同構之逐元素樣式機制(<code>banjiao</code> 渲染早已就緒、此為純路由,無新壓縮邏輯/CSS)。單元測試 121→127 全綠。</p>
<h3>設置格式</h3>
@@ -2770,6 +2779,15 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
<script type="text/markdown" id="md-source">
# 變更紀錄(Changelog)— 聚珍(Juzhen)/cjk-autospace
## splitTreeAfter 攀升對稱守衛(opaque 中間祖先不再被克隆肢解)(2026-07-11)
下游 .selection-bar-message 病例:垂懸字段末實義字落於 opaque `<button>`(巢於可切 `<span>`)時,splitElementAt 僅驗頂層 spansplitTreeAfter 逐層克隆「文本節點→top」祖先鏈時中間 opaque button 從未複查即被克隆肢解(複製出 splitoff button)。firstTextlastText 守下潛,攀升卻無對稱守衛——真正病根,下游 per-instance 迴避僅治標。
### 修復
- 攀升守衛:splitTreeAfter 切分前先驗「textNode.parentNode…top(含 top)」整條祖先鏈皆 canSplit,任一層 opaquepillisolate/帶 id 即放棄、回退整綁(與下潛守衛對稱)。純新增守衛、無新克隆邏輯;jinze 之 isolateBoundaryToken 因下潛已守、行為不變。附帶杜絕中間祖先帶 id 之克隆重複 id。
- 不變量:canSplit 須對「切點 textNode 至 top 之整條祖先鏈」成立、非僅 top——下潛(firstTextlastText)+攀升(splitTreeAfter 守衛)雙向守。
- 測試 127→129 全綠(+2):末實義字在 opaque button(巢於可切 span)→ 綁整 span、無 splitoff、button 完整;退化路徑 render→revert 還原一致。亲驗暫禁守衛則該測試於「無 splitoff」斷言失敗。
## 逐元素標點樣式覆寫(data-jz-stylestylePolicy)(2026-07-11
下游需求:非段落/特定範圍元素用半角式,段落用全角/開明。原樣式僅由 lang 決定,新增逐元素覆寫(與 data-jz-level 同構、純路由,banjiao 渲染早已就緒)。測試 121→127 全綠。
+18
View File
@@ -25,6 +25,15 @@
<!-- BODY -->
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
<h2>splitTreeAfter 攀升對稱守衛(opaque 中間祖先不再被克隆肢解) <span class="badge accent">2026-07-11</span></h2>
<p>下游 <code>.selection-bar-message</code> 病例:垂懸字(orphan)之段末實義字落於 opaque <code>&lt;button&gt;</code>(巢於可切 <code>&lt;span&gt;</code>)時,<code>splitElementAt</code> 僅驗頂層 span 可切;<code>splitTreeAfter</code> 逐層克隆「文本節點→top」祖先鏈時,中間 opaque button 從未複查即被克隆肢解(複製出 <code>splitoff</code> button、破壞互動元素)。<code>firstText</code><code>lastText</code> 守下潛(遇非可切回 <code>null</code>),攀升卻無對稱守衛——此即真正病根,下游 per-instance 迴避僅治標。</p>
<h3>修復</h3>
<ul>
<li><strong>攀升守衛</strong><code>splitTreeAfter</code> 切分前先驗「<code>textNode.parentNode</code>…top(含 top)」整條祖先鏈皆 <code>canSplit</code>,任一層 opaquepillisolate/帶 id 即放棄、回退整綁(退化但安全,與下潛守衛對稱)。純新增守衛、無新克隆邏輯;jinze 之 <code>isolateBoundaryToken</code> 因下潛已守,行為不變。附帶亦杜絕中間祖先帶 <code>id</code> 之克隆重複 id。</li>
<li><strong>不變量</strong><code>canSplit</code> 須對「切點 textNode 至 top 之<strong>整條</strong>祖先鏈」成立、非僅 top——下潛(<code>firstText</code><code>lastText</code>)+攀升(<code>splitTreeAfter</code> 守衛)雙向守。</li>
<li>單元測試 127→129 全綠(+2):末實義字在 opaque button(巢於可切 span)→ 綁整 span、無 <code>splitoff</code>、button 完整;退化路徑 render→revert 還原一致。亲驗——暫禁守衛則該測試於「無 splitoff」斷言失敗(證明非空通過、病根屬實)。</li>
</ul>
<h2>逐元素標點樣式覆寫(data-jz-stylestylePolicy <span class="badge accent">2026-07-11</span></h2>
<p>下游需求:非段落/特定範圍元素用半角式(banjiao),段落用全角/開明。原標點樣式僅由 <code>lang</code> 決定(<code>lang.policy</code><code>lang.style</code>),無逐元素覆寫。新增與 <code>data-jz-level</code><code>level.text</code> 同構之逐元素樣式機制(<code>banjiao</code> 渲染早已就緒、此為純路由,無新壓縮邏輯/CSS)。單元測試 121→127 全綠。</p>
<h3>設置格式</h3>
@@ -234,6 +243,15 @@
<script type="text/markdown" id="md-source">
# 變更紀錄Changelog 聚珍Juzhencjk-autospace
## splitTreeAfter 攀升對稱守衛opaque 中間祖先不再被克隆肢解2026-07-11
下游 .selection-bar-message 病例垂懸字段末實義字落於 opaque `<button>`巢於可切 `<span>`splitElementAt 僅驗頂層 spansplitTreeAfter 逐層克隆文本節點top祖先鏈時中間 opaque button 從未複查即被克隆肢解複製出 splitoff buttonfirstTextlastText 守下潛攀升卻無對稱守衛真正病根下游 per-instance 迴避僅治標
### 修復
- 攀升守衛splitTreeAfter 切分前先驗textNode.parentNodetop top整條祖先鏈皆 canSplit任一層 opaquepillisolate id 即放棄回退整綁與下潛守衛對稱純新增守衛無新克隆邏輯jinze isolateBoundaryToken 因下潛已守行為不變附帶杜絕中間祖先帶 id 之克隆重複 id
- 不變量canSplit 須對切點 textNode top 之整條祖先鏈成立非僅 top下潛firstTextlastText攀升splitTreeAfter 守衛雙向守
- 測試 127129 全綠2末實義字在 opaque button巢於可切 span 綁整 span splitoffbutton 完整退化路徑 renderrevert 還原一致亲驗暫禁守衛則該測試於 splitoff斷言失敗
## 逐元素標點樣式覆寫data-jz-stylestylePolicy2026-07-11
下游需求非段落特定範圍元素用半角式段落用全角開明原樣式僅由 lang 決定新增逐元素覆寫 data-jz-level 同構純路由banjiao 渲染早已就緒測試 121127 全綠