The Adeu Text Projection: Fidelity Model
August 22, 2026 · View on GitHub
Adeu edits DOCX files through a text proxy: extract (CLI) / read_docx
(MCP) project the document into Markdown-flavored text, and apply /
process_document_batch translate text changes back into tracked OOXML
changes. This page states precisely which document properties that projection
preserves exactly, which it normalizes, and which it omits — so
agents and automation never assume a guarantee the representation does not
make (QA 2026-07-19 ADEU-QA-007).
Terminology used below:
- Exact — round-trips byte-for-byte through extract → apply.
- Normalized — projected in a canonical form; applying text back preserves the underlying structure, but the projection is not a literal rendering of the XML.
- Read-only — visible in the projection but rejected as an edit target
(
BatchValidationErroron any attempt to modify or fabricate it). - Omitted — not visible in the projection at all; untouched by edits.
| Property | Fidelity | Details |
|---|---|---|
| Body text | Exact | Character-for-character, including Unicode. Smart quotes are matched tolerantly on lookup but never rewritten in the document. |
| Paragraph identity | Exact | One projected block (\n\n-separated) per w:p, in strict XML document order (tables interleaved where they occur). |
| Bold / italic | Normalized | Projected as **bold** and _italic_ (never *italic*). Markers hug non-whitespace; adjacent same-style runs are coalesced. Other run formatting (underline, color, fonts, size) is omitted from the text but preserved through edits — untouched runs are never rewritten. |
| Headings | Normalized | Style-based headings project as #–######. Depth is clamped to 6. |
| Lists | Normalized | Numbered items always project as the constant 1. (Markdown renumbers); bullets as * ; nesting as 4 spaces per level. The real numPr/ilvl structure is preserved and reconstructed on write. Do not assume the projected number is the rendered number. |
| Tables | Normalized | Cells joined by |, rows by single newlines. The | boundary is a hard cell wall: text edits across it are split per-cell; structural row/column changes via text are rejected (use insert_row / delete_row). |
| Headers / footers / footnotes / endnotes | Normalized | Projected as separate parts flattened into one string. Edits can never cross a part boundary; comments are rejected outside the main body. Footnotes project as [^fn-ID] markers with bi-directional editing. |
| Hyperlinks | Normalized | [text](url). Editing text produces tracked changes; editing the URL retargets the relationship silently (no redline). |
| Images / drawings | Read-only | Projected as . Cannot be added, removed, or retitled via text; diff reports image differences as warnings, never as edits. |
| Cross-references | Read-only | Projected as [~text~](#_Ref). Display text is computed by Word; modifying it is rejected. |
| Bookmarks / anchors | Read-only | Projected inline as {#name}. {#cell:paraId} anchors are valid targets for writing into empty table cells, but the tokens themselves cannot be edited or fabricated. |
| Tracked changes / comments | Normalized | CriticMarkup ({++ins++}, {--del--}, {>>meta<<}). A replacement's del+ins pair is annotated (pairs with Chg:N) — one accept/reject resolves the whole pair. The clean view (--clean-view) renders the accepted state instead. |
| Content controls (text, date, dropdown, richtext) | Normalized | Projected inline as {#cc:N}…{#/cc:N} anchor pairs; the open token carries the flags locked, bound, and group when applicable, and an empty field shows its prompt as a {>>placeholder: …<<} bubble between the anchors. set_field fills values with type validation; data-bound controls (w:dataBinding) dual-write the underlying XML store (customXml or docProps/core.xml). |
Checkboxes (w14:checkbox) | Normalized | Projected as ASCII [ ] / [x] tokens (never raw ballot glyphs). Toggles render inside CriticMarkup {++[x]++}{--[ ]--}. |
Fields ledger (mode="fields") | Read-only | Structural listing of form fields with IDs, tags, aliases, types, states, option lists, and data bindings. Paged via fields_offset. |
| Fields (TOC, page numbers, computed values) | Omitted / read-only | Field results may appear as text where Word cached them; field codes are not projected. TOC boundaries are guarded via the structural appendix. |
| Page breaks / line breaks | Normalized | A line break projects as a newline; a page break (w:br w:type="page") projects as U+000C FORM FEED, the conventional plain-text page separator, so pagination can honour manual breaks without putting markup in the text. Every w:br type is preserved in the XML — runs containing breaks are immutable boundaries. Both engines project both forms identically. |
| Section breaks / page geometry | Omitted | Margins, columns, orientation and w:sectPr are invisible to the projection and never modified (a deleted paragraph's sectPr is explicitly carried over). |
| Text boxes / drawing canvases / SmartArt | Omitted | Content inside floating drawing objects is not projected and cannot be edited via text. |
"Pages" (--page) | Synthetic | Adeu pages are length-based content chunks sized for LLM consumption. They do not correspond to printed Word pages or explicit page breaks; banners say so explicitly. Outline mode ignores --page. |
| Document metadata | Out of scope for extract | Core/custom properties, document variables (w:docVar), RSIDs and timestamps are invisible to extract; adeu sanitize is the tool that manages them, and its report enumerates the categories it scrubbed. |
The load-bearing guarantees
- Untouched content is never rewritten. The engine operates in surgical mode: paragraphs your edits do not touch keep their exact XML, whitespace included.
- Text edits become tracked changes (
w:ins/w:del) attributed to the configured author — never silent rewrites. - Structural constructs fail loudly. Anything the text proxy cannot
safely express (read-only markers, cross-part spans, table structure via
text, unbalanced paragraph merges) is rejected with a
BatchValidationErrorbefore anything is written — never applied approximately. - Exit status is authoritative. File existence does not imply success:
a failed
applywrites no file at the requested output path (a.unverified.docxdiagnostic may exist alongside), andsanitizeprintsResult: CLEANonly after re-scanning the saved bytes.