Problem
From verification of #85 (Devil's Advocate §1.1, BLOCKING-ish refutation of Requirements Reviewer):
Issue body explicitly names 「假設 H₀ 為...」 as the academic-paper inline-math anchor pattern. Probe-verified post-fix MathSubSuperScript.visibleText returns "H0" (ASCII H + ASCII 0), but user-typed anchor is "H₀" (Unicode U+2080 SUBSCRIPT ZERO). "H₀".contains("H0") is false.
— Source: team:devils-advocate + team:requirements
The post-#85 fix in v0.21.5 covers the t (<m:r><m:t>t</m:t></m:r>) case — primary repro from issue body. But the secondary inline-math identifier pattern with subscripts (H₀, X̄, α₁) emits the base + subscript text as plain ASCII. Anchors typed using the natural Unicode glyph still 0-match.
Verified by executable probe against e53fa00:
[PROBE A] H₀ visibleText = 'H0' (expected 'H0', user anchor 'H₀' won't match)
Type
enhancement (or bug — depends on whether you count "headline use case still broken" as bug)
Proposed Solution
Two paths:
Path A — Unicode subscript/superscript translation in MathSubSuperScript.visibleText
In Sources/OOXMLSwift/Models/MathComponent.swift, modify MathSubSuperScript.visibleText:
public extension MathSubSuperScript {
var visibleText: String {
let baseText = base.visibleText
let subText = (sub?.visibleText).map { translateToUnicodeSubscript($0) } ?? ""
let supText = (sup?.visibleText).map { translateToUnicodeSuperscript($0) } ?? ""
return baseText + subText + supText
}
}
private func translateToUnicodeSubscript(_ s: String) -> String {
// U+2080..U+2089 for digits 0-9; common subscript letters U+2090..U+209C.
// Fall back to original char when no Unicode subscript exists.
return String(s.map { c in
switch c {
case "0": return "₀"; case "1": return "₁"; case "2": return "₂";
case "3": return "₃"; case "4": return "₄"; case "5": return "₅";
case "6": return "₆"; case "7": return "₇"; case "8": return "₈";
case "9": return "₉"
case "+": return "₊"; case "-": return "₋"; case "=": return "₌";
case "a": return "ₐ"; case "e": return "ₑ"; case "i": return "ᵢ";
// ... extend as needed
default: return c
}
})
}
// translateToUnicodeSuperscript similar with U+2070..U+2079 + variants
Pros: anchors-as-typed work. Cons: lossy (subscript j has no canonical Unicode form — ⱼ is Latin); breaks anchors typed as "H0" ASCII (now mismatched against "H₀" Unicode).
Path B — Bidirectional matching at anchor lookup (recommended)
Don't change visibleText; change the anchor matcher. In Body.findBodyChildContainingText (or wherever anchor matching lives), normalize BOTH the haystack and the needle to a canonical form (e.g., strip Unicode subscripts/superscripts to ASCII): "H₀ + H₁".normalized == "H0 + H1". Caller can search for either form.
Pros: opt-in via a new AnchorLookupOptions field (e.g., caseInsensitiveAndScriptInsensitive); doesn't break existing callers; lossless on the AST side. Cons: requires the public API change in #86 (anchor lookup public surface).
Path C — Doc-only caveat
Document the limitation in the flattenedDisplayText docstring + #85 closing comment. Punt the fix to v0.22+ when MCP-layer string-matching gets a richer model.
Impact
LOW-MEDIUM. Depends on path. Path A is 30-40 LOC; Path B requires #86 first; Path C is documentation only.
Probably should pair with #86 (public anchor lookup API) so the matcher gets unified options for case + script + Unicode normalization in one redesign.
Related
Current Status
Key Decisions
- Path B implemented: bidirectional lookup-time math-script normalization, exact matching remains default.
- OOXMLSwift adds
AnchorLookupOptions(mathScriptInsensitive:) and keeps MathSubSuperScript.visibleText / flattenedDisplayText() unchanged.
- che-word-mcp exposes
match_options.math_script_insensitive on scoped insertion text anchors.
Scope Changes
- Implementation split into two PRs because che-word-mcp depends on the new OOXMLSwift public API.
- che-word-mcp#115 is draft while it points at the OOXMLSwift feature branch; after OOXMLSwift release, switch back to a version dependency.
Blocking
- OOXMLSwift PR/release blocks che-word-mcp merge readiness.
Related Commits
Current Status
Phase: diagnosed
Last updated: 2026-05-25 by /idd-diagnose (cross-repo chain context)
Problem
The post-#85 fix in v0.21.5 covers the
t(<m:r><m:t>t</m:t></m:r>) case — primary repro from issue body. But the secondary inline-math identifier pattern with subscripts (H₀,X̄,α₁) emits the base + subscript text as plain ASCII. Anchors typed using the natural Unicode glyph still 0-match.Verified by executable probe against
e53fa00:Type
enhancement (or bug — depends on whether you count "headline use case still broken" as bug)
Proposed Solution
Two paths:
Path A — Unicode subscript/superscript translation in
MathSubSuperScript.visibleTextIn
Sources/OOXMLSwift/Models/MathComponent.swift, modifyMathSubSuperScript.visibleText:Pros: anchors-as-typed work. Cons: lossy (subscript
jhas no canonical Unicode form — ⱼ is Latin); breaks anchors typed as"H0"ASCII (now mismatched against"H₀"Unicode).Path B — Bidirectional matching at anchor lookup (recommended)
Don't change
visibleText; change the anchor matcher. InBody.findBodyChildContainingText(or wherever anchor matching lives), normalize BOTH the haystack and the needle to a canonical form (e.g., strip Unicode subscripts/superscripts to ASCII):"H₀ + H₁".normalized == "H0 + H1". Caller can search for either form.Pros: opt-in via a new
AnchorLookupOptionsfield (e.g.,caseInsensitiveAndScriptInsensitive); doesn't break existing callers; lossless on the AST side. Cons: requires the public API change in #86 (anchor lookup public surface).Path C — Doc-only caveat
Document the limitation in the
flattenedDisplayTextdocstring + #85 closing comment. Punt the fix to v0.22+ when MCP-layer string-matching gets a richer model.Impact
LOW-MEDIUM. Depends on path. Path A is 30-40 LOC; Path B requires #86 first; Path C is documentation only.
Probably should pair with #86 (public anchor lookup API) so the matcher gets unified options for case + script + Unicode normalization in one redesign.
Related
findBodyChildContainingTextAPI (logical home for Path B)e53fa00+f1f7a41) — coverstcase but notH₀Current Status
Key Decisions
AnchorLookupOptions(mathScriptInsensitive:)and keepsMathSubSuperScript.visibleText/flattenedDisplayText()unchanged.match_options.math_script_insensitiveon scoped insertion text anchors.Scope Changes
Blocking
Related Commits
Current Status
Phase: diagnosed
Last updated: 2026-05-25 by /idd-diagnose (cross-repo chain context)