{"name":"psychquant-che-word-mcp-plugins-che-word-mcp","owner":{"name":"ClaudePluginHub"},"plugins":[{"name":"psychquant-che-word-mcp-plugins-che-word-mcp","source":{"source":"git-subdir","url":"https://github.com/psychquant/macdoc","path":"plugins/che-word-mcp"},"description":"v3.20.0 cross-document OMath splice MCP tools (#160) — splice_omath_from_source + splice_paragraph_omath_from_source wrap ooxml-swift v0.24.0's spliceOMath API for verbatim copy of <m:oMath> XML blocks between WordDocument paragraphs. Source via source_path (Direct mode read-only) or source_doc_id (Session mode); target requires session-mode doc_id. Position via atStart/atEnd/afterText/beforeText with optional anchor + instance. Carrier preservation (Run.rawXML stays inline, unrecognizedChildren stays direct-child); joint document-order index for omath_index across both source carriers. rpr_mode controls source Run rPr propagation (full default verbatim / omathOnly whitelist / discard empty); namespace_policy controls prefix vs URI handling (lenient default accepts mml: vs m: prefix mismatch with same URI per ECMA-376 / strict throws on any mismatch). Error taxonomy returned as structured strings: sourceHasNoOMath / omathIndexOutOfRange / targetParagraphOutOfRange / anchorNotFound / namespaceMismatch / contextAnchorNotFound (batch only). Unblocks kiki830621/collaboration_guo_analysis Phase 7 inline-math restoration pipeline. Tests: 8 new Issue160SpliceOMathFromSourceTests (Direct/Session source modes, atEnd/afterText positions, error taxonomy, batch mode, rPr discard). Full suite: 297 passing, 0 failures, 9 pre-existing skips. Bumps ooxml-swift dep 0.21.0 → 0.24.0. v3.19.0 caption detection (#136) + estimate_paragraph_for_page structural weights v2 (#142). #136: two-layer detection — Paragraph.style primary + expanded prefix set (English Tab./Fig./Listing + CJK 表3/圖1 no-separator + U+3000 ideographic space) with digit-after-prefix guard rejecting body sentences like \"Table reservations are required...\". 17 new tests. #142: walker upgrade getParagraphs → collectStructuralBlocks enum (.paragraph/.table/.imageOnlyParagraph/.displayEquationParagraph). Per-block weights (12pt thesis calibration): table = tableRows × avgCellChars (200/row fallback), image = +200/drawing, display eq = 120 chars. New structural_breakdown metadata (9 sub-fields). API method bumped char_count_heuristic → char_count_heuristic_v2. ~95× thesis figure-counting accuracy improvement. paragraph_index semantics + getParagraphs() preserved (30+ other callers unaffected). Tests: 6 new + #89 backward-compat passes (text-only fixtures unchanged, just method literal updated). 1 P3 follow-up filed (#159 display-eq fixture limitation). v3.18.1 transitive dependency bump — ooxml-swift v0.21.11 → v0.22.1. Closes PsychQuant/che-word-mcp#155 (parent / mirror) auto-resolves via Package.resolved bump alone (no MCP source change). Pre-fix: che-word-mcp__search_text MCP tool's Server.swift:10310 calls para.getText(), which was a legacy 7-line implementation that only joined runs.map { $0.text } + hyperlink.text — missed every walker enhancement landed in flattenedDisplayText() over the #85 / #92 / #99 / #100 / #101 / #102 / #103 cluster. Callers couldn't grep for inline math symbols (α / β / γ / θ / λ / t) — silent zero gaps in match positions. Post-fix: ooxml-swift#43 collapsed Paragraph.getText() to single-line return flattenedDisplayText(); two text-extraction paths now return identical strings. Server.swift:10310 still calls para.getText() but that method now traverses inline OMML correctly. Position arithmetic now matches before_text / after_text anchor-matching paths. Downstream impact: kiki830621/collaboration_guo_analysis#6 (thesis 30 inline math symbols) unblocked from automation — re-running search_text 進行t檢定 against 碩士論文.docx para 324 now returns a match. Released via /idd-all #43 --cwd cross-repo IDD orchestration (issue-driven-dev v2.40.0). Closing summary at https://github.com/PsychQuant/ooxml-swift/issues/43#issuecomment-4365430488. v3.18.0 insert_equation argument-contract hardening (closes #105 #106 #107). v3.17.8 closes PsychQuant/che-word-mcp#98 — insert_equation MCP handler refactored across 3 commits (91506f8/357cbe7/339ab77). Pre-fix three structural defects: (1) silent-clamp on out-of-range paragraph_index via non-throwing insertParagraph(_:at: Int) — tool reported success but inserted at wrong location; (2) lib's #84/#91 InsertLocation overload + InsertLocationError.inlineModeRequiresParagraphIndex / .invalidParagraphIndex(Int) structured errors never reached MCP callers (handler self-built OMML and bypassed lib); (3) inline mode (display_mode=false) always created a NEW Paragraph(runs: [eqRun]) instead of appending OMML run to the EXISTING paragraph at paragraph_index. v1 (91506f8) tried delegating to lib but Codex 6-AI verify caught P1 regression: lib's Document.insertEquation overload internally uses @available(*, deprecated, ...) MathEquation flat output (Field.swift:301) — emits truncated '(a)/(b' AND nested <w:p><w:p> invalid OOXML. v2 (357cbe7) unified handler — both latex AND components paths use MathComponent.toOMML() for structurally correct OMML; display mode routes through lib's throwing insertParagraph(_:at: InsertLocation); inline mode handler-side appends OMML run to existing paragraph. v3 (339ab77) restored eqPara.properties.alignment = .center for display mode. BREAKING for inline mode callers: pre-fix inserted NEW paragraph; post-fix appends OMML run to EXISTING paragraph at paragraph_index. Migration: use display_mode=true for 'new paragraph with equation' or call insert_paragraph + insert_equation separately. 7 NEW Issue98InsertEquationLibBypassTests pin contracts (5 RED→GREEN scenarios + 2 quality regression tests including unzip -p document.xml verifying <m:f> structure + centering survives + deprecated '(a)/(b)' pattern absent). 6-AI verify ensemble (5 Claude reviewers + Codex gpt-5.5 xhigh) caught both P1 regressions in v1; Codex sanity check on v2 caught the centering P2. 6 P2/P3 follow-up issues filed (#105-#110). Suite: 236 → 243 tests, 0 failures, 9 pre-existing skips. Backward compatible except inline-mode BREAKING (documented). v3.17.7 ooxml-swift dep bump 0.21.10 → 0.21.11 — closes 5-issue cluster PsychQuant/che-word-mcp #99 + #100 + #101 + #102 + #103. Bilateral mirror coverage for direct-child OMML at 4 wrapper positions (<w:p> direct child for Pandoc display math / <w:hyperlink> direct child / <mc:Fallback> direct child / nested wrapper combos), plus 2 NEW library-wide spec capabilities. Pre-fix Paragraph.flattenedDisplayText AND Document.replaceInParagraphSurfaces shared a symmetric blind spot: direct-child <m:oMath>/<m:oMathPara> (not wrapped in <w:r>) was silently dropped → anchor lookups against paragraphs containing display math silently 0-matched. Spectra change flatten-replace-omml-bilateral-coverage. 2 NEW spec capabilities promoted to openspec/specs/: ooxml-paragraph-text-mirror (mirror invariant + ReplaceResult informative refusal contract) + ooxml-library-design-principles (Correctness primacy + Human-like operations as foundational normative invariants for all ooxml-swift mutators). Read side: flattenedDisplayText walks direct-child OMML at all 4 wrapper positions with source-XML position ordering. Write side: NEW public API WordDocument.replaceTextWithBoundaryDetection returns ReplaceResult enum (.replaced(count:) / .refusedDueToOMMLBoundary(occurrences:) / .mixed(replacedCount:, refusedOccurrences:)) with Occurrence(matchSpan:, ommlSpans:) carrying flattened-text coordinates. Mirror invariant — asymmetric by design: reads include OMML visibleText (anchor lookup universe extends to math); writes treat OMML as opaque structural units (refuse cross-OMML mutation rather than silently delete equations). Decision 4 raw passthrough preserved: direct-child OMML stays in Paragraph.unrecognizedChildren / HyperlinkChild.rawXML(_) / AlternateContent.rawXML — no parser change, no writer change, round-trip fidelity unaffected. MCP impact: replace_text and other anchor-lookup tools now find paragraphs containing direct-child OMML at all 4 wrapper positions; existing replace_text MCP tool unchanged (backward-compatible). Tests: 236 passing che-word-mcp / 813→829 ooxml-swift (+16 in Issue99FlattenReplaceOMMLBilateralTests). Backward compatible — strict superset of pre-fix behavior. v3.17.6 ooxml-swift dep bump 0.21.9 → 0.21.10 — closes PsychQuant/che-word-mcp#104. Form-level FieldParser canonical 5-run fldChar fix (orthogonal to v3.17.5's #94 container-level fix). Pre-fix update_all_fields returned silent no-op ('no SEQ fields found') on docs containing valid SEQ paragraphs at body top level when fldChar block was emitted in canonical 5-run form (each <w:fldChar>/<w:instrText>/<w:t> in its own <w:r> sibling — what DocxReader produces post-roundtrip and what native Word always emits). Pre-fix worked only on in-memory wrap_caption_seq output before save. Two ooxml-swift commits land via this dep bump: 537de62 FieldParser two-phase parse (Phase-1 baked form + Phase-2 parseFiveRunSpan state machine probing both Run.rawXML and Run.rawElements per recognizedRunChildren = ['rPr','t','drawing','oMath','oMathPara'] allowlist) + 58fe4f9 P1 sub-fix surfaced by 6-AI verify (Logic + Devil's Advocate runtime test): canonical-branch Run.text rewrite was silently overridden by Run.toXML() rawXML short-circuit; new rewriteCanonicalCachedText helper splices new value into embedded <w:t> while preserving <w:rPr> + xml:space=preserve, AND keeps Run.text in sync. MCP impact: update_all_fields now finds and updates SEQ fields in canonical 5-run form (post-roundtrip / native Word emission); list_captions benefits transitively via shared FieldParser. Verified by 6-AI ensemble (5 Claude reviewers + Codex gpt-5.5 xhigh) — 4 PASS / 2 WARN / 0 BLOCK; production reproducer rescue-swift-v317.docx via DocxReader path confirmed working. 5 P3 follow-ups filed in ooxml-swift (#29 SEQ Table coverage / #30 multi-paragraph counter / #31 multi-SEQ same paragraph / #32 DoS hardening / #33 discriminator invariant). Tests: 236 passing che-word-mcp / 809→813 ooxml-swift (+4 sub-tests in Issue104FieldParserCanonicalFormTests). Backward compatible — strict superset of pre-fix behavior. v3.17.5 ooxml-swift dep bump 0.21.8 → 0.21.9 — triple #87 + #93 + #94 release. #87 (Comment.paragraphIndex flat-counter, observable behavior change): list_comments paragraph_index now consistently 0-indexed against get_paragraphs() flat list; pre-fix off-by-N for any docx with non-paragraph BodyChild siblings before commented paragraph; callers manually compensating with paragraph_index - 1 must remove compensation. #93 (wrap_caption_seq SEQ inherits source position, caption visual fix): pre-fix 「圖 4-1：xxx」 became 「圖 4-：xxx1」 because new SEQ run had position=nil while source-loaded preText/postText had position>0; one-line fix seqRun.position = preRun.position; insert_bookmark=true × source-loaded paragraph still has same gap (filed PsychQuant/ooxml-swift#24, default insert_bookmark=false unaffected). #94 (update_all_fields traverses .table and .contentControl containers): pre-fix body loop only processed top-level .paragraph BodyChild, silently skipped .table and .contentControl(_, children:) — SEQ fields inside table cells/block-level SDTs never updated, returning 'no SEQ fields found' for thesis docs (caption paragraphs commonly live inside the table they describe); same gap #68 closed for findBodyChildContainingText; new walkAndProcessBodyChildForFields recursive walker mirrors #68 pattern; heading-count semantics: only top-level direct .paragraph body children count toward chapter-reset. Known incompleteness (3 follow-ups filed): ooxml-swift#25 header/footer/footnote/endnote SEQ scans still flat .paragraphs view; ooxml-swift#26 FieldParser.parse(paragraph:) misses inline SDT/hyperlink/fieldSimple/alternateContent surfaces; ooxml-swift#27 verify-with-user-fixture for real thesis docx roundtrip; plus ooxml-swift#28 refactor candidate (extract BodyChildVisitor protocol). Verified by 6-AI ensemble (5 Claude reviewers + Codex gpt-5.5 xhigh). Tests: 236 passing che-word-mcp / 805→809 ooxml-swift. Backward compatible except #87 documented behavior change. v3.17.4 paired #91 + #92 release. v3.17.3 bump ooxml-swift dep 0.21.6 → 0.21.7 (pure transitive bump, no MCP source changes; exposes public anchor lookup API). Three WordDocument APIs upgraded private → public (PsychQuant/che-word-mcp#86): findBodyChildContainingText(_:nthInstance:) instance method + bodyChildContainsText(_:needle:) static + tableContainsText(_:needle:) static. External Swift SPM consumers (rescue scripts, dxedit CLI, third-party tooling) can now call canonical anchor-lookup logic directly instead of reimplementing with diverging semantics (some skipped .contentControl recursion, some skipped table cell traversal pre-#68, some used different nthInstance counting rules). Result is exactly what insert_paragraph / insert_image_from_path / insert_caption etc. tools see when resolving after_text / before_text anchors. 10 new public-API surface tests in Issue86PublicAnchorLookupTests pin the canonical behavior across releases. Backward compatible: pure additive private → public visibility change; no API removals, no behavioral changes for existing callers. v3.17.2 bump ooxml-swift dep 0.21.2 → 0.21.6 (pure transitive bump, no MCP source changes; 4 ooxml-swift releases worth of hardening + new APIs land transparently). v0.21.3 (XML hardening, PsychQuant/ooxml-swift#7): DTD reject + 64KB attr-value cap + SAX-based root-element attribute parsing + name whitelist on emit. New XMLHardeningError throws on malicious .docx input. v0.21.4 (roundtrip loud-fail, PsychQuant/ooxml-swift#6): AlternateContent.fallbackRunsModified dirty flag throws RoundtripError.unserializedFallbackEdit on stale fallbackRuns mutation. Run.commentIds @available deprecated; migrate to commentRangeMarkers. v0.21.5 (insertEquation flexibility, PsychQuant/che-word-mcp#84 #85): InsertLocation overload for Document.insertEquation + flattenedDisplayText OMML coverage extends anchor lookup beyond plain runs. v0.21.6 (mutation surface, PsychQuant/ooxml-swift#5): Hyperlink.text setter @available deprecated (lossy); migrate to .runs property. Position field cascade Int = 0 → Int? = nil across 13 typed-child models. xml:space=preserve autosense in Run.toXMLThrowing emit. Plus 3 unreleased docs commits on ooxml-swift main (PsychQuant/ooxml-swift#14 #15 #17 + corrective cd841e7) covering needsPPr emit-gate ↔ Issue4 lock-in test bidirectional reference, parseRun vs parseParagraph walker pattern divergence rationale, foreign-namespace pPr asymmetry documentation. Tests: 236 passing (no regressions). Backward compatible: deprecation warnings only; no API removals. v3.17.1 bump ooxml-swift dep to v0.21.2 — pulls in pPr regression-guard + test infrastructure hardening from upstream (ooxml-swift#4 walker whitelist + #if DEBUG assert / ooxml-swift#13 empty <w:pPr/> self-closing test gap / ooxml-swift#16 countPPrOpenTags regex hardening excluding <w:pPrChange>). No public MCP tool change. v3.17.0 wrap_caption_seq MCP tool (Refs #62): Phase 2 of cross-repo work — exposes ooxml-swift v0.21.0 lib API as MCP tool. Bulk-wraps plain-text caption number portions in SEQ field runs across body paragraphs whose flattened text matches a regex (EXACTLY ONE numeric capture group). Captured digit becomes SEQ field cachedResult so Word's first-open render preserves user-typed numbering before F9. Rescues docs pasted from external sources (LaTeX-converted Word, Google Docs, Pandoc) so insert_table_of_figures / insert_table_of_tables produce populated TOFs. Idempotent: paragraphs already wrapping a SEQ field for sequence_name reported in skipped, never double-wrapped (detection covers both FieldSimple AND rawXML fldChar emissions). Phase 1 ships scope:body only (recurses into table cells + nestedTables + block-level SDT children); scope:all returns Error: scope_not_implemented for now (cross-container path lands in v3.17.x). Bookmark wrap opt-in (insert_bookmark + bookmark_template with literal ${number}) so default 23-caption rescue does NOT pollute list_bookmarks. Returns JSON: {matched_paragraphs, fields_inserted, paragraphs_modified:[idx,...], skipped:[{paragraph_index, reason},...]}. All preconditions checked BEFORE document mutation (regex compile + capture-group count + format/scope enums + bookmark_template invariant + doc_id opened). Tests: 5 new sub-tests in Issue62WrapCaptionSeqTests covering Scenarios 1-5. Suite 231 → 236 (+5, 0 fail / 9 skip). No ooxml-swift dep bump (still v0.21.0 from v3.16.2). v3.16.2 ooxml-swift dep bump 0.20.5 → 0.21.0 (Refs #62 #68): pure dep bump, no MCP source changes. Picks up two ooxml-swift fixes that surface transparently via existing tool dispatch. #68 (ooxml-swift v0.20.6): InsertLocation.findBodyChildContainingText now traverses .table (rows × cells × paragraphs + nestedTables) and .contentControl(_, children:) (recursive). MCP impact — insert_paragraph / insert_image_from_path / insert_equation / insert_caption calls using before_text / after_text now succeed when anchor text lives inside a table cell or block-level SDT (common in thesis docs with figure/table captions inside table cells). Returned position is top-level body.children index of the containing structure. Use into_table_cell for inside-cell inserts. Empty-needle guard: passing before_text:'' / after_text:'' now returns textNotFound instead of silently inserting at index 1. #62 (ooxml-swift v0.21.0): WordDocument.wrapCaptionSequenceFields(...) is now linked into the binary. Not yet exposed as an MCP tool — the wrap_caption_seq MCP wrapper ships in v3.17.0 (Phase 2 of the cross-repo work). Existing MCP tools unaffected. Suite: 231 → 231 (0 fail / 9 skip). v3.16.1 anchorPresence whitelist drift prevention (Refs #80): pure refactor, no runtime behavior change. New static toolAnchorWhitelists dict (single source of truth, keyed by MCP tool name → accepted anchor list) + new detectPresentAnchors(_:tool:) overload. 4 conflict-detection call sites switched from literal anchor arrays to (tool:) lookup. 4 new invariant/parity tests. Suite 227 → 231 (+4). Old (args, anchors:) overload preserved. Out-of-scope follow-ups: schema descriptions + dispatcher if-else chains still hardcode anchor names (pre-existing surfaces, not introduced by this PR). v3.16.0 Bundle B anchor DX consistency (Refs #70 #71 #72): BREAKING (input validation only) — three coordinated MCP-layer changes across the 4 #61-target tools. #71 (behavior) silent priority on conflicting anchors → structured error: insert_paragraph(after_text + index) was previously silent-priority; now returns 'Error: insert_paragraph: received conflicting anchors: after_text + index. Specify exactly one.' New static helper detectPresentAnchors with per-anchor type-aware predicates (null and wrong-type values do NOT count). #72 (validation) explicit text_instance ≤ 0 rejected — 'Error: <tool>: text_instance must be ≥ 1, got <N>.' Omitted text_instance still defaults to 1. #70 (DX) all 32 'return Error:' lines in 4 #61-target tools rewritten as 'Error: <tool>: <body>' for AI-caller error attribution. throw WordError.* paths unchanged. Scope deliberately limited to 4 tools; remaining 41 return Error lines elsewhere deferred to error-prefix-sweep follow-up. SemVer rationale (minor not major): no schema break, no tool removal, restricting previously-undefined behavior. Tests: 201 → 227 (+26 sub-tests, 0 fail / 9 skip). No ooxml-swift dep bump (still v0.20.5). v3.15.3 Bundle A2 polish from v3.15.2 verify R3-R6 follow-ups (Refs #76 #77 #78 #79): #76 (docs) insert_caption description corrected from '三種 anchor' to enumerate all 5 (paragraph_index / after_image_id / after_table_index / after_text / before_text); insert_equation paragraph_index description clarified that the int is body.children-indexed (cross-references PsychQuant/ooxml-swift#10 for the lib-layer convention split). #77 (docs) insert_caption anchor set wording precision in CHANGELOG / manifest / marketplace.json / plugin.json — was 'its own anchor set including after_table_index' (implies disjoint), now 'shares after_image_id / after_text / before_text / paragraph_index, adds after_table_index + position, lacks into_table_cell' (explicit shared/adds/lacks). #78 (test) extends #69 append-index regression pin to bookmarkMarker / rawBlockElement / block-level contentControl body-children — the table case alone wouldn't catch a regression to getParagraphs().count - 1 that breaks for SDT / TOC bookmark / vendor extensions. #79 (test) adds round-trip depth: testInsertParagraphAppendIndexRoundTripsForInsertCalls demonstrates insert-family round-trip works (append + insert(N+1) + verify ordering); testInsertParagraphAppendIndexCannotRoundTripToUpdate pins the cross-family trade-off (update_paragraph(index=N) throws WordError.invalidIndex). Tests: 196 → 201 (+5 sub-tests, 0 fail / 9 skip). No production code change. No ooxml-swift dep bump (still v0.20.5). v3.15.2 closes Bundle A polish from #61 R2 verify (Refs #69 #73 #74 #75): #69 (bug) insert_paragraph append message reports body.children index instead of getParagraphs().count - 1 (mis-reported in docs with tables/SDTs by skipping table children); #74 (bug) insert_image_from_path debug log labels after_image_id correctly (was silently labeled 'index' since v3.15.1); #73 (test) regression pin for equation F5 partial-dict guard (existed since v3.15.1 but was untested); #75 (docs) clarifies '3 insert tools' wording — scope is the 3 #61-target tools (insert_paragraph / insert_equation / insert_image_from_path); insert_caption is a 4th insert tool with a partially-overlapping anchor set (shares after_image_id / after_text / before_text / paragraph_index, adds after_table_index + position, lacks into_table_cell), intentionally outside this unification scope. Tests: 194 → 196 (0 fail / 9 skip). No behavior change in normal call paths. No ooxml-swift dep bump (still v0.20.5). Word MCP Server - Swift 原生 OOXML 操作，233 個工具。v3.15.1 closes verify findings F1+F2+F3+F5 from v3.15.0 6-AI ensemble (5 Claude reviewers + Codex gpt-5.5 xhigh)：F1 (P1) `after_image_id` anchor 加到 insert_paragraph + insert_equation (display only) + insert_image_from_path — lib InsertLocation.afterImageId 從 #44 起就 ready 但只有 insert_caption 暴露 MCP-layer；v3.15.0 inherited 這個 gap，本 release 補齊。F2 (P1) `into_table_cell` 加到 insert_equation (display only) — display equation 是新建 paragraph，cell 放置 well-defined；inline mode 拒絕。F3 (P2) equation 成功訊息加 anchor info（'Inserted equation (display mode: true, after text X (instance N))' 等）— 關閉同 v3.14.4 LOOKUP 的 over-claim 模式（caller 之前無法區分 anchor 命中 vs append fallthrough）。F5 (P2) malformed `into_table_cell` partial dict（傳 `{table_index: 0}` 缺 row + col）silent fallthrough → 走 next anchor / append → 結果在錯位置且 caller 不知。改回 structured 'Error: into_table_cell requires all three fields'，3 #61-target tools 同步修（cross-cutting consistency）。Anchor priority unified across all 3 #61-target insert tools (`insert_paragraph` / `insert_equation` / `insert_image_from_path`; `insert_caption` has its own anchor set)：into_table_cell > after_image_id > after_text > before_text > index > append。Inline equation 拒絕擴大 — 現在拒絕所有 4 個 anchor params（before/after_text + after_image_id + into_table_cell），不只 v3.15.0 的 2 個。Tests: Issue61V315PointReleaseTests (9 sub-tests cross 3 tools)。Suite 185 → 194 (0 fail / 9 pre-existing skips)。**No ooxml-swift dep bump** — 仍 v0.20.5（lib 從 #44 起就 ready）。Follow-up issues 另開：F4 inline equation 更通用設計 (e.g. into_paragraph_with_text) / F6 text anchor 擴及 table-cell paragraphs 與 block-level SDT / F7 getParagraphs().count - 1 message 在 doc 含 tables/SDTs 時 mis-report (pre-existing) / F8 error message 加 tool-prefix / F9 multiple anchor params 同時傳入 silent priority winner / F10 text_instance≤0 normalize。Backward compatible — schema additions optional，既有 v3.15.0 callers 不變；只有 malformed into_table_cell 從 silent fallthrough 改成 structured error（會被 buggy caller 注意到）+ equation message 加 suffix（substring 'Inserted equation' 仍存在）。v3.15.0 closes #61 — insert_paragraph 與 insert_equation 現在接受跟 insert_image_from_path 一致的 anchor 參數（after_text / before_text / text_instance / into_table_cell — into_table_cell 僅 insert_paragraph）。Pre-fix MCP 層 silently drop 這些參數 — JSON schema 接受但 handler dispatch 忽略，呼叫 fall through 到 legacy paragraph_index path 或 append at end。Lib API Document.insertParagraph(_: at: InsertLocation) 從 #44 起就支援所有六種 anchor cases（paragraphIndex / afterImageId / afterTableIndex / intoTableCell / afterText / beforeText），本 release 補齊 MCP 側 wire-up gap，無需 ooxml-swift dep bump（v0.20.5 已足夠）。Anchor priority mirror insert_image_from_path：into_table_cell > after_text > before_text > index > append。Errors（textNotFound / tableIndexOutOfRange / tableCellOutOfRange）回 structured 訊息而非 silent fallthrough — AI caller 能 surface failure 而非拿到位置錯誤的 misleading 'success'。**Inline equation explicit rejection**：insert_equation 在 display_mode=false（inline）時 explicitly 拒絕 after_text / before_text，回 structured error — 語意模糊（'append OMML run into existing para containing this text' vs 'insert new para before/after target para'），inline placement 仍用 paragraph_index。Display-mode equation 建新 paragraph，anchor 語意明確。Tests: Issue61InsertParagraphAnchorsSmokeTests（5 sub-tests：after_text resolution / before_text resolution / text_instance disambiguation / into_table_cell append / textNotFound error）+ Issue61InsertEquationAnchorsSmokeTests（4 sub-tests：after_text + before_text in display mode / inline mode rejection / textNotFound error）。Suite 176 → 185 (0 fail / 9 pre-existing skips)。**No ooxml-swift dep bump** — v0.20.5 已有所有需要的 lib API。Backward compatible — anchor params 全 optional；既有 index / paragraph_index callers 不變；無 schema removal、無既有行為改動。**Real-world impact**：thesis-rescue / template-population workflow 不再需要 fall back 到「append at end + 手動 cut/paste in Word UI」或 binary-search 猜 paragraph_index，AI caller 對 3 #61-target insert tools（insert_image_from_path / insert_paragraph / insert_equation）對稱地用 surrounding context 定位 anchor。v3.14.5 closes Refs #63 verify F1 P1：擴充 findBodyChildContainingText 涵蓋所有 editable surfaces，補上 v3.14.4 CHANGELOG over-claim 的 insert anchor lookup gap。Pre-fix v3.14.4 只修了 REPLACE path（replace_text → Document.replaceInParagraphSurfaces 走 contentControls / hyperlinks / fieldSimples / alternateContents）但 LOOKUP path（findBodyChildContainingText 用於 InsertLocation.afterText / .beforeText 解析）只看 para.runs，所以 insert_image_from_path / insert_paragraph / insert_caption before_text/after_text 對 SDT-wrapped anchor 仍丟 textNotFound。Verify ensemble（5 Claude reviewers + Codex）的 requirements F1 P1 finding 抓到 CHANGELOG over-claim — 用戶選擇 Option B 擴充修而非縮 scope。ooxml-swift v0.20.5 新增 TextReplacementEngine.flatTextOfContentXML（read-only XML walker mirror replaceInContentXML flattening rules，跳過 <w:delText> / <w:instrText> / nested <w:sdt> subtrees）+ Paragraph.flattenedDisplayText 擴充 method 涵蓋 runs + hyperlinks + fieldSimples + alternateContents + contentControls（recursive into nested SDT children）。findBodyChildContainingText 改用 flattenedDisplayText 取代原本的 para.runs.map { $0.text }.joined()。新增 Issue63InsertAnchorInlineSDTTests（lib，3 wrappers × afterText/beforeText/insertImage = 3 sub-tests / 5 assertions）+ Issue63InsertAnchorInlineSDTSmokeTests（MCP，2 sub-tests pin lib-layer fix）。Suite 693 → 696 ooxml-swift / 174 → 176 che-word-mcp（0 fail）。基於 ooxml-swift v0.20.5。Backward compatible — strict superset of pre-fix lookup behavior（找到更多 anchors，既有 plain runs anchor 仍照常運作）。**Insert anchor lookup gap 此 release 完整補齊**，所有 inline wrappers 在 REPLACE + LOOKUP 兩個 path 都對稱覆蓋。v3.14.4 修 replace_text 對 inline `<w:sdt>` content control 的 wrapper coverage gap（Refs #63）：Document.replaceInParagraphSurfaces 之前覆蓋 paragraph.runs / hyperlinks / fieldSimples / alternateContents 但 **沒有** paragraph.contentControls — 包在 inline `<w:sdt>` 裡的文字 silently 0-match。外部 converter（pandoc / Quarto / LaTeX→docx）習慣把 cross-ref placeholder（[tab:foo] / [fig:bar] / [Smith 2020]）包成 inline SDT，所以症狀跟 bracketed text 高度相關，但其實 **brackets 是 coincidence** — bracket-free needle 在 inline SDT 裡也 fail。Issue title「literal `[ ]` brackets」是誤導，差別測試（fldChar / fldSimple / hyperlink / inlineSDT 四個 inline wrapper × 四種 needle）證實只有 inline SDT case 失敗，其他三個 wrapper 從 v0.19.0+ #56 Phase 5 起就 typed-runs 覆蓋好了。Surgical fix architecture：ooxml-swift v0.20.4 新增 TextReplacementEngine.replaceInContentXML（XML DOM walker，wrap ContentControl.content 在 synthetic root xmlns:w，遍歷所有 `<w:t>` descendants 在 document order，build flat string + offset map mirror flattenRuns invariant，run same literal/regex find logic，splice replacements 回 `<w:t>` element string content；re-serialize wrapper children 去掉 wrapper tag）+ Document.replaceInContentControl（recursive helper 涵蓋 cc.content + cc.children 處理 nested SDT）。Wired 進 Document.replaceInParagraphSurfaces 接在 alternateContents loop 之後。設計上跳過：`<w:delText>`（TC deletion text，不顯示）、`<w:instrText>`（field instruction code，不顯示）、nested `<w:sdt>` subtrees（typed cc.children 由外層 recursion 處理避免 double-replacement）。Round-trip discipline：只 mutate `<w:t>` element 的 string content；xml:space=\"preserve\" 與其他 attribute 完整保留（attribute set 從不被 touch）。新增 Issue63InlineSDTReplaceTests（4 個 wrapper × 4 個 needle 的 differential test + nested SDT recursion + round-trip wrapper preservation = 3 sub-tests / 18 assertions）+ MCP-layer Issue63ReplaceTextInlineSDTSmokeTests（2 sub-tests pin lib-layer fix）。Suite 690 → 693 ooxml-swift / 172 → 174 che-word-mcp（0 fail）。基於 ooxml-swift v0.20.4。Backward compatible — surgical fix 只新增 code path，沒改任何既有行為（runs/hyperlinks/fieldSimples/alternateContents replacement path 不動，ContentControl model 維持 raw XML storage 不重構）。Out-of-scope（separate follow-up）：ContentControl 從 content:String 升級為 typed Run 列表（SDD-warranted refactor）；smartTags / bidiOverrides / customXmlBlocks / unrecognizedChildren 維持 raw-carrier passthrough。v3.14.3 sub-stack E of paragraph-level content-equality (closes #66)：Paragraph 新增 w14ParaId / w14TextId 欄位，提取並 round-trip <w:p> opening tag 上的 w14:* 屬性（Word 用於 collaborative editing 和 comment threading 的 revision-tracking GUIDs）。Plain attribute passthrough，String? typing — Word 的 GUIDs 是 8-char hex tokens（NOT RFC 4122 UUIDs），所以 opaque-string round-trip 是正確選擇。Pre-fix v3.14.2 silently dropped 兩個 attributes — 佔了 NTPU 論文 fixture w14:* token loss 的 ~95%（2214 / 2359 lost tokens 是這兩個 attrs）。Post-E 量測：w14: 保留率 10.55% → 93.98%；document.xml 流失 10.95% → 8.02%。Combined with sub-stack D (#65), total impact since v3.14.1：<w:lang> 50% → 98.89% (D)、w14:* 5% → 93.98% (E)、document.xml 流失 16.66% → 8.02% (D+E, -8.64 pp)。Matrix-pin testDocumentContentEqualityInvariant 同步抬升 floor（w14: 0.04→0.90、sizeLossRatio 上限 0.12→0.10）— matrix-pin 現在 LOAD-BEARING across **5 preservation classes**（rFonts/noProof/lang/kern/w14:）spanning run-level + paragraph-level + paragraph-mark scope。Defensive design (R2 review fixes)：openingPTag() routes attributes through escapeXMLAttribute；parseParagraph rejects schema-invalid empty-string GUIDs。基於 ooxml-swift v0.20.3。Backward compatible（兩個 fields 都 optional、default nil；openingPTag empty-attrs gate 防止 synthetic emit）。剩餘 8% 流失主要是其他 w14:* attribute classes（如 w14:* on <w:r>）— tracked as separate follow-up SDD。v3.14.2 sub-stack D of paragraph-level content-equality (closes #65)：ParagraphProperties 新增 markRunProperties 欄位，提取並 round-trip <w:rPr> direct child of <w:pPr> — paragraph-mark formatting per ECMA-376 §17.3.1.27 CT_PPrBase（控制 pilcrow ¶ 字符外觀的字型/顏色/語言/字距）。Reuses parseRunProperties verbatim — schema 跟 run-level CT_RPr 一致，所以 sub-stack C 的 typed extraction（rFonts 4-axis / noProof / kern / lang 3-axis）和 rawChildren passthrough（w14:* 效果）全部免費繼承。NTPU 論文 fixture 量測影響：<w:lang> 保留率 50% → 98.89%；<w:rFonts> 88% → 98.77%；<w:noProof> 92% → 100%；<w:kern> 84% → 99.93%；document.xml 大小流失 16.66% → 10.95%。Matrix-pin testDocumentContentEqualityInvariant 同步抬升 floor（lang 0.45→0.95、rFonts/noProof/kern 0.95、sizeLossRatio 上限 0.175→0.12）。Sub-stack E (#66 w14:paraId/textId) 接著 ship 到 v3.14.3，把流失壓到 < 5%，達成「edit 一個字 → document.xml shrinks <1%」strong demo。基於 ooxml-swift v0.20.2。Backward compatible（markRunProperties optional、default nil、writer empty-gate 防止 synthetic empty <w:rPr/>）。v3.14.1 sub-stack C-CONT closes triple-confirmed P0 (R2 + R5 + Codex 6-AI verify)：recognizedRprChildren Set 列了 ~16+ rPr child kinds 為 'recognized' 但 parseRunProperties 沒有 typed extraction → silent drop。受影響的常見元素：<w:spacing>（character spacing）、<w:caps>/<w:smallCaps>、<w:position>、<w:shd>（run shading）、<w:bdr>、<w:em>（CJK emphasis marks）、<w:effect>、<w:vanish>/<w:specVanish>/<w:webHidden>、<w:outline>/<w:shadow>/<w:emboss>/<w:imprint>、<w:bCs>/<w:iCs>/<w:dstrike>。Fix：trim Set 到 ONLY actually-typed-extracted-or-emitted kinds。Round-trip size loss: pre-fix v3.13.x 32% → v3.14.0 17.75% → v3.14.1 16.66%。Methodology lesson (6th)：P2 from one reviewer can become P0 when another applies real-world impact lens. v3.14.0 closes #60（sub-stack C of #58/#59/#60）— RunProperties field-loss audit。Bump ooxml-swift v0.19.13→v0.20.0。新增 typed fields：4-axis rFonts (ascii/hAnsi/eastAsia/cs/hint — 之前被收斂成單一值)、noProof、kern、3-axis lang (val/eastAsia/bidi)，加上 rawChildren passthrough 處理 unrecognized rPr children（如 w14:textOutline / w14:textFill / w14:glow）。**Pre-fix MCP 用戶看到 eastAsia/cs 字型（如 DFKai-SB 用於繁體中文）在 round-trip 時 silently 被替換成 ascii 值；v3.14.0 完整保留 4 個 axis**。Matrix-pin testDocumentContentEqualityInvariant 加上 preservation-class-3 ratio-floor assertions，現在 LOAD-BEARING — 任何未來 RunProperties regression 都會被 matrix-pin 抓到。Thesis fixture document.xml round-trip 大小：pre-fix 32% 損失 → post-sub-stack-C 17.75% 損失（改善 14.25 percentage points）。剩餘 17.75% 是 paragraph-mark rPr + w14:paraId/textId drops（separate out-of-scope follow-up SDD）。**'if not typed, preserve as raw' 原則架構性完成** — 從 sub-stack A (#58 BodyChild)、B (#59 WhitespaceOverlay) 一路發展到 C (#60 RunProperties)。Backward compatible — 保留 fontName field，mirror rFonts.ascii。v3.13.13 CRITICAL HOTFIX (sub-stack B-CONT-2-CONT) reverted v3.13.12 的 TIER-0 over-fix。v3.13.12 (DO NOT USE — 刪除 <w:del> 內容)。v3.13.11 sub-stack B-CONT。基於 ooxml-swift v0.20.0。","version":"3.20.0","strict":true,"keywords":["mcp","word","docx","ooxml","document","swift"],"category":"productivity"}]}