dsh-office-kit 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/assets/skills/excel-live-control/SKILL.md +119 -152
  2. package/assets/skills/excel-live-control/domain_guidance/financial_models.md +42 -58
  3. package/assets/skills/excel-live-control/domain_guidance/healthcare.md +36 -42
  4. package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +33 -34
  5. package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +34 -36
  6. package/assets/skills/excel-live-control/references/office-automation.md +127 -153
  7. package/assets/skills/excel-live-control/references/read_only_qna.md +40 -45
  8. package/assets/skills/excel-live-control/style_guidelines.md +56 -64
  9. package/assets/skills/excel-live-control/workflows/create_workflows.md +40 -47
  10. package/assets/skills/excel-live-control/workflows/edit_workflows.md +37 -45
  11. package/assets/skills/office-documents/SKILL.md +24 -60
  12. package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +5 -5
  13. package/assets/skills/office-documents/ooxml/comments.md +8 -8
  14. package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +10 -10
  15. package/assets/skills/office-documents/ooxml/rels_and_content_types.md +7 -7
  16. package/assets/skills/office-documents/ooxml/tracked_changes.md +12 -12
  17. package/assets/skills/office-documents/references/docx-structure.md +11 -13
  18. package/assets/skills/office-documents/references/template-elicitation.md +11 -13
  19. package/assets/skills/office-documents/references/writing-quality.md +20 -20
  20. package/assets/skills/office-documents/scripts/add_tracked_replacements.py +4 -3
  21. package/assets/skills/office-documents/scripts/docx_kit.py +68 -27
  22. package/assets/skills/office-documents/scripts/heading_audit.py +3 -4
  23. package/assets/skills/office-documents/scripts/internal_nav.py +3 -1
  24. package/assets/skills/office-documents/scripts/merge_docx_append.py +104 -0
  25. package/assets/skills/office-documents/scripts/table_geometry.py +13 -3
  26. package/assets/skills/office-documents/scripts/watermark_add.py +6 -5
  27. package/assets/skills/office-documents/scripts/watermark_audit_remove.py +6 -4
  28. package/assets/skills/office-documents/tasks/accessibility_a11y.md +16 -22
  29. package/assets/skills/office-documents/tasks/captions_crossrefs.md +23 -26
  30. package/assets/skills/office-documents/tasks/clean_tracked_changes.md +17 -22
  31. package/assets/skills/office-documents/tasks/comments_manage.md +9 -11
  32. package/assets/skills/office-documents/tasks/compare_diff.md +10 -16
  33. package/assets/skills/office-documents/tasks/create_edit.md +14 -16
  34. package/assets/skills/office-documents/tasks/equations.md +17 -28
  35. package/assets/skills/office-documents/tasks/fields_update.md +14 -16
  36. package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +19 -33
  37. package/assets/skills/office-documents/tasks/footnotes_endnotes.md +12 -14
  38. package/assets/skills/office-documents/tasks/forms_content_controls.md +16 -20
  39. package/assets/skills/office-documents/tasks/headings_numbering.md +17 -22
  40. package/assets/skills/office-documents/tasks/images_figures.md +15 -25
  41. package/assets/skills/office-documents/tasks/multi_doc_merge.md +13 -23
  42. package/assets/skills/office-documents/tasks/navigation_internal_links.md +18 -21
  43. package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +16 -22
  44. package/assets/skills/office-documents/tasks/protection_restrict_editing.md +19 -21
  45. package/assets/skills/office-documents/tasks/read_review.md +9 -20
  46. package/assets/skills/office-documents/tasks/redaction_anonymization.md +17 -18
  47. package/assets/skills/office-documents/tasks/sections_layout.md +16 -23
  48. package/assets/skills/office-documents/tasks/style_lint_normalize.md +18 -22
  49. package/assets/skills/office-documents/tasks/tables_spreadsheets.md +21 -31
  50. package/assets/skills/office-documents/tasks/templates_style_packs.md +17 -21
  51. package/assets/skills/office-documents/tasks/toc_workflow.md +10 -14
  52. package/assets/skills/office-documents/tasks/verify_render.md +11 -23
  53. package/assets/skills/office-documents/tasks/watermarks_background.md +10 -14
  54. package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +6 -6
  55. package/assets/skills/office-documents/troubleshooting/run_splitting.md +12 -12
  56. package/assets/skills/office-pdf/SKILL.md +36 -51
  57. package/assets/skills/office-pdf/references/form-fields.md +20 -20
  58. package/assets/skills/office-pdf/references/geometry.md +10 -10
  59. package/assets/skills/office-pdf/references/layout-quality.md +13 -13
  60. package/assets/skills/office-pdf/references/limits.md +12 -56
  61. package/assets/skills/office-pdf/tasks/annotations.md +5 -5
  62. package/assets/skills/office-pdf/tasks/create.md +6 -9
  63. package/assets/skills/office-pdf/tasks/edit-pages.md +14 -14
  64. package/assets/skills/office-pdf/tasks/forms.md +15 -15
  65. package/assets/skills/office-pdf/tasks/read.md +9 -9
  66. package/assets/skills/office-pdf/tasks/redact.md +18 -18
  67. package/assets/skills/office-pdf/tasks/stamp.md +10 -10
  68. package/assets/skills/office-presentations/SKILL.md +71 -91
  69. package/assets/skills/office-presentations/container_tools/preview_render.py +4 -1
  70. package/assets/skills/office-presentations/container_tools/render_slides.py +1 -1
  71. package/assets/skills/office-presentations/domain_guidance/finance.md +16 -16
  72. package/assets/skills/office-presentations/recipes/README.md +24 -35
  73. package/assets/skills/office-presentations/recipes/autolayout.md +12 -13
  74. package/assets/skills/office-presentations/recipes/connectors.md +11 -12
  75. package/assets/skills/office-presentations/recipes/images.md +11 -12
  76. package/assets/skills/office-presentations/recipes/master-and-layouts.md +18 -18
  77. package/assets/skills/office-presentations/recipes/native-charts.md +8 -8
  78. package/assets/skills/office-presentations/recipes/rich-text-runs.md +13 -13
  79. package/assets/skills/office-presentations/recipes/speaker-notes.md +7 -7
  80. package/assets/skills/office-presentations/recipes/tables.md +14 -14
  81. package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +9 -9
  82. package/assets/skills/office-presentations/recipes/theme-tokens.md +7 -8
  83. package/assets/skills/office-presentations/references/authoring-workflow.md +38 -43
  84. package/assets/skills/office-presentations/references/finalization.md +10 -12
  85. package/assets/skills/office-presentations/references/native-bullets.md +14 -18
  86. package/assets/skills/office-presentations/references/native-evidence.md +18 -32
  87. package/assets/skills/office-presentations/references/style-guidelines.md +27 -32
  88. package/assets/skills/office-presentations/references/templates.md +31 -60
  89. package/assets/skills/office-presentations/routing/google_slides.md +4 -4
  90. package/assets/skills/office-presentations/tasks/apply-annotations.md +17 -17
  91. package/assets/skills/office-presentations/tasks/charts-tables-notes.md +20 -23
  92. package/assets/skills/office-presentations/tasks/create-deck.md +19 -19
  93. package/assets/skills/office-presentations/tasks/edit-deck.md +14 -16
  94. package/assets/skills/office-presentations/tasks/inspect-and-verify.md +31 -34
  95. package/assets/skills/office-presentations/tasks/template-following.md +23 -28
  96. package/assets/skills/office-spreadsheets/SKILL.md +32 -39
  97. package/assets/skills/office-spreadsheets/recipes/charts.md +8 -26
  98. package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +9 -26
  99. package/assets/skills/office-spreadsheets/recipes/data-tables.md +7 -23
  100. package/assets/skills/office-spreadsheets/recipes/named-ranges.md +8 -21
  101. package/assets/skills/office-spreadsheets/recipes/number-formats.md +7 -23
  102. package/assets/skills/office-spreadsheets/recipes/sparklines.md +7 -20
  103. package/assets/skills/office-spreadsheets/references/charts.md +15 -34
  104. package/assets/skills/office-spreadsheets/references/domain/financial-models.md +10 -25
  105. package/assets/skills/office-spreadsheets/references/domain/healthcare.md +8 -19
  106. package/assets/skills/office-spreadsheets/references/domain/marketing.md +8 -22
  107. package/assets/skills/office-spreadsheets/references/domain/research.md +8 -19
  108. package/assets/skills/office-spreadsheets/references/formula-patterns.md +20 -50
  109. package/assets/skills/office-spreadsheets/references/style-guidelines.md +19 -44
  110. package/assets/skills/office-spreadsheets/references/workbook-structure.md +12 -35
  111. package/assets/skills/office-spreadsheets/routing/google-sheets.md +11 -26
  112. package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +10 -25
  113. package/assets/skills/office-spreadsheets/tasks/annotations.md +12 -30
  114. package/assets/skills/office-spreadsheets/tasks/create-workbook.md +5 -5
  115. package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +2 -2
  116. package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +5 -5
  117. package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +9 -9
  118. package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +10 -30
  119. package/assets/skills/shared/annotation-recipes.md +98 -120
  120. package/assets/skills/shared/common-workflow.md +70 -80
  121. package/dist/index.js +1 -1
  122. package/package.json +1 -1
  123. package/python/office_cli.py +38 -4
@@ -1,77 +1,39 @@
1
1
  ---
2
- description: Build, inspect, revise, and visually verify Word .docx files using the Office helper CLI and the bundled XML scripts.
2
+ description: Create, read, edit, render, and verify Word documents (.docx) — build new files, revise existing ones, and check the rendered pages.
3
3
  ---
4
4
 
5
5
  # Documents (.docx)
6
6
 
7
- A `.docx` is a zip of XML parts: `word/document.xml` holds the body, and headers, footers, footnotes, comments, styles and numbering live beside it. Two routes reach every part of it:
8
-
9
- - **Fast path** — the helper CLI inspects, renders and creates: `docx create`, `docx read`, `docx render`. It performs no in-place mutation.
10
- - **Structural path** — `scripts/*.py` in this skill do all editing: replace text, append blocks, set properties, and everything structural — comments, tracked changes, fields, protection, watermarks.
11
-
12
- Pick the cheapest route that produces the structure the user asked for, then verify it visually. Never invent content, and never deliver a file you have not looked at.
7
+ A `.docx` is a zip of XML parts: `word/document.xml` holds the body, with headers, footers, footnotes, comments, styles and numbering beside it. Two routes reach them: the helper CLI (`docx create`, `docx read`, `docx render`, no in-place mutation) and `scripts/*.py`, which edits everything structural — text, blocks, properties, comments, tracked changes, fields, protection, watermarks. Pick the cheapest route that yields the requested structure, then look at the pages. Never invent content; never deliver a file you have not looked at.
13
8
 
14
9
  ## Before any file work
15
10
 
16
- 1. Call the `office_status` tool.
17
- 2. Read the `## Office runtime` block at the end of this file for the verified `python`, `cli`, `skill` and `shared` paths.
18
- 3. If that block says no runtime is available, stop and give the user the `node "..."` remedy command it names, then call `office_status` again. Do not fake a document or claim success.
19
-
20
- Quote every path. Spaces, non-ASCII names and `&` are normal on Windows.
11
+ Call `office_status`, then read the `## Office runtime` block at the end of this file — it holds the verified `python`, `cli`, `skill` and `shared` paths for this install. If there is no runtime, stop, give the user the `node "..."` remedy command, and call `office_status` again — never fake a document or claim success. Quote every path: spaces, non-ASCII names and `&` are normal on Windows.
21
12
 
22
13
  ## Delivery
23
14
 
24
- Always hand the finished file to the user with the `present` tool, then summarise in one sentence what changed. A bare path is not delivery: the chat transcript renders `.docx`/`.pptx`/`.xlsx`/`.pdf` links as "二进制文件,无法显示改动" and the user cannot open them.
25
-
26
- - Creating a file: write it into the user's workspace, verify it, then `present`.
27
- - Editing an existing file: never overwrite the original. Write `<original>-edited.<ext>` (or a name the user asked for), verify it, `present` it, and state both paths.
15
+ Deliver with `present` plus a one-sentence summary. A bare path is not delivery: the transcript renders `.docx`/`.pptx`/`.xlsx`/`.pdf` links as "二进制文件,无法显示改动". Write new files into the workspace, verify, `present`. Never overwrite an original — write `<original>-edited.<ext>` (or the user's name), verify, `present`, and state both paths.
28
16
 
29
17
  ## Annotations
30
18
 
31
- The user may reply with an `office-annotations` block: `[{ page, text, requirement, region: { left, top, width, height } }]`. `region` is normalised 0–1 against the rendered preview page the user drew on; `requirement` is free-form natural language. Treat it as a change request, not as editor state: locate what the region covers, apply the smallest edit that satisfies `requirement`, re-render, and confirm the region now looks right.
32
-
33
- When the payload cannot be mapped to a concrete object (the region is empty or the file has no geometry), do not guess silently — say what you could not locate.
34
-
35
- For `.docx`, `page` is the page of the *rendered PDF preview*, not a Word page. Use `text` (and the paragraph text near it) to locate the target; the region is a hint for near-misses only. A region that lands on a table row, a caption or a header is ambiguous — state which object you picked before editing.
19
+ An `office-annotations` payload is `[{ page, text, requirement, region: { left, top, width, height } }]`: `region` normalised 0–1 against the rendered preview page, `requirement` free-form. Treat it as a change request, not editor state — find what the region covers, apply the smallest edit that satisfies `requirement`, re-render, confirm. Unmappable payload (empty region, no geometry): say what you could not locate; never guess. For `.docx`, `page` is a rendered PDF page, not a Word page: locate by `text` and its neighbouring paragraphs, use the region only for near-misses, and name the object you picked when a region lands on a table row, caption or header.
36
20
 
37
21
  ## Verify
38
22
 
39
- Rendering is the only proof that a document looks right.
40
-
41
23
  ```bash
42
24
  "<python>" "<cli>" docx render --input "<file.docx>" --out-dir "<render-dir>"
43
25
  ```
44
26
 
45
- The command writes a PDF plus one PNG per page and prints the paths. Open each PNG with `read_image` and check: heading hierarchy, table width and cell wrapping, page breaks, header/footer, clipped or overflowing text, missing glyphs (dashes, CJK, symbols), equation rendering, and caption placement.
46
-
47
- - Iterate until the pages look correct, then deliver. "It parses" is not verification.
48
- - If the runtime reports no renderer (`renderers.can_render.docx: false` — the DSH LibreOffice kit is missing), ask the user to fix the install, or fall back to `docx read` plus the audit scripts. Say plainly that layout was not visually verified — do not describe a page you did not see.
49
- - After a script edits XML, re-render. A package can stay valid while the page it produces is broken.
50
-
51
- ## Choosing a route
52
-
53
- | Job | Fast path | Structural path |
54
- |---|---|---|
55
- | New document from scratch | `docx create --spec` | `python-docx` snippet in `tasks/create_edit.md` |
56
- | Replace or append text and blocks | — | `scripts/replace_text.py`, `scripts/append_blocks.py` |
57
- | Document properties | — | `scripts/core_properties.py` |
58
- | Read structure | `docx read` | `scripts/docx_kit.py text`, `style_lint`, audits |
59
- | Comments, tracked changes | — | `scripts/comments_*.py`, `scripts/*tracked*.py` |
60
- | Fields, TOC, cross-references | — | `scripts/insert_toc.py`, `insert_ref_fields.py`, `fields_*.py` |
61
- | Footnotes, endnotes | — | `scripts/insert_note.py`, `footnotes_report.py` |
62
- | Tables, layout geometry | `docx create` table block | `scripts/table_geometry.py`, `docx_table_to_csv.py` |
63
- | Styles, templates | — | `scripts/style_*.py`, `apply_template_styles.py` |
64
- | Protection, watermarks, privacy | — | `scripts/set_protection.py`, `watermark_*.py`, `privacy_scrub.py`, `redact_docx.py` |
65
-
66
- Where no verb exists, the fallback is the listed script or a short `python-docx`/`lxml` snippet. Both are runnable with the runtime `python`; neither needs a new dependency.
27
+ Writes a PDF and one PNG per page and prints the paths. Open each PNG with `read_image`; check heading hierarchy, table width and cell wrapping, page breaks, header/footer, clipped or overflowing text, missing glyphs (dashes, CJK, symbols), equations, caption placement. Iterate, then deliver — "it parses" is not verification. Without a renderer (`office_status` reports `renderers.available: false`, with a `remedy`) ask the user to fix the install or fall back to `docx read` plus the audit scripts; say layout was not visually verified and never describe an unseen page. Re-render after every script edit: a package can be valid while its page is broken. Details: `tasks/verify_render.md`. Setup: `troubleshooting/libreoffice_headless.md`.
67
28
 
68
29
  ## Task index
69
30
 
70
- | Task | Read | Main tool |
31
+ | Job | Task doc | CLI or main tool |
71
32
  |---|---|---|
72
- | Create or edit a document | `tasks/create_edit.md` | CLI create, `replace_text.py`, `append_blocks.py`, `core_properties.py` |
73
- | Read and review a file | `tasks/read_review.md` | CLI read, `docx_kit.py text` |
74
- | Comments | `tasks/comments_manage.md` | `comments_add/extract/apply_patch/strip.py` |
33
+ | Create a document | `tasks/create_edit.md` | `docx create --spec`, `python-docx` snippet, `append_blocks.py` |
34
+ | Read and review a file | `tasks/read_review.md` | `docx read`, `docx_kit.py text`, `style_lint`, audits |
35
+ | Edit text, blocks or properties | `tasks/create_edit.md` | `replace_text.py`, `append_blocks.py`, `core_properties.py` |
36
+ | Comments | `tasks/comments_manage.md` | `comments_*.py` |
75
37
  | Tracked changes | `tasks/clean_tracked_changes.md` | `accept_tracked_changes.py`, `add_tracked_replacements.py` |
76
38
  | Fields and updates | `tasks/fields_update.md` | `fields_report.py`, `fields_materialize.py` |
77
39
  | Table of contents | `tasks/toc_workflow.md` | `insert_toc.py`, `fields_*.py` |
@@ -79,7 +41,7 @@ Where no verb exists, the fallback is the listed script or a short `python-docx`
79
41
  | Navigation links | `tasks/navigation_internal_links.md` | `internal_nav.py` |
80
42
  | Footnotes and endnotes | `tasks/footnotes_endnotes.md` | `insert_note.py`, `footnotes_report.py` |
81
43
  | Headings and numbering | `tasks/headings_numbering.md` | `heading_audit.py` |
82
- | Tables | `tasks/tables_spreadsheets.md` | `table_geometry.py`, `docx_table_to_csv.py`, `xlsx_to_docx_table.py` |
44
+ | Tables and geometry | `tasks/tables_spreadsheets.md` | `docx create` table block, `table_geometry.py`, `docx_table_to_csv.py`, `xlsx_to_docx_table.py` |
83
45
  | Images and figures | `tasks/images_figures.md` | `images_audit.py` |
84
46
  | Sections and page setup | `tasks/sections_layout.md` | `section_audit.py` |
85
47
  | Styles | `tasks/style_lint_normalize.md` | `style_lint.py`, `style_normalize.py` |
@@ -91,24 +53,26 @@ Where no verb exists, the fallback is the listed script or a short `python-docx`
91
53
  | Privacy and metadata | `tasks/privacy_scrub_metadata.md` | `privacy_scrub.py` |
92
54
  | Redaction | `tasks/redaction_anonymization.md` | `redact_docx.py` |
93
55
  | Merging documents | `tasks/multi_doc_merge.md` | `merge_docx_append.py` |
94
- | Comparing versions | `tasks/compare_diff.md` | `render_and_diff.py` |
95
- | Equations | `tasks/equations.md` | CLI `equation` block |
96
- | Visual verification | `tasks/verify_render.md` | `render_docx.py` (skill root) |
56
+ | Comparing versions | `tasks/compare_diff.md` | `docx render`, `render_and_diff.py` |
57
+ | Equations | `tasks/equations.md` | `docx create` `equation` block |
58
+ | Visual verification | `tasks/verify_render.md` | `docx render`, `render_docx.py` (skill root) |
97
59
  | Fixtures and edge cases | `tasks/fixtures_edge_cases.md` | `make_fixtures.py` |
98
60
 
61
+ Where no verb exists, use the listed script or a short `python-docx`/`lxml` snippet — both run on the runtime `python` without a new dependency.
62
+
99
63
  ## Document defaults
100
64
 
101
65
  - Letter 8.5×11 portrait (A4 when the user or template says so), 1in margins, body 11–12pt, single column.
102
- - Black headings and body text; colour only for deliberate emphasis, links and chart series.
103
- - Tables: light grey `#D9D9D9` borders, a bold header row, `w:tblHeader` so the header repeats across pages, no vertical merges in wide tables.
104
- - No decorative callout boxes, drop shadows or coloured sidebars. Structure comes from headings, lists and tables.
105
- - Captions sit under figures and above tables, in the `Caption` style, and stay with their object.
106
- - Prefer native structures — heading styles, real lists, real tables, OMML equations — over hand-positioned runs, because the next edit depends on that structure.
66
+ - Black headings and body; colour only for deliberate emphasis, links and chart series.
67
+ - Tables: `#D9D9D9` borders, bold header row, `w:tblHeader` repeat, no vertical merges in wide tables.
68
+ - No callout boxes, drop shadows or coloured sidebars — structure comes from headings, lists and tables.
69
+ - Captions under figures and above tables, `Caption` style, staying with the object.
70
+ - Prefer native headings, lists, tables and OMML equations over hand-positioned runs: the next edit depends on that structure.
107
71
 
108
72
  ## Writing quality
109
73
 
110
- Write the content the user asked for, in plain sentences, without filler. `references/writing-quality.md` holds the checklist; `references/docx-structure.md` explains which package part each feature lives in before you touch XML.
74
+ Plain sentences, no filler. Checklist `references/writing-quality.md`; package parts `references/docx-structure.md`.
111
75
 
112
76
  ## Shared workflow
113
77
 
114
- Cross-format habits, the review loop and how to read annotation payloads live in `"<shared>/common-workflow.md"` and `"<shared>/annotation-recipes.md"`; read them when the job spans more than one Office format.
78
+ Cross-format habits and annotation payloads: `"<shared>/common-workflow.md"`, `"<shared>/annotation-recipes.md"`.
@@ -1,6 +1,6 @@
1
1
  # End-to-end smoke test
2
2
 
3
- A short job that touches creation, inspection, structural changes and verification. Run it once after a runtime change, or as a template for "is this environment healthy".
3
+ A short job touching creation, inspection, structural change and verification. Run it after a runtime change, or as a template for "is this environment healthy".
4
4
 
5
5
  ```bash
6
6
  PY="<python>"; CLI="<cli>"; S="<skill>/scripts"; R="<skill>"; W="/tmp/docx-smoke"
@@ -41,7 +41,7 @@ Expect `ok: true` and non-zero `headings`, `tables`, `equations`.
41
41
  "$PY" "$S/style_lint.py" "$W/base.docx"
42
42
  ```
43
43
 
44
- Expect the sections, the parts list to include `word/document.xml`, Letter 8.5×11 portrait, and an empty style-lint `counts`.
44
+ Expect the sections, `word/document.xml` in the parts list, Letter 8.5×11 portrait, and an empty style-lint `counts`.
45
45
 
46
46
  ## 3. Change it structurally
47
47
 
@@ -63,7 +63,7 @@ Expect the sections, the parts list to include `word/document.xml`, Letter 8.5×
63
63
  "$PY" "$S/table_geometry.py" "$W/with-toc.docx"
64
64
  ```
65
65
 
66
- Audits may exit 1 with findings — that is a report, not a failure of the environment. Every finding should be explainable.
66
+ Audits may exit 1 with findings: that is a report, not an environment failure. Every finding should be explainable.
67
67
 
68
68
  ## 5. Verify visually
69
69
 
@@ -71,7 +71,7 @@ Audits may exit 1 with findings — that is a report, not a failure of the envir
71
71
  "$PY" "$R/render_docx.py" "$W/with-toc.docx" --output_dir "$W/render" --dpi 150
72
72
  ```
73
73
 
74
- Then open each PNG with `read_image`. Without the DSH LibreOffice kit the command exits 3 with `no_renderer`; that is a setup problem, not a document problem, and the rest of the test still stands.
74
+ Open each PNG with `read_image`. Without the DSH LibreOffice kit the command exits 3 with `no_renderer`: a setup problem, not a document problem, and the rest of the test still stands.
75
75
 
76
76
  ## 6. Deliver
77
77
 
@@ -83,4 +83,4 @@ Copy the final file into the user's workspace, `present` it, and state what chan
83
83
  rm -rf "$W"
84
84
  ```
85
85
 
86
- Nothing under the workspace should be left behind from a smoke test.
86
+ Nothing under the workspace should be left behind by a smoke test.
@@ -1,6 +1,6 @@
1
1
  # Comments in the package
2
2
 
3
- A comment is three pieces that must agree: `word/comments.xml` holds the content, `word/document.xml` holds a range marking which text is discussed, and the relationship plus content-type override make the part exist at all.
3
+ A comment is three pieces that must agree: `word/comments.xml` holds the content, `word/document.xml` holds a range marking which text is discussed, and a relationship plus content-type override make the part exist at all.
4
4
 
5
5
  ## The markup
6
6
 
@@ -20,21 +20,21 @@ A comment is three pieces that must agree: `word/comments.xml` holds the content
20
20
 
21
21
  Rules that matter in practice:
22
22
 
23
- - `w:id` must be unique across comments **and** match in all three places. A range start without an end leaves Word showing a comment with no anchor; an end without a start can make the file refuse to open.
24
- - The comment reference run belongs at the end of the range, not somewhere else in the paragraph.
23
+ - `w:id` is unique across comments **and** matches in all three places. A range start without an end leaves Word showing a comment with no anchor; an end without a start can make the file refuse to open.
24
+ - The comment reference run belongs at the end of the range, not elsewhere in the paragraph.
25
25
  - `word/comments.xml` needs an override in `[Content_Types].xml` and a relationship from `word/document.xml`; without both, the part is ignored or the file is corrupt.
26
- - The file's root has `mc:Ignorable` listing `w14`/`w15` etc.; a `w15:done` attribute (the "resolved" flag) is only legal when those namespaces are declared.
26
+ - The file root lists `w14`/`w15` in `mc:Ignorable`; the `w15:done` "resolved" attribute is only legal with those namespaces declared.
27
27
 
28
28
  ## Resolved versus deleted
29
29
 
30
- `w15:done="1"` on the `w:comment` keeps the comment in the file and shows it as resolved. Removing the comment is a different operation: delete the `w:comment`, both range markers and the reference run. Resolving hides it from the default pane; it does not remove the text from the file.
30
+ `w15:done="1"` on `w:comment` keeps the comment in the file and shows it as resolved. Removing it is a different operation: delete the `w:comment`, both range markers and the reference run. Resolving hides the comment from the default pane; it does not remove the text from the file.
31
31
 
32
32
  ## Where comments hide
33
33
 
34
- - Replies are comments too, linked by `w15:paraIdParent`; a strip that removes the parent leaves an orphan.
34
+ - Replies are comments too, linked by `w15:paraIdParent`; removing the parent leaves an orphan.
35
35
  - Anchors can sit in headers, footnotes and text boxes as well as the body.
36
- - A comment deleted in Word leaves no trace; a comment whose range was deleted may remain in `comments.xml` with nothing anchoring it. That is why extraction reports anchors: a comment with empty anchored text is evidence of drift.
36
+ - A comment deleted in Word leaves no trace, but a comment whose range was deleted may remain in `comments.xml` with nothing anchoring it. That is why extraction reports anchors: a comment with empty anchored text is evidence of drift.
37
37
 
38
38
  ## Working from the scripts
39
39
 
40
- `comments_extract.py` reads the pairing and prints anchored text per comment; `comments_add.py` writes all three pieces; `comments_apply_patch.py` edits content and the resolved flag; `comments_strip.py` removes every trace and the part itself. Use `docx_kit.py xml '<file.docx>' word/comments.xml` when you need to see the raw part.
40
+ `comments_extract.py` reads the pairing and prints anchored text per comment; `comments_add.py` writes all three pieces; `comments_apply_patch.py` edits content and the resolved flag; `comments_strip.py` removes every trace and the part itself. Use `docx_kit.py xml '<file.docx>' word/comments.xml` for the raw part.
@@ -1,6 +1,6 @@
1
1
  # Hyperlinks and fields
2
2
 
3
- A hyperlink and a field are both "a run that looks like text but is a pointer", and both break in the same way: the visible result is cached, so editing the text does not update anything.
3
+ A hyperlink and a field are both "a run that looks like text but is a pointer", and both break the same way: the visible result is cached, so editing the text updates nothing.
4
4
 
5
5
  ## Hyperlinks
6
6
 
@@ -10,11 +10,11 @@ A hyperlink and a field are both "a run that looks like text but is a pointer",
10
10
  </w:hyperlink>
11
11
  ```
12
12
 
13
- - The target is a relationship in `word/_rels/document.xml.rels` with `TargetMode="External"` for a URL, or a bare relative path for a file inside the package.
13
+ - The target is a relationship in `word/_rels/document.xml.rels`: `TargetMode="External"` for a URL, a bare relative path for a file inside the package.
14
14
  - An internal link uses `w:anchor="bookmarkName"` and no relationship at all.
15
- - `w:hyperlink` is a wrapper around runs; a link that spans paragraphs is not legal — put one link per paragraph.
15
+ - `w:hyperlink` wraps runs; a link spanning paragraphs is not legal — one link per paragraph.
16
16
 
17
- A link whose relationship is missing renders as blue underlined text that goes nowhere. That is also what a copy-paste between documents produces.
17
+ A link whose relationship is missing renders as blue underlined text that goes nowhere. Copy-paste between documents produces the same thing.
18
18
 
19
19
  ## Simple fields
20
20
 
@@ -24,7 +24,7 @@ A link whose relationship is missing renders as blue underlined text that goes n
24
24
  </w:fldSimple>
25
25
  ```
26
26
 
27
- The `w:t` inside is a cache, not the truth. Change the text and the next field update overwrites it.
27
+ The `w:t` inside is a cache, not the truth: change the text and the next field update overwrites it.
28
28
 
29
29
  ## Complex fields
30
30
 
@@ -36,13 +36,13 @@ The `w:t` inside is a cache, not the truth. Change the text and the next field u
36
36
  <w:r><w:fldChar w:fldCharType="end"/></w:r>
37
37
  ```
38
38
 
39
- The begin/instrText/separate/result/end sequence must stay in that order inside one paragraph. Word shows the result between `separate` and `end`, or recomputes on open when `w:updateFields` is set in `settings.xml`:
39
+ The begin/instrText/separate/result/end sequence keeps that order inside one paragraph. Word shows the result between `separate` and `end`, or recomputes on open when `settings.xml` sets:
40
40
 
41
41
  ```xml
42
42
  <w:updateFields w:val="true"/>
43
43
  ```
44
44
 
45
- Setting it makes Word prompt or update on open. A viewer that ignores it (a PDF export, some libraries) keeps showing the cache — which is why a document delivered as PDF can show "1" for every page number.
45
+ That makes Word prompt or update on open. A viewer that ignores it (PDF export, some libraries) keeps the cache — which is why a document delivered as PDF can show "1" for every page number.
46
46
 
47
47
  ## Which field for which job
48
48
 
@@ -57,9 +57,9 @@ Setting it makes Word prompt or update on open. A viewer that ignores it (a PDF
57
57
  ## Editing fields safely
58
58
 
59
59
  - To freeze a result, replace the whole field span with a plain run (`fields_materialize.py`, `flatten_ref_fields.py`).
60
- - To renumber, update the cached result and leave the instruction alone; do not delete the begin/end markers.
61
- - `w:instrText` is where the instruction lives — a search-and-replace over `w:t` will not find `TOC`.
62
- - Bookmarks referenced by `REF` must exist (`w:bookmarkStart`/`w:bookmarkEnd` with matching `w:id`); a missing bookmark shows "Error! Reference source not found."
60
+ - To renumber, update the cached result and leave the instruction alone; never delete the begin/end markers.
61
+ - The instruction lives in `w:instrText`, so a search-and-replace over `w:t` will not find `TOC`.
62
+ - Bookmarks referenced by `REF` must exist (`w:bookmarkStart` / `w:bookmarkEnd` with matching `w:id`); a missing bookmark shows "Error! Reference source not found.".
63
63
 
64
64
  ## See also
65
65
 
@@ -1,6 +1,6 @@
1
1
  # Relationships and content types
2
2
 
3
- A `.docx` is a zip of parts, and every relationship between parts is declared twice: once as a relationship entry, once as a content type. Miss either and the file either loses the feature or refuses to open.
3
+ A `.docx` is a zip of parts, and every relationship between parts is declared twice: once as a relationship entry, once as a content type. Miss either and the file loses the feature or refuses to open.
4
4
 
5
5
  ## The two registries
6
6
 
@@ -10,7 +10,7 @@ _rels/.rels # package -> document, core props
10
10
  word/_rels/document.xml.rels # document -> styles, numbering, media, header, footer, comments, hyperlinks
11
11
  ```
12
12
 
13
- `[Content_Types].xml` has `<Default Extension="xml" ContentType="..."/>` for extension defaults and `<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>` for parts that do not follow an extension rule.
13
+ `[Content_Types].xml` uses `<Default Extension="xml" ContentType="..."/>` for extension defaults and `<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"/>` for parts that do not follow an extension rule.
14
14
 
15
15
  A relationship:
16
16
 
@@ -20,7 +20,7 @@ A relationship:
20
20
  Target="comments.xml"/>
21
21
  ```
22
22
 
23
- `Id` is scoped to the part that owns the `.rels` file, so `rId5` in the document and `rId5` in a header are unrelated. `Target` is relative to the owner directory unless it is absolute (`/word/media/image1.png`) or external (`TargetMode="External"`, used for hyperlinks).
23
+ `Id` is scoped to the part that owns the `.rels` file, so `rId5` in the document and `rId5` in a header are unrelated. `Target` is relative to the owner directory unless absolute (`/word/media/image1.png`) or external (`TargetMode="External"`, used for hyperlinks).
24
24
 
25
25
  ## What breaks
26
26
 
@@ -32,13 +32,13 @@ A relationship:
32
32
  | Broken link | Relationship removed but `r:id` left in the body |
33
33
  | Lost image | Media copied without a new relationship id |
34
34
 
35
- Ids must be unique within a `.rels` part. When adding one, read the existing ids and take `max+1`; reusing an id silently rewires an unrelated reference.
35
+ Ids are unique within a `.rels` part. Read the existing ids and take `max+1`; reusing an id silently rewires an unrelated reference.
36
36
 
37
37
  ## The parts that exist only when used
38
38
 
39
- `word/comments.xml`, `word/footnotes.xml`, `word/endnotes.xml`, `word/headerN.xml`, `word/footerN.xml`, `word/numbering.xml`, `docProps/custom.xml` — none are required, and each needs three pieces when added: the part, the override, the relationship. Removing one means removing all three (or Word reports corruption).
39
+ `word/comments.xml`, `word/footnotes.xml`, `word/endnotes.xml`, `word/headerN.xml`, `word/footerN.xml`, `word/numbering.xml`, `docProps/custom.xml` — none are required, and each needs three pieces when added: part, override, relationship. Removing one means removing all three, or Word reports corruption.
40
40
 
41
- Headers and footers are referenced from the section properties, not from the document root:
41
+ Headers and footers are referenced from the section properties, not the document root:
42
42
 
43
43
  ```xml
44
44
  <w:sectPr>
@@ -59,4 +59,4 @@ Headers and footers are referenced from the section properties, not from the doc
59
59
 
60
60
  ## Rebuild rather than regenerate
61
61
 
62
- When a package is broken, unzipping it, fixing the two registries and rezipping with `[Content_Types].xml` first (stored, not deflated) usually recovers it. The kit writes that order on save for this reason.
62
+ When a package is broken, unzip it, fix the two registries and rezip with `[Content_Types].xml` first (stored, not deflated). The kit writes that order on save for this reason.
@@ -1,6 +1,6 @@
1
1
  # Tracked changes
2
2
 
3
- Tracked changes are markup, not a diff. The file contains both the old and the new text, wrapped in elements that say which is which, plus enough metadata for Word to show reviewer names and accept or reject each edit.
3
+ Tracked changes are markup, not a diff. The file holds both old and new text, wrapped in elements that say which is which, plus enough metadata for Word to show reviewer names and accept or reject each edit.
4
4
 
5
5
  ## The elements
6
6
 
@@ -13,7 +13,7 @@ Tracked changes are markup, not a diff. The file contains both the old and the n
13
13
  | `w:delText` | The deleted characters — not `w:t` |
14
14
  | `w:delInstrText` | A deleted field instruction |
15
15
 
16
- Each carries `w:id`, `w:author`, `w:date`. Ids must be unique within the document; reusing one makes Word show the wrong reviewer.
16
+ Each carries `w:id`, `w:author` and `w:date`. Ids are unique within the document; reusing one makes Word show the wrong reviewer.
17
17
 
18
18
  A deletion is a `w:del` containing runs with `w:delText`, not a run with a strikethrough:
19
19
 
@@ -28,21 +28,21 @@ A deletion is a `w:del` containing runs with `w:delText`, not a run with a strik
28
28
 
29
29
  ## Tracking has to be on
30
30
 
31
- `<w:trackRevisions/>` in `word/settings.xml` makes *Word* track subsequent edits. It does not affect markup already in the file. Write the markup directly when you are generating the change; turn tracking on when the user will continue editing.
31
+ `<w:trackRevisions/>` in `word/settings.xml` makes *Word* track subsequent edits; it does not affect markup already in the file. Write the markup directly when generating a change; turn tracking on when the user will continue editing.
32
32
 
33
33
  ## Accepting and rejecting
34
34
 
35
- - **Accept** — keep `w:ins` content and unwrap it; delete `w:del` entirely; for moves keep `w:moveTo`.
36
- - **Reject** — delete `w:ins` content; keep `w:del` content and convert `w:delText` back to `w:t` (unwrap the `w:del`).
37
- - Either way, remove the revision containers afterwards; leaving an empty `w:ins` shows a stray marker.
38
- - Format-change elements have no accept/reject choice in the content sense: accepted means the new properties stay, rejected means the old ones are restored.
35
+ - **Accept** — keep `w:ins` content and unwrap it, delete `w:del` entirely, and for moves keep `w:moveTo`.
36
+ - **Reject** — delete `w:ins` content, keep `w:del` content and convert `w:delText` back to `w:t` (unwrap the `w:del`).
37
+ - Either way, remove the revision containers afterwards; an empty `w:ins` shows a stray marker.
38
+ - Format-change elements are not content: accepting keeps the new properties, rejecting restores the old ones.
39
39
 
40
40
  ## Where tracking hides
41
41
 
42
- - Inside headers, footers, footnotes, endnotes, table cells, text boxes.
43
- - Table-level changes: `w:trPr/w:ins` for an inserted row, `w:tcPr/w:cellIns` for a cell.
44
- - Paragraph-level: a paragraph mark inserted or deleted uses `w:rPr/w:ins` in `w:pPr`, so "delete this paragraph" is not a `w:del` around the paragraph.
45
- - Comments are separate markup; a comment can survive its being accepted.
42
+ - Inside headers, footers, footnotes, endnotes, table cells and text boxes.
43
+ - Table level: `w:trPr/w:ins` for an inserted row, `w:tcPr/w:cellIns` for a cell.
44
+ - Paragraph level: an inserted or deleted paragraph mark uses `w:rPr/w:ins` in `w:pPr`, so "delete this paragraph" is not a `w:del` around the paragraph.
45
+ - Comments are separate markup; a comment can survive its text being accepted.
46
46
 
47
47
  ## Reading markup without a renderer
48
48
 
@@ -50,4 +50,4 @@ A deletion is a `w:del` containing runs with `w:delText`, not a run with a strik
50
50
  "<python>" "<skill>/scripts/accept_tracked_changes.py" "<file.docx>" --mode report
51
51
  ```
52
52
 
53
- The report lists insertions and deletions with authors and the text on each side. That is the honest way to answer "what changed" when there is no rendered view, and it is also how you confirm a generated change landed as markup rather than as plain text.
53
+ The report lists insertions and deletions with authors and the text on each side — the honest answer to "what changed" with no rendered view, and the way to confirm a generated change landed as markup rather than as plain text.
@@ -19,9 +19,7 @@ word/comments.xml, footnotes.xml, endnotes.xml optional
19
19
 
20
20
  ## The body
21
21
 
22
- `word/document.xml` is a sequence of block-level elements: `w:p` (paragraph), `w:tbl` (table), `w:sdt` (content control), and finally one `w:sectPr` per section (the last one sits in the body, earlier ones inside a paragraph's `pPr`).
23
-
24
- A paragraph is:
22
+ `word/document.xml` is a sequence of block elements — `w:p` (paragraph), `w:tbl` (table), `w:sdt` (content control) — plus one `w:sectPr` per section: the last sits in the body, earlier ones inside a paragraph's `pPr`.
25
23
 
26
24
  ```xml
27
25
  <w:p>
@@ -39,9 +37,9 @@ A paragraph is:
39
37
  </w:p>
40
38
  ```
41
39
 
42
- Everything a reader sees is a run inside a paragraph; everything that makes it look different from its style is direct formatting in `pPr`/`rPr`. Direct formatting is why `style_normalize.py` exists.
40
+ Everything a reader sees is a run inside a paragraph; everything that makes it differ from its style is direct formatting in `pPr`/`rPr`, which is why `style_normalize.py` exists (`tasks/style_lint_normalize.md`).
43
41
 
44
- A table is `w:tbl` > `w:tblPr` (style, width, layout) + `w:tblGrid` (column widths) + `w:tr` > `w:tc` > `w:p`. A cell always contains at least one paragraph.
42
+ A table is `w:tbl` > `w:tblPr` (style, width, layout) + `w:tblGrid` (column widths) + `w:tr` > `w:tc` > `w:p`. Every cell holds at least one paragraph.
45
43
 
46
44
  ## Units
47
45
 
@@ -52,31 +50,31 @@ A table is `w:tbl` > `w:tblPr` (style, width, layout) + `w:tblGrid` (column widt
52
50
  | eighth-point | border widths (`w:sz` on borders) | 8 per point |
53
51
  | EMU (914400/in) | drawing extents | 914400 per inch |
54
52
 
55
- A Letter page is 12240×15840 twips. Text width = page width − left − right margins (default 9360 twips with 1in margins).
53
+ A Letter page is 12240×15840 twips. Text width = page width − left − right margins (9360 twips at 1in margins).
56
54
 
57
55
  ## Styles
58
56
 
59
- `w:style` with `w:type` of `paragraph`, `character`, `table` or `numbering`. Attributes: `w:styleId` (the internal name), `w:name` (the UI name), `w:basedOn`, `w:next`, `w:link`, plus `w:pPr`/`w:rPr`.
57
+ `w:style` with `w:type` of `paragraph`, `character`, `table` or `numbering`; attributes `w:styleId` (internal name), `w:name` (UI name), `w:basedOn`, `w:next`, `w:link`, plus `w:pPr` / `w:rPr`.
60
58
 
61
- - `Normal` is the root; `Heading1`..`Heading9` drive the outline level and the TOC.
62
- - Outline level can also be set explicitly with `w:outlineLvl` (0-based) in `pPr`; a paragraph can look like a heading without being one, and the TOC will not see it.
59
+ - `Normal` is the root; `Heading1`..`Heading9` drive outline level and the TOC.
60
+ - `w:outlineLvl` (0-based) in `pPr` sets an outline level explicitly: a paragraph can look like a heading without being one, and the TOC will not see it.
63
61
  - Latent styles do not exist in the file until used; a document may reference `Heading3` while `styles.xml` has no definition for it.
64
62
 
65
63
  ## Numbering
66
64
 
67
- `word/numbering.xml` holds `w:abstractNum` (the list definition, per level: format, text, indentation) and `w:num` (`w:numId` → `w:abstractNumId`, optionally overridden). A paragraph joins a list with `w:numPr` referencing a `numId`. Two paragraphs with the same `numId` share a counter.
65
+ `word/numbering.xml` holds `w:abstractNum` (list definition: per level its format, text and indentation) and `w:num` (`w:numId` → `w:abstractNumId`, optionally overridden). A paragraph joins a list with `w:numPr` referencing a `numId`; two paragraphs with the same `numId` share a counter.
68
66
 
69
67
  ## Sections
70
68
 
71
- `w:sectPr` carries page size (`w:pgSz`), margins (`w:pgMar`), orientation (implied by width vs height, or `w:orient`), columns, and the header/footer references. A document with one section has one `sectPr`; a new section is a paragraph whose `pPr` contains the *previous* section's `sectPr`.
69
+ `w:sectPr` carries page size (`w:pgSz`), margins (`w:pgMar`), orientation (implied by width vs height, or `w:orient`), columns and header/footer references. A single-section document has one `sectPr`; a new section is a paragraph whose `pPr` contains the *previous* section's `sectPr`.
72
70
 
73
71
  ## Fields
74
72
 
75
- Simple (`w:fldSimple`) and complex (`fldChar` begin/separate/end with `w:instrText`). The visible text is a cache. `w:updateFields` in `settings.xml` asks the editor to refresh on open. See `ooxml/hyperlinks_and_fields.md`.
73
+ Simple (`w:fldSimple`) and complex (`fldChar` begin/separate/end with `w:instrText`). The visible text is a cache; `w:updateFields` in `settings.xml` asks the editor to refresh on open. See `ooxml/hyperlinks_and_fields.md`.
76
74
 
77
75
  ## Revision markup
78
76
 
79
- `w:ins`, `w:del` (with `w:delText`), `w:moveFrom`/`w:moveTo`, `w:rPrChange`/`w:pPrChange`, each with author and date. See `ooxml/tracked_changes.md`.
77
+ `w:ins`, `w:del` (with `w:delText`), `w:moveFrom` / `w:moveTo`, `w:rPrChange` / `w:pPrChange`, each with author and date. See `ooxml/tracked_changes.md`.
80
78
 
81
79
  ## Minimum valid package
82
80
 
@@ -1,19 +1,17 @@
1
1
  # Eliciting a template
2
2
 
3
- A user who says "use our house style" has a file somewhere. Getting it is worth one question; guessing produces a document that has to be redone.
3
+ A user who says "use our house style" has a file somewhere. One question gets it; guessing produces a document that has to be redone.
4
4
 
5
5
  ## Ask for the artefact, not the description
6
6
 
7
- Description ("blue headings, maybe 11pt") produces a different document every time. The artefact (`house-template.dotx`, an old `.docx` of the same kind, a PDF of the last report) carries the actual styles, margins and fonts.
8
-
9
- One sentence is enough:
7
+ A description ("blue headings, maybe 11pt") produces a different document every time. The artefact (`house-template.dotx`, an old `.docx` of the same kind, a PDF of the last report) carries the actual styles, margins and fonts.
10
8
 
11
9
  > Is there an existing document in this style I should match? Send the file (`.dotx`, or any `.docx` in that style) and I will follow it exactly.
12
10
 
13
- If they have no file, ask for two or three specifics that cannot be guessed and choose the rest from `SKILL.md` defaults:
11
+ With no file, ask for two or three specifics that cannot be guessed and take the rest from `SKILL.md` defaults:
14
12
 
15
- - page size and margins, if their organisation is not US Letter,
16
- - the heading/body typeface and sizes,
13
+ - page size and margins, if the organisation is not US Letter,
14
+ - heading and body typeface and sizes,
17
15
  - anything required by policy (logo in the header, classification mark in the footer).
18
16
 
19
17
  ## Read the template before using it
@@ -24,7 +22,7 @@ If they have no file, ask for two or three specifics that cannot be guessed and
24
22
  "<python>" "<skill>/scripts/section_audit.py" "<template.docx>"
25
23
  ```
26
24
 
27
- From `styles.xml`, note the `styleId` of what will be used: `Heading1`..`Heading3`, `Title`, `Caption`, `Quote`, `ListParagraph`. From the section audit, record page size, margins, header/footer text and whether the first page is different.
25
+ From `styles.xml`, note the `styleId` of what will be used: `Heading1`..`Heading3`, `Title`, `Caption`, `Quote`, `ListParagraph`. From the section audit, record page size, margins, header and footer text, and whether the first page is different.
28
26
 
29
27
  ## Apply it
30
28
 
@@ -32,18 +30,18 @@ From `styles.xml`, note the `styleId` of what will be used: `Heading1`..`Heading
32
30
  "<python>" "<skill>/scripts/apply_template_styles.py" --template "<template.docx>" --target "<draft.docx>" --out "<styled.docx>"
33
31
  ```
34
32
 
35
- This merges style definitions (and numbering) by id — the body content is untouched, so anything written with direct formatting keeps it. Follow with:
33
+ Merges style definitions (and numbering) by id; the body content is untouched, so anything written with direct formatting keeps it. Then:
36
34
 
37
35
  ```bash
38
- "<python>" "<skill>/scripts/style_normalize.py" "<styled.docx>" --out "<styled-final.docx>" --clear_paragraph_format --enforce_heading_spacing
36
+ "<python>" "<skill>/scripts/style_normalize.py" "<styled.docx>" --out "<styled-final.docx" --clear_paragraph_format --enforce_heading_spacing
39
37
  ```
40
38
 
41
- Now the text picks up the template's look instead of the draft's.
39
+ The text now picks up the template's look instead of the draft's.
42
40
 
43
41
  ## When the template is a PDF or an image
44
42
 
45
- Extract structure, not styles: heading hierarchy, section order, table layout, caption convention, margin proportions. Then reproduce them with named styles in a generated document. Say which parts were matched approximately — a PDF does not tell you the font size of a heading, only how it looks.
43
+ Extract structure, not styles: heading hierarchy, section order, table layout, caption convention, margin proportions. Reproduce them with named styles in a generated document. Say which parts were matched approximately — a PDF does not reveal a heading's font size, only how it looks.
46
44
 
47
45
  ## When there is no template and no details
48
46
 
49
- Fall back to the defaults in `SKILL.md` (Letter, 1in margins, 11–12pt body, black headings, `#D9D9D9` table borders) and state the assumption in one line with the delivered file. Do not stall on a question the user may not care about.
47
+ Fall back to the `SKILL.md` defaults (Letter, 1in margins, 11–12pt body, black headings, `#D9D9D9` table borders) and state the assumption in one line with the delivered file. Do not stall on a question the user may not care about.
@@ -1,49 +1,49 @@
1
1
  # Writing quality
2
2
 
3
- Content quality is the part of a document the format cannot fix. These are the rules to apply while drafting, before any rendering.
3
+ Content quality is the part of a document the format cannot fix. Apply these rules while drafting, before any rendering.
4
4
 
5
5
  ## Structure before prose
6
6
 
7
- - Every heading states a claim the reader can disagree with ("Services carried the quarter", not "Services"). A heading that only names a topic is a missed opportunity to be clear.
7
+ - Every heading states a claim the reader can disagree with ("Services carried the quarter", not "Services").
8
8
  - A section that cannot be summarised in one sentence is two sections.
9
- - Three levels of heading is the practical maximum; a fourth usually means the content should be a table or an appendix.
10
- - Nothing between a heading and its first paragraph. No label line, no restated title.
9
+ - Three heading levels is the practical maximum; a fourth means the content wants a table or an appendix.
10
+ - Nothing between a heading and its first paragraph — no label line, no restated title.
11
11
 
12
12
  ## Paragraphs
13
13
 
14
- - One idea per paragraph, first sentence carries it.
15
- - 3–6 sentences is the comfortable range; a 1600-word paragraph is a formatting defect even if the prose is good.
16
- - No em-dash pile-ups, no "furthermore/moreover" chains, no sentence that only announces the next sentence.
17
- - The last paragraph of a section says what it meant, not that it is finished.
14
+ - One idea per paragraph, carried by the first sentence.
15
+ - 3–6 sentences is comfortable; a 1600-word paragraph is a formatting defect even when the prose is good.
16
+ - No em-dash pile-ups, no "furthermore/moreover" chains, no sentence that only announces the next one.
17
+ - The last paragraph of a section says what the section meant, not that it is finished.
18
18
 
19
19
  ## Lists and tables
20
20
 
21
21
  - Bullets for parallel items of equal weight; never for a sequence (that is a numbered list).
22
- - If items have two attributes, a table beats a bulleted list with parentheticals.
23
- - Table headers are nouns, units in the header ("Revenue ($m)"), numbers right-aligned.
22
+ - Items with two attributes belong in a table, not a bulleted list with parentheticals.
23
+ - Table headers are nouns with units ("Revenue ($m)"); numbers right-aligned.
24
24
  - Every table earns its caption; a table without a purpose is a screenshot of data.
25
25
 
26
26
  ## Voice
27
27
 
28
- - Active unless the actor is unknown or unimportant; "We found", not "It was found".
28
+ - Active unless the actor is unknown or unimportant: "We found", not "It was found".
29
29
  - Present tense for findings, past for actions taken.
30
- - No unsupported superlatives. "18% growth" beats "strong growth"; if a number is unknown, say so rather than adding an adverb.
31
- - Define an acronym once, then use it. Never define it twice in different places.
30
+ - No unsupported superlatives: "18% growth" beats "strong growth"; if the number is unknown, say so instead of adding an adverb.
31
+ - Define an acronym once, then use it; never redefine it elsewhere.
32
32
 
33
33
  ## Numbers
34
34
 
35
- - Consistent precision. Do not mix "12.4%" and "8%" in one table unless the source did.
35
+ - Consistent precision: do not mix "12.4%" and "8%" in one table unless the source did.
36
36
  - Percentages below 1 take a leading zero (0.4%, not .4%).
37
- - State the period and the currency in the table or its caption, not in the surrounding prose.
38
- - A number in prose must match the number in the table; rendering does not catch this, re-reading does.
37
+ - State period and currency in the table or its caption, not in surrounding prose.
38
+ - A prose number must match the table number; rendering does not catch this, re-reading does.
39
39
 
40
40
  ## What to cut
41
41
 
42
- - Opening paragraphs that explain that the document will explain something.
43
- - Closing paragraphs that thank the reader.
42
+ - Opening paragraphs explaining that the document will explain something.
43
+ - Closing paragraphs thanking the reader.
44
44
  - Bold on every second phrase: emphasis used everywhere is emphasis used nowhere.
45
- - Sentences that begin "It is important to note that".
45
+ - Sentences beginning "It is important to note that".
46
46
 
47
47
  ## Where this connects
48
48
 
49
- `references/docx-structure.md` covers how the structure is expressed in Word — styles, captions, table layout. `tasks/verify_render.md` covers checking the result on a page. Neither replaces reading the text once with the question "would a busy reader get the point from the headings alone?"
49
+ `references/docx-structure.md` covers how structure is expressed in Word — styles, captions, table layout. `tasks/verify_render.md` covers checking the result on a page. Neither replaces reading the text once asking whether a busy reader would get the point from the headings alone.
@@ -137,9 +137,10 @@ def main(argv: list[str] | None = None) -> int:
137
137
  print("error: %s" % exc, file=sys.stderr)
138
138
  return 2
139
139
  hits = 0
140
- for paragraph in doc.paragraphs():
141
- if tracked_replace(paragraph, old, new, args.author, stamp, counter):
142
- hits += 1
140
+ for part in doc.text_parts():
141
+ for paragraph in doc.paragraphs(part):
142
+ if tracked_replace(paragraph, old, new, args.author, stamp, counter):
143
+ hits += 1
143
144
  applied.append({"old": old, "new": new, "paragraphs": hits})
144
145
 
145
146
  settings = doc.xml("word/settings.xml")