dsh-office-kit 0.1.0 → 0.3.0
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.
- package/CAPABILITY.md +147 -68
- package/LICENSE.md +21 -21
- package/README.md +108 -106
- package/assets/skills/excel-live-control/SKILL.md +234 -0
- package/assets/skills/excel-live-control/container_tools/live_excel.py +1589 -0
- package/assets/skills/excel-live-control/domain_guidance/financial_models.md +86 -0
- package/assets/skills/excel-live-control/domain_guidance/healthcare.md +58 -0
- package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +53 -0
- package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +53 -0
- package/assets/skills/excel-live-control/references/office-automation.md +207 -0
- package/assets/skills/excel-live-control/references/read_only_qna.md +59 -0
- package/assets/skills/excel-live-control/style_guidelines.md +87 -0
- package/assets/skills/excel-live-control/workflows/create_workflows.md +78 -0
- package/assets/skills/excel-live-control/workflows/edit_workflows.md +72 -0
- package/assets/skills/office-documents/SKILL.md +90 -77
- package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +86 -0
- package/assets/skills/office-documents/ooxml/comments.md +40 -0
- package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +66 -0
- package/assets/skills/office-documents/ooxml/rels_and_content_types.md +62 -0
- package/assets/skills/office-documents/ooxml/tracked_changes.md +53 -0
- package/assets/skills/office-documents/references/docx-structure.md +83 -0
- package/assets/skills/office-documents/references/template-elicitation.md +49 -0
- package/assets/skills/office-documents/references/writing-quality.md +49 -0
- package/assets/skills/office-documents/render_docx.py +92 -0
- package/assets/skills/office-documents/scripts/a11y_audit.py +194 -0
- package/assets/skills/office-documents/scripts/accept_tracked_changes.py +151 -0
- package/assets/skills/office-documents/scripts/add_tracked_replacements.py +148 -0
- package/assets/skills/office-documents/scripts/append_blocks.py +108 -0
- package/assets/skills/office-documents/scripts/apply_template_styles.py +104 -0
- package/assets/skills/office-documents/scripts/captions_and_crossrefs.py +102 -0
- package/assets/skills/office-documents/scripts/comments_add.py +136 -0
- package/assets/skills/office-documents/scripts/comments_apply_patch.py +114 -0
- package/assets/skills/office-documents/scripts/comments_extract.py +85 -0
- package/assets/skills/office-documents/scripts/comments_strip.py +74 -0
- package/assets/skills/office-documents/scripts/content_controls.py +192 -0
- package/assets/skills/office-documents/scripts/core_properties.py +124 -0
- package/assets/skills/office-documents/scripts/docx_kit.py +587 -0
- package/assets/skills/office-documents/scripts/docx_ooxml_patch.py +225 -0
- package/assets/skills/office-documents/scripts/docx_table_to_csv.py +99 -0
- package/assets/skills/office-documents/scripts/fields_materialize.py +66 -0
- package/assets/skills/office-documents/scripts/fields_report.py +59 -0
- package/assets/skills/office-documents/scripts/flatten_ref_fields.py +62 -0
- package/assets/skills/office-documents/scripts/footnotes_report.py +77 -0
- package/assets/skills/office-documents/scripts/google_docs_title_sanitize.py +96 -0
- package/assets/skills/office-documents/scripts/heading_audit.py +81 -0
- package/assets/skills/office-documents/scripts/images_audit.py +80 -0
- package/assets/skills/office-documents/scripts/insert_note.py +126 -0
- package/assets/skills/office-documents/scripts/insert_ref_fields.py +90 -0
- package/assets/skills/office-documents/scripts/insert_toc.py +84 -0
- package/assets/skills/office-documents/scripts/internal_nav.py +146 -0
- package/assets/skills/office-documents/scripts/make_fixtures.py +169 -0
- package/assets/skills/office-documents/scripts/merge_docx_append.py +239 -0
- package/assets/skills/office-documents/scripts/privacy_scrub.py +131 -0
- package/assets/skills/office-documents/scripts/redact_docx.py +113 -0
- package/assets/skills/office-documents/scripts/render_and_diff.py +101 -0
- package/assets/skills/office-documents/scripts/replace_text.py +154 -0
- package/assets/skills/office-documents/scripts/section_audit.py +91 -0
- package/assets/skills/office-documents/scripts/set_protection.py +92 -0
- package/assets/skills/office-documents/scripts/style_lint.py +111 -0
- package/assets/skills/office-documents/scripts/style_normalize.py +103 -0
- package/assets/skills/office-documents/scripts/table_geometry.py +167 -0
- package/assets/skills/office-documents/scripts/watermark_add.py +48 -0
- package/assets/skills/office-documents/scripts/watermark_audit_remove.py +67 -0
- package/assets/skills/office-documents/scripts/xlsx_to_docx_table.py +117 -0
- package/assets/skills/office-documents/tasks/accessibility_a11y.md +45 -0
- package/assets/skills/office-documents/tasks/captions_crossrefs.md +49 -0
- package/assets/skills/office-documents/tasks/clean_tracked_changes.md +49 -0
- package/assets/skills/office-documents/tasks/comments_manage.md +59 -0
- package/assets/skills/office-documents/tasks/compare_diff.md +40 -0
- package/assets/skills/office-documents/tasks/create_edit.md +107 -0
- package/assets/skills/office-documents/tasks/equations.md +59 -0
- package/assets/skills/office-documents/tasks/fields_update.md +44 -0
- package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +50 -0
- package/assets/skills/office-documents/tasks/footnotes_endnotes.md +38 -0
- package/assets/skills/office-documents/tasks/forms_content_controls.md +48 -0
- package/assets/skills/office-documents/tasks/headings_numbering.md +55 -0
- package/assets/skills/office-documents/tasks/images_figures.md +63 -0
- package/assets/skills/office-documents/tasks/multi_doc_merge.md +43 -0
- package/assets/skills/office-documents/tasks/navigation_internal_links.md +38 -0
- package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +45 -0
- package/assets/skills/office-documents/tasks/protection_restrict_editing.md +41 -0
- package/assets/skills/office-documents/tasks/read_review.md +50 -0
- package/assets/skills/office-documents/tasks/redaction_anonymization.md +42 -0
- package/assets/skills/office-documents/tasks/sections_layout.md +54 -0
- package/assets/skills/office-documents/tasks/style_lint_normalize.md +44 -0
- package/assets/skills/office-documents/tasks/tables_spreadsheets.md +64 -0
- package/assets/skills/office-documents/tasks/templates_style_packs.md +53 -0
- package/assets/skills/office-documents/tasks/toc_workflow.md +39 -0
- package/assets/skills/office-documents/tasks/verify_render.md +50 -0
- package/assets/skills/office-documents/tasks/watermarks_background.md +35 -0
- package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +47 -0
- package/assets/skills/office-documents/troubleshooting/run_splitting.md +36 -0
- package/assets/skills/office-pdf/SKILL.md +73 -56
- package/assets/skills/office-pdf/references/form-fields.md +56 -0
- package/assets/skills/office-pdf/references/geometry.md +44 -0
- package/assets/skills/office-pdf/references/layout-quality.md +41 -0
- package/assets/skills/office-pdf/references/limits.md +58 -0
- package/assets/skills/office-pdf/scripts/extract_text.py +172 -0
- package/assets/skills/office-pdf/scripts/forms.py +556 -0
- package/assets/skills/office-pdf/scripts/metadata.py +161 -0
- package/assets/skills/office-pdf/scripts/page_ops.py +198 -0
- package/assets/skills/office-pdf/scripts/redact_region.py +347 -0
- package/assets/skills/office-pdf/scripts/render_pages.py +207 -0
- package/assets/skills/office-pdf/scripts/split_pages.py +155 -0
- package/assets/skills/office-pdf/scripts/stamp_overlay.py +199 -0
- package/assets/skills/office-pdf/tasks/annotations.md +98 -0
- package/assets/skills/office-pdf/tasks/create.md +75 -0
- package/assets/skills/office-pdf/tasks/edit-pages.md +71 -0
- package/assets/skills/office-pdf/tasks/forms.md +67 -0
- package/assets/skills/office-pdf/tasks/read.md +62 -0
- package/assets/skills/office-pdf/tasks/redact.md +72 -0
- package/assets/skills/office-pdf/tasks/stamp.md +60 -0
- package/assets/skills/office-presentations/SKILL.md +141 -87
- package/assets/skills/office-presentations/container_tools/add_slides.py +136 -0
- package/assets/skills/office-presentations/container_tools/core_properties.py +120 -0
- package/assets/skills/office-presentations/container_tools/create_montage.py +126 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_layout_geometry.py +285 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_package_integrity.py +157 -0
- package/assets/skills/office-presentations/container_tools/native_chart_title_gate.py +144 -0
- package/assets/skills/office-presentations/container_tools/native_quantitative_chart_gate.py +199 -0
- package/assets/skills/office-presentations/container_tools/native_table_arithmetic_gate.py +212 -0
- package/assets/skills/office-presentations/container_tools/ooxml_helpers.py +345 -0
- package/assets/skills/office-presentations/container_tools/preview_render.py +375 -0
- package/assets/skills/office-presentations/container_tools/render_slides.py +139 -0
- package/assets/skills/office-presentations/container_tools/replace_text.py +168 -0
- package/assets/skills/office-presentations/container_tools/template_source_fidelity_gate.py +229 -0
- package/assets/skills/office-presentations/domain_guidance/finance.md +66 -0
- package/assets/skills/office-presentations/recipes/README.md +71 -0
- package/assets/skills/office-presentations/recipes/autolayout.md +88 -0
- package/assets/skills/office-presentations/recipes/connectors.md +83 -0
- package/assets/skills/office-presentations/recipes/images.md +80 -0
- package/assets/skills/office-presentations/recipes/master-and-layouts.md +127 -0
- package/assets/skills/office-presentations/recipes/native-charts.md +87 -0
- package/assets/skills/office-presentations/recipes/rich-text-runs.md +86 -0
- package/assets/skills/office-presentations/recipes/speaker-notes.md +65 -0
- package/assets/skills/office-presentations/recipes/tables.md +88 -0
- package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +71 -0
- package/assets/skills/office-presentations/recipes/theme-tokens.md +87 -0
- package/assets/skills/office-presentations/references/authoring-workflow.md +65 -0
- package/assets/skills/office-presentations/references/finalization.md +63 -0
- package/assets/skills/office-presentations/references/native-bullets.md +78 -0
- package/assets/skills/office-presentations/references/native-evidence.md +67 -0
- package/assets/skills/office-presentations/references/style-guidelines.md +66 -0
- package/assets/skills/office-presentations/references/templates.md +76 -0
- package/assets/skills/office-presentations/routing/google_slides.md +45 -0
- package/assets/skills/office-presentations/tasks/apply-annotations.md +83 -0
- package/assets/skills/office-presentations/tasks/charts-tables-notes.md +79 -0
- package/assets/skills/office-presentations/tasks/create-deck.md +106 -0
- package/assets/skills/office-presentations/tasks/edit-deck.md +93 -0
- package/assets/skills/office-presentations/tasks/inspect-and-verify.md +107 -0
- package/assets/skills/office-presentations/tasks/template-following.md +132 -0
- package/assets/skills/office-presentations/template_following_scripts/check_template_fidelity.py +176 -0
- package/assets/skills/office-presentations/template_following_scripts/inspect_template_deck.py +154 -0
- package/assets/skills/office-presentations/template_following_scripts/make_contact_sheet.py +111 -0
- package/assets/skills/office-presentations/template_following_scripts/prepare_template_starter_deck.py +239 -0
- package/assets/skills/office-presentations/template_following_scripts/validate_template_plan.py +185 -0
- package/assets/skills/office-spreadsheets/SKILL.md +105 -101
- package/assets/skills/office-spreadsheets/container_tools/add_chart.py +236 -0
- package/assets/skills/office-spreadsheets/container_tools/add_image.py +107 -0
- package/assets/skills/office-spreadsheets/container_tools/append_rows.py +122 -0
- package/assets/skills/office-spreadsheets/container_tools/diff_workbooks.py +306 -0
- package/assets/skills/office-spreadsheets/container_tools/inspect_workbook.py +398 -0
- package/assets/skills/office-spreadsheets/container_tools/recalc_gate.py +275 -0
- package/assets/skills/office-spreadsheets/container_tools/set_cells.py +135 -0
- package/assets/skills/office-spreadsheets/container_tools/sheet_ops.py +134 -0
- package/assets/skills/office-spreadsheets/container_tools/style_cells.py +133 -0
- package/assets/skills/office-spreadsheets/recipes/charts.md +66 -0
- package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +69 -0
- package/assets/skills/office-spreadsheets/recipes/data-tables.md +58 -0
- package/assets/skills/office-spreadsheets/recipes/named-ranges.md +41 -0
- package/assets/skills/office-spreadsheets/recipes/number-formats.md +72 -0
- package/assets/skills/office-spreadsheets/recipes/sparklines.md +43 -0
- package/assets/skills/office-spreadsheets/references/charts.md +63 -0
- package/assets/skills/office-spreadsheets/references/domain/financial-models.md +40 -0
- package/assets/skills/office-spreadsheets/references/domain/healthcare.md +22 -0
- package/assets/skills/office-spreadsheets/references/domain/marketing.md +28 -0
- package/assets/skills/office-spreadsheets/references/domain/research.md +22 -0
- package/assets/skills/office-spreadsheets/references/formula-patterns.md +78 -0
- package/assets/skills/office-spreadsheets/references/style-guidelines.md +69 -0
- package/assets/skills/office-spreadsheets/references/workbook-structure.md +60 -0
- package/assets/skills/office-spreadsheets/routing/google-sheets.md +36 -0
- package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +37 -0
- package/assets/skills/office-spreadsheets/tasks/annotations.md +51 -0
- package/assets/skills/office-spreadsheets/tasks/create-workbook.md +66 -0
- package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +38 -0
- package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +69 -0
- package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +38 -0
- package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +57 -0
- package/assets/skills/shared/annotation-recipes.md +252 -0
- package/assets/skills/shared/check_runtime.py +253 -0
- package/assets/skills/shared/common-workflow.md +139 -0
- package/assets/skills/shared/scripts/ensure_raster_image.py +187 -0
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/index.js +239 -89
- package/package.json +146 -149
- package/postinstall.mjs +92 -82
- package/python/office_cli.py +171 -246
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Read and review a file
|
|
2
|
+
|
|
3
|
+
**Goal.** Understand an existing document before touching it: what it contains, how it is structured, and what is wrong with it.
|
|
4
|
+
|
|
5
|
+
## Read the content
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
"<python>" "<cli>" docx read --input "<file.docx>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Returns the title, author, paragraphs with their styles, headings, tables, image and equation counts, and page dimensions. Use it as the first look, including for files you produced earlier in the session.
|
|
12
|
+
|
|
13
|
+
## Read the structure
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
"<python>" "<skill>/scripts/docx_kit.py" parts '<file.docx>' # every package part and its size
|
|
17
|
+
"<python>" "<skill>/scripts/docx_kit.py" text '<file.docx>' # paragraphs with style and outline level
|
|
18
|
+
"<python>" "<skill>/scripts/docx_kit.py" xml '<file.docx>' word/document.xml
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`parts` tells you which features exist at all (a `comments.xml` means the file carries comments; its absence means none, even if the user says otherwise). `text` is faster than reading raw XML when you only need the words and their heading levels.
|
|
22
|
+
|
|
23
|
+
## Audit what is there
|
|
24
|
+
|
|
25
|
+
| Question | Command |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Are headings consistent and styled? | `heading_audit.py` |
|
|
28
|
+
| Is the prose styled directly instead of by style? | `style_lint.py` |
|
|
29
|
+
| Are images described, sized sensibly? | `images_audit.py` |
|
|
30
|
+
| Is the page setup sane? | `section_audit.py` |
|
|
31
|
+
| Are there fields, TOC entries, cross-references? | `fields_report.py` |
|
|
32
|
+
| Are there footnotes or endnotes? | `footnotes_report.py` |
|
|
33
|
+
| What comments exist, and on what text? | `comments_extract.py` |
|
|
34
|
+
|
|
35
|
+
Every audit script prints JSON and exits non-zero when it found something, so the exit code alone tells you whether the document needs attention. Read the `findings` array rather than the exit code when you need the reason.
|
|
36
|
+
|
|
37
|
+
## Review for the user
|
|
38
|
+
|
|
39
|
+
When the job is "review this document" rather than "change it":
|
|
40
|
+
|
|
41
|
+
1. Read the text, not just the audits. Point out the argument, not only the formatting.
|
|
42
|
+
2. Separate defects (broken table, skipped heading level, missing alt text) from style preferences.
|
|
43
|
+
3. Cite locations by section heading and paragraph text — Word has no stable page numbers.
|
|
44
|
+
4. Offer the fix as a concrete next action, with the command that performs it.
|
|
45
|
+
|
|
46
|
+
## Pitfalls
|
|
47
|
+
|
|
48
|
+
- `docx read` shows the body only; headers, footers, footnotes and comments need `docx_kit.py xml` or the dedicated report scripts.
|
|
49
|
+
- Tracked changes are visible as plain text in a read; use `accept_tracked_changes.py --mode report` to see what is proposed.
|
|
50
|
+
- A field's displayed text is a cached result. `fields_report.py` shows the instruction, which is the truth.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Redaction and anonymisation
|
|
2
|
+
|
|
3
|
+
**Goal.** Remove personal data — addresses, phone numbers, names, account references — from a document before it leaves the organisation, in the text and in the places text hides.
|
|
4
|
+
|
|
5
|
+
Text lives in `w:t` (and `w:delText` for deletions, `w:instrText` for field instructions). A redaction that only rewrites paragraphs via `python-docx` misses headers, footers, footnotes and comments.
|
|
6
|
+
|
|
7
|
+
## Redact
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<skill>/scripts/redact_docx.py" "<file.docx>" "<file-redacted.docx>" --emails --phones
|
|
11
|
+
"<python>" "<skill>/scripts/redact_docx.py" "<file.docx>" "<file-redacted.docx>" --pattern "ACCT-\d{6}" --replacement "<account>"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- Default run covers emails and phone numbers; add `--pattern` (repeatable) for account numbers, case ids, postcodes.
|
|
15
|
+
- `--mask_char *` keeps the shape of the value visible (`***@***.com` style), `--replacement` swaps in a token, `--no_preserve_length` stops padding to the original length.
|
|
16
|
+
- `--include_comments` also rewrites `word/comments.xml`; without it, comments keep the original text.
|
|
17
|
+
- Applied to body, every header and footer, footnotes and endnotes.
|
|
18
|
+
|
|
19
|
+
Matching happens per text run, because merging runs would destroy the surrounding formatting. A value split across runs (bolded digits inside a phone number) is not found — an acceptable trade only if you check the result; when the file mixes formatting inside the sensitive value, flatten that paragraph first (`tasks/style_lint_normalize.md`).
|
|
20
|
+
|
|
21
|
+
## Redaction versus anonymisation
|
|
22
|
+
|
|
23
|
+
Redaction removes the value; anonymisation replaces it consistently. If the same account number appears four times and the argument depends on it matching, replace all occurrences with the same token rather than `[redacted]` every time — `--replacement "<account-A>"` per pattern does that.
|
|
24
|
+
|
|
25
|
+
## Check the whole file
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
"<python>" "<skill>/scripts/docx_kit.py" text '<file-redacted.docx>' --part word/footnotes.xml
|
|
29
|
+
"<python>" "<skill>/scripts/docx_kit.py" xml '<file-redacted.docx>' word/document.xml | grep -c "example.com"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
A grep over dumped XML is the honest check: run it for each sensitive term, across the document, header, footer, footnote and comment parts.
|
|
33
|
+
|
|
34
|
+
## Verify
|
|
35
|
+
|
|
36
|
+
Render, then read the pages the data appeared on. A redaction that changed the line length enough to shift the layout is worth a look; a redaction that left a `<w:t>` intact shows up in the grep, not the page.
|
|
37
|
+
|
|
38
|
+
## Pitfalls
|
|
39
|
+
|
|
40
|
+
- Field results and field instructions can both contain data; the script rewrites `w:t` and `w:delText` only.
|
|
41
|
+
- Images are not scanned — a screenshot of a spreadsheet is not redacted by any text pass. Say so plainly.
|
|
42
|
+
- Charts store category labels in `word/charts/*.xml`; inspect those parts when a chart carried the sensitive data.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Sections and page layout
|
|
2
|
+
|
|
3
|
+
**Goal.** Page size, orientation, margins, columns, headers and footers that are deliberate at every point in the document, including after a mid-document change.
|
|
4
|
+
|
|
5
|
+
Page setup lives in a `w:sectPr` element. The last one sits at the end of the body; earlier ones sit inside the last paragraph of the section they end (`w:pPr/w:sectPr`). That is why adding a landscape page means adding a section break before and after, not changing one global setting.
|
|
6
|
+
|
|
7
|
+
## Audit
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<skill>/scripts/section_audit.py" "<file.docx>"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Per section: width and height in inches, orientation derived from them, every margin, column count, section start type, header and footer parts referenced, `titlePg` (different first page) and any explicit page-number start.
|
|
14
|
+
|
|
15
|
+
Findings worth fixing: `mixed_page_sizes` (sections that disagree, usually a template artefact), `narrow_text_column` (left plus right margin eats most of the width), `portrait_wider_than_letter`.
|
|
16
|
+
|
|
17
|
+
## Common changes
|
|
18
|
+
|
|
19
|
+
| Wanted | Route |
|
|
20
|
+
|---|---|
|
|
21
|
+
| New document defaults | `docx create` spec: `page_size`, `orientation`, `top_margin`/`bottom_margin`/`left_margin`/`right_margin` in inches |
|
|
22
|
+
| A landscape page inside a portrait document | two section breaks with a landscape section between them |
|
|
23
|
+
| Different first page header | set `titlePg` on the section and fill `first_page_header` |
|
|
24
|
+
| Continuous section change (e.g. narrower margins) | `w:type val="continuous"` on the new section |
|
|
25
|
+
|
|
26
|
+
Landscape and columns are section properties, so a snippet is often faster than a flag:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
"<python>" - <<'PY'
|
|
30
|
+
from docx import Document
|
|
31
|
+
from docx.enum.section import WD_ORIENT, WD_SECTION
|
|
32
|
+
d = Document(r"<file.docx>")
|
|
33
|
+
s = d.add_section(WD_SECTION.NEW_PAGE)
|
|
34
|
+
s.orientation = WD_ORIENT.LANDSCAPE
|
|
35
|
+
s.page_width, s.page_height = s.page_height, s.page_width
|
|
36
|
+
d.save(r"<file-edited.docx>")
|
|
37
|
+
PY
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Swap the width and height explicitly: setting the orientation alone does not swap them.
|
|
41
|
+
|
|
42
|
+
## Headers and footers
|
|
43
|
+
|
|
44
|
+
A header is `word/headerN.xml` referenced from `w:headerReference`. Headers inherit from the previous section unless the section sets `headerReference` itself. Page numbers go in the footer as a `PAGE` field, page counts as `NUMPAGES`; `docx_ooxml_patch.py --add-page-numbers` writes one.
|
|
45
|
+
|
|
46
|
+
## Verify
|
|
47
|
+
|
|
48
|
+
Render and flip through every page, including the ones after a section break: header content, page numbers, orientation, margins. A section break that leaves an empty page is the classic defect — check for a stray empty paragraph carrying the break.
|
|
49
|
+
|
|
50
|
+
## Pitfalls
|
|
51
|
+
|
|
52
|
+
- `w:pgSz` is width then height; a portrait page taller than wide is normal, a "landscape" 11×8.5 that still says portrait in the panel means only the size changed.
|
|
53
|
+
- `titlePg` without filling the first-page header leaves page one with a blank header, not with the normal one.
|
|
54
|
+
- Deleting a section break merges two sections and keeps the earlier section's settings; edit before deleting.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Styles: lint and normalize
|
|
2
|
+
|
|
3
|
+
**Goal.** Formatting that comes from styles, not from pressing bold on each run, so the document can be restyled, exported and understood later.
|
|
4
|
+
|
|
5
|
+
A paragraph carries a `pStyle` reference; a run carries an `rStyle` plus direct formatting (`w:b`, `w:i`, `w:sz`, `w:color`). Direct formatting wins over the style, which is why a "make all body text 11pt" request fails on the runs someone bolded and resized by hand.
|
|
6
|
+
|
|
7
|
+
## Lint
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<skill>/scripts/style_lint.py" "<file.docx>" --json "<report.json>"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Findings: undefined style references, headings carrying their own paragraph or run formatting, empty runs, manual bullets (`•` typed into text instead of a list style), double spaces, trailing spaces, body sizes that appear once or twice against the dominant size, tables without a style.
|
|
14
|
+
|
|
15
|
+
Exit code 1 means findings; the JSON has the counts and examples, which is what you want when the number is large.
|
|
16
|
+
|
|
17
|
+
## Normalize
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
"<python>" "<skill>/scripts/style_normalize.py" "<file.docx>" --out "<file-clean.docx>" \
|
|
21
|
+
--clear_paragraph_format --enforce_heading_spacing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `--clear_paragraph_format` strips everything from each run's `rPr` except `rStyle`, `rtl` and `lang`, and drops the paragraph-mark run properties. The style then controls the look.
|
|
25
|
+
- `--enforce_heading_spacing` sets space-before/space-after per heading level and `keepNext`, so a heading cannot sit alone at the bottom of a page.
|
|
26
|
+
|
|
27
|
+
Run this before applying a template; a template cannot win against direct formatting.
|
|
28
|
+
|
|
29
|
+
## What "correct" looks like
|
|
30
|
+
|
|
31
|
+
- Body text 11–12pt from `Normal`; headings from `Heading 1`–`Heading 3` with no direct bold or size.
|
|
32
|
+
- Bold used for emphasis inside a sentence, via a character style or a single `w:b` run — not for whole paragraphs that are really headings.
|
|
33
|
+
- Lists from `List Bullet` / `List Number`, never typed bullet characters.
|
|
34
|
+
- One table style per document, usually `Table Grid`.
|
|
35
|
+
|
|
36
|
+
## Verify
|
|
37
|
+
|
|
38
|
+
Re-run `style_lint.py` (counts should drop), then render: clearing direct formatting can change the visual hierarchy when a document faked emphasis with size. Compare the rendered page with the original before you claim the clean-up is neutral.
|
|
39
|
+
|
|
40
|
+
## Pitfalls
|
|
41
|
+
|
|
42
|
+
- Clearing `rPr` removes field-result formatting too; re-run the field-related scripts afterwards if the number must be bold.
|
|
43
|
+
- A style referenced but not defined falls back to `Normal` silently — the lint is the only warning you get.
|
|
44
|
+
- `w:rsid` and proofing attributes are noise, not style; `privacy_scrub.py` removes them when the file leaves the organisation.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Tables and spreadsheets
|
|
2
|
+
|
|
3
|
+
**Goal.** A table that fits the page, repeats its header row across pages, and survives an edit without cells drifting out of alignment.
|
|
4
|
+
|
|
5
|
+
A table's geometry is three things that must agree: the grid (`w:tblGrid`, one `w:gridCol` per column), the table width (`w:tblW`), and each cell width (`w:tcW`). When they disagree Word shows a table wider than the text block or columns that jump on the first edit.
|
|
6
|
+
|
|
7
|
+
## Create
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<cli>" docx create --spec "<spec.json>" --out "<out.docx>"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"type": "table",
|
|
16
|
+
"headers": ["Region", "Revenue", "Growth"],
|
|
17
|
+
"rows": [["EMEA", "4.1M", "12%"], ["APAC", "3.2M", "9%"]],
|
|
18
|
+
"style": "Table Grid"
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Header cells are bold. For a document with the header repeating on every page, follow up with the a11y fix, which writes `w:tblHeader` on row 1:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
"<python>" "<skill>/scripts/a11y_audit.py" "<out.docx>" --fix_table_headers first_row --out "<out-fixed.docx>"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Inspect and fix geometry
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
"<python>" "<skill>/scripts/table_geometry.py" "<file.docx>"
|
|
32
|
+
"<python>" "<skill>/scripts/table_geometry.py" "<file.docx>" --fix --out "<file-fixed.docx>"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The audit reports `tblW`, layout mode, every grid column in twips and the sum, plus the narrowest and widest cell. `--fix` writes a fixed-layout table at the text width (page width minus margins), rescales the grid proportionally and rewrites each `tcW` including `gridSpan`.
|
|
36
|
+
|
|
37
|
+
Text width on Letter with 1in margins is 9360 twips — that is the number a full-width table should total.
|
|
38
|
+
|
|
39
|
+
## Convert
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
"<python>" "<skill>/scripts/docx_table_to_csv.py" "<file.docx>" --table_index 0 --out "<table.csv>"
|
|
43
|
+
"<python>" "<skill>/scripts/xlsx_to_docx_table.py" "<book.xlsx>" --sheet Revenue --out "<table.docx>" --title "Revenue by region"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The first flattens a Word table (expanding horizontal spans, repeating vertically merged values) for data work. The second imports a worksheet as a Word table with a bold header row and `w:tblHeader`, which is the route for "put this spreadsheet in the report" — not a screenshot and not an embedded object.
|
|
47
|
+
|
|
48
|
+
## Design
|
|
49
|
+
|
|
50
|
+
- Bold header row, light grey `#D9D9D9` borders, no shading inside the body.
|
|
51
|
+
- Numeric columns right-aligned; text left-aligned. Thousands separators in the cells themselves.
|
|
52
|
+
- Two or three columns of prose at most; a wide table wants landscape, and that means a section break (`tasks/sections_layout.md`), not a smaller font.
|
|
53
|
+
- Caption above the table (`tasks/captions_crossrefs.md`), and keep it with the table.
|
|
54
|
+
- Avoid vertical merges: they look fine in the file and confuse every downstream reader.
|
|
55
|
+
|
|
56
|
+
## Verify
|
|
57
|
+
|
|
58
|
+
Render and look at the page the table lands on: nothing spills past the right margin, header row intact, no split row across pages, and the last column not truncated. A table that fits in the XML but overflows the page is a real defect, and only the render shows it.
|
|
59
|
+
|
|
60
|
+
## Pitfalls
|
|
61
|
+
|
|
62
|
+
- A table inside a table cell inherits the cell width; check the outer table first.
|
|
63
|
+
- `autofit` is a request, not a guarantee — fixed layout plus explicit widths is the only thing that renders the same everywhere.
|
|
64
|
+
- Cell margins default to 108 twips left/right; that is padding, not missing width.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Templates and style packs
|
|
2
|
+
|
|
3
|
+
**Goal.** Make a new or existing document look like the house template, without copying the template's content into it.
|
|
4
|
+
|
|
5
|
+
A `.dotx` is a `.docx` whose package is a template: `styles.xml` defines the look, `numbering.xml` defines lists, and the body is empty. Copying the body would import placeholder text; what you actually need is the styles part, the numbering definitions they reference, and the theme.
|
|
6
|
+
|
|
7
|
+
## Ask before guessing
|
|
8
|
+
|
|
9
|
+
Do not invent a house style. Get from the user: heading fonts and sizes, body font and size, accent colour, margins, table style, and whether a logo or cover page is required. `references/template-elicitation.md` has the question set that produces a spec.
|
|
10
|
+
|
|
11
|
+
## Apply a template
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
"<python>" "<skill>/scripts/apply_template_styles.py" --template "<house.dotx>" --target "<draft.docx>" --out "<draft-styled.docx>"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The script merges the template's style definitions by `styleId` (replacing same-id definitions, appending missing ones, taking the template's `docDefaults`), and copies `numbering.xml` with its relationship and content-type override. The target's body is untouched, so the content survives and the look changes.
|
|
18
|
+
|
|
19
|
+
Then normalize so direct formatting does not override the template:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
"<python>" "<skill>/scripts/style_normalize.py" "<draft-styled.docx>" --out "<draft-final.docx>" --clear_paragraph_format
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Restyle without a template
|
|
26
|
+
|
|
27
|
+
When the user describes the style instead of supplying a file, set it on the styles rather than on runs:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
"<python>" - <<'PY'
|
|
31
|
+
from docx import Document
|
|
32
|
+
from docx.shared import Pt, RGBColor
|
|
33
|
+
d = Document(r"<draft.docx>")
|
|
34
|
+
normal = d.styles["Normal"]
|
|
35
|
+
normal.font.name = "Calibri"
|
|
36
|
+
normal.font.size = Pt(11)
|
|
37
|
+
for level, size in ((1, 18), (2, 14), (3, 12)):
|
|
38
|
+
style = d.styles[f"Heading {level}"]
|
|
39
|
+
style.font.size = Pt(size)
|
|
40
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
41
|
+
d.save(r"<draft-styled.docx>")
|
|
42
|
+
PY
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Verify
|
|
46
|
+
|
|
47
|
+
Render and compare against the template's own preview page by page: heading sizes, body leading, margins, table borders. A style page that looks right in a style list but wrong on the page usually means a direct-formatting run left over — run the lint.
|
|
48
|
+
|
|
49
|
+
## Pitfalls
|
|
50
|
+
|
|
51
|
+
- A template applied over content that references missing styles produces the fallback look, not an error; run `style_lint.py` to find undefined references.
|
|
52
|
+
- Themes (`word/theme/theme1.xml`) carry fonts and accent colours; a style that names a theme font changes when the theme does.
|
|
53
|
+
- Keeping the template's `numbering.xml` without its `abstractNum` ids produces lists that all share one counter.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Table of contents
|
|
2
|
+
|
|
3
|
+
**Goal.** Put a navigable contents list at the front of the document, built from heading styles rather than typed by hand.
|
|
4
|
+
|
|
5
|
+
The TOC is a `TOC` field. Word fills it in when it recalculates; the file stores the last result. A hand-typed list looks identical in a screenshot and then goes wrong on the first edit.
|
|
6
|
+
|
|
7
|
+
## Insert
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<skill>/scripts/insert_toc.py" "<file.docx>" --out "<file-toc.docx>" --levels 1-3
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The script looks for a placeholder paragraph containing `[[TOC]]` (override with `--placeholder`). If there is none it inserts the field at the start of the body. It writes `TOC \o "1-3" \h \z \u`, sets `w:updateFields`, and adds a short cached hint so the page is not blank before recalculation.
|
|
14
|
+
|
|
15
|
+
Check that every heading you want in the list uses a heading style: `heading_audit.py` finds headings formatted by hand, which the TOC will silently skip.
|
|
16
|
+
|
|
17
|
+
## Rebuild after edits
|
|
18
|
+
|
|
19
|
+
If headings changed, the cached list is stale. Either ask the user to update the field in Word (right-click → Update Field), or regenerate: strip the old field with `fields_materialize.py`? — no, that freezes it. Instead delete the field paragraphs and run `insert_toc.py` again. Freezing a TOC is almost never what a reader wants.
|
|
20
|
+
|
|
21
|
+
## Static contents list
|
|
22
|
+
|
|
23
|
+
Sometimes the requirement really is a fixed list — a PDF export that will not be recalculated, or a template with a hand-built contents page. `internal_nav.py` builds one: a heading plus hyperlink entries to every heading, with optional back-links.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
"<python>" "<skill>/scripts/internal_nav.py" "<file.docx>" --out "<file-nav.docx>" --levels 1 2
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
See `tasks/navigation_internal_links.md`.
|
|
30
|
+
|
|
31
|
+
## Verify
|
|
32
|
+
|
|
33
|
+
Render the first pages and read them. A TOC that shows one line of grey hint text means the field never recalculated in that renderer; a TOC that shows the old headings means the document was rendered before the field updated. Say which one happened instead of claiming the TOC is correct.
|
|
34
|
+
|
|
35
|
+
## Pitfalls
|
|
36
|
+
|
|
37
|
+
- TOC entries come from outline levels, not from text that merely looks like a heading.
|
|
38
|
+
- A TOC inside a header or a text box does not work; it belongs in the body.
|
|
39
|
+
- Level 4+ headings are excluded unless you widen `--levels`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Visual verification
|
|
2
|
+
|
|
3
|
+
**Goal.** See the pages before the user does. A document that parses and a document that looks right are different claims.
|
|
4
|
+
|
|
5
|
+
## Render
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
"<python>" "<cli>" docx render --input "<file.docx>" --out-dir "<render-dir>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Writes a PDF and one PNG per page, and prints their paths plus the renderer it used (Word via COM, or LibreOffice). Use the same command through `render_docx.py` when you want the JSON report and page list:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
"<python>" "<skill>/render_docx.py" "<file.docx>" --output_dir "<render-dir>" --dpi 150
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Inspect
|
|
18
|
+
|
|
19
|
+
Open every PNG with `read_image` and check, in this order:
|
|
20
|
+
|
|
21
|
+
1. **Hierarchy** — does the page show the structure the user asked for, at a glance?
|
|
22
|
+
2. **Fit** — nothing past the right margin, no clipped table column, no text over a watermark.
|
|
23
|
+
3. **Breaks** — no heading alone at the bottom, no orphaned caption, no half-empty page before a section break.
|
|
24
|
+
4. **Text** — the words that were meant to change did change, and nothing else moved.
|
|
25
|
+
5. **Glyphs** — dashes, quotes, CJK, currency symbols and maths render, not as boxes.
|
|
26
|
+
6. **Header and footer** — present, correct, not repeated on a first page that should not have it.
|
|
27
|
+
|
|
28
|
+
## When no renderer exists
|
|
29
|
+
|
|
30
|
+
The runtime reports `renderers.libreoffice: null` and no Office installation. `render` then exits non-zero with a JSON error naming the missing renderer, and `render_docx.py` exits 3 with `reason: no_renderer`.
|
|
31
|
+
|
|
32
|
+
In that state:
|
|
33
|
+
|
|
34
|
+
- Verify structure with `docx read`, `docx_kit.py parts|text|xml` and the audit scripts.
|
|
35
|
+
- Tell the user plainly that layout was not visually verified, and offer to install Microsoft Office, WPS Office, or LibreOffice instead of describing a page you did not see.
|
|
36
|
+
- Do not claim a file "looks good" from XML evidence alone.
|
|
37
|
+
|
|
38
|
+
## After any script edit
|
|
39
|
+
|
|
40
|
+
Re-render. Package-valid is not page-valid: a caption inserted before the paragraph it describes, a table grown past the margin, a style merge that changed heading sizes — all are invisible to the XML and obvious in the image.
|
|
41
|
+
|
|
42
|
+
## Comparing revisions
|
|
43
|
+
|
|
44
|
+
`render_and_diff.py` renders two files and writes a heatmapped difference page. Read the diff images yourself; a ratio is not a conclusion.
|
|
45
|
+
|
|
46
|
+
## Pitfalls
|
|
47
|
+
|
|
48
|
+
- Different renderers paginate differently. Compare like with like, and do not promise Word-perfect pagination from a LibreOffice render.
|
|
49
|
+
- A render fails silently into a blank page when a font is missing; a blank page is a finding, not a rendering bug.
|
|
50
|
+
- Render into a fresh directory; stale PNGs from an earlier run are the classic false pass.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Watermarks and background text
|
|
2
|
+
|
|
3
|
+
**Goal.** A diagonal DRAFT / CONFIDENTIAL mark behind the page content, and the ability to find and remove one that a template left behind.
|
|
4
|
+
|
|
5
|
+
A Word watermark is not a document feature with its own element: it is a VML shape (`v:shape` with `v:textpath`) in a header part, positioned behind the text. That is why it disappears if the header is replaced, and why it can appear on some pages and not others when sections have their own headers.
|
|
6
|
+
|
|
7
|
+
## Add
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
"<python>" "<skill>/scripts/watermark_add.py" "<file.docx>" --out "<file-draft.docx>" --text "DRAFT"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Writes the shape into the default header, light grey, rotated -45°, behind the body. The script refuses to add a second watermark when it finds one — remove the old one first, or you get two overlapping marks.
|
|
14
|
+
|
|
15
|
+
For a per-page mark across a document with several sections, each section's header needs the shape; that is a loop over sections, not one insertion.
|
|
16
|
+
|
|
17
|
+
## Find and remove
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
"<python>" "<skill>/scripts/watermark_audit_remove.py" "<file.docx>" --mode report
|
|
21
|
+
"<python>" "<skill>/scripts/watermark_audit_remove.py" "<file.docx>" --mode remove --out "<file-clean.docx>"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The report lists the text of every VML text path and the part it lives in, and exits 1 when any watermark exists. `--contains` narrows to a substring, which is the case "the template says Company Confidential and it should not". Remove deletes the enclosing paragraph from the header.
|
|
25
|
+
|
|
26
|
+
## Verify
|
|
27
|
+
|
|
28
|
+
Render and read the pages: the mark sits behind the text (not over it), stays inside the page, and appears on every page the user expects. A watermark that vanishes on page two means that section has a different header.
|
|
29
|
+
|
|
30
|
+
## Pitfalls
|
|
31
|
+
|
|
32
|
+
- A watermark in a "different first page" header only shows on page one.
|
|
33
|
+
- Removing the header paragraph can leave an empty header that adds vertical space; check the top margin on the rendered page.
|
|
34
|
+
- An image watermark is an `v:imagedata` shape, not a text path — the report will not name it, so inspect the header XML when the visual mark is a logo.
|
|
35
|
+
- Watermarks are not security. See `tasks/protection_restrict_editing.md` for what actually restricts editing.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Rendering setup and failures
|
|
2
|
+
|
|
3
|
+
## First: does this machine render
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
"<python>" "<cli>" status
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Look at `renderers.libreoffice` and `microsoft_office`. Both null means `docx render` will fail — not with a corrupt file, but with `no document renderer found`. That is a setup problem, not a document problem.
|
|
10
|
+
|
|
11
|
+
## LibreOffice
|
|
12
|
+
|
|
13
|
+
Install LibreOffice (Windows: winget `TheDocumentFoundation.LibreOffice`), then check:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
soffice --headless --version
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If the binary is not on `PATH`, the runtime cannot find it; either add its `program` directory to `PATH` or install into the default location the runtime probes. On Windows, `soffice.com` is the console wrapper — use that one, not `soffice.exe`, when a script needs to wait for exit.
|
|
20
|
+
|
|
21
|
+
## Microsoft Office
|
|
22
|
+
|
|
23
|
+
When Word is installed, the CLI can drive it through COM and does not need LibreOffice. Office must be activated and able to launch without a first-run dialog; a pending activation or a modal "sign in" blocks the render forever.
|
|
24
|
+
|
|
25
|
+
## Common failures
|
|
26
|
+
|
|
27
|
+
| Message | Cause | Fix |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `no document renderer found` | Neither installed / not on PATH | Install one, re-check `status` |
|
|
30
|
+
| Render hangs | A dialog is waiting in the headless process | Kill the leftover process, add the `--headless --norestore` flags the CLI uses |
|
|
31
|
+
| Blank pages | Missing font | Install the font or substitute it |
|
|
32
|
+
| Text shifted | Font substitution | Compare with a render from the same renderer the user will use |
|
|
33
|
+
| `soffice already running` | A user profile lock from a crashed run | Remove the stale lock file under the LibreOffice user profile, or wait for the old process to exit |
|
|
34
|
+
|
|
35
|
+
## Working without a renderer
|
|
36
|
+
|
|
37
|
+
Structure, text and metadata are all verifiable headlessly (`docx read`, the audit scripts, `docx_kit.py parts|text|xml`). Layout is not. In that state:
|
|
38
|
+
|
|
39
|
+
1. Do the structural verification.
|
|
40
|
+
2. Tell the user that page layout was not visually checked, and why.
|
|
41
|
+
3. Offer the install, or ask them to open the file and say what looks wrong.
|
|
42
|
+
|
|
43
|
+
Never describe a page you did not render.
|
|
44
|
+
|
|
45
|
+
## Keep renders disposable
|
|
46
|
+
|
|
47
|
+
Render into a fresh temp directory each time (`--out-dir`). A stale PNG from a previous run reads as a pass and is the most common false confirmation.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Splitting a long job
|
|
2
|
+
|
|
3
|
+
A twenty-section document is not one command. Splitting it into bounded steps keeps each change verifiable and stops one failure from hiding inside a large edit.
|
|
4
|
+
|
|
5
|
+
## One step per structural change
|
|
6
|
+
|
|
7
|
+
1. **Plan** — the outline (headings and what each section must establish), written down before any file work.
|
|
8
|
+
2. **Skeleton** — create the file with the outline only: heading blocks, no body text.
|
|
9
|
+
3. **Content, in batches** — a batch per top-level section, appended with `append_blocks.py`.
|
|
10
|
+
4. **Numbers** — captions, cross-references, TOC once the structure is final, never while sections are still moving.
|
|
11
|
+
5. **Verify** — render, inspect, fix.
|
|
12
|
+
|
|
13
|
+
## Why this order
|
|
14
|
+
|
|
15
|
+
- Captions and TOC are fields over structure; running them after every batch means renumbering noise and a TOC that points at deleted headings.
|
|
16
|
+
- A batch of one section is small enough to redo when the layout is wrong.
|
|
17
|
+
- The skeleton proves the heading hierarchy before the text makes it expensive to change.
|
|
18
|
+
|
|
19
|
+
## Batch boundaries
|
|
20
|
+
|
|
21
|
+
- One section per file: `section-01.docx`, `section-02.docx`, then `merge_docx_append.py`. This is the only sane way to draft in parallel or across sessions.
|
|
22
|
+
- Keep a `manifest.json` listing section files in order. Merging is then mechanical and the order is not remembered from chat.
|
|
23
|
+
- Merge before numbering, not after.
|
|
24
|
+
|
|
25
|
+
## When a batch fails
|
|
26
|
+
|
|
27
|
+
Do not patch a large edit inside a large document. Discard the failed batch's output, fix the batch, re-apply it to the previous good file. Keep the previous good file until the batch is verified.
|
|
28
|
+
|
|
29
|
+
## Context hygiene
|
|
30
|
+
|
|
31
|
+
- Long sessions lose the document design. Keep the plan and the design defaults (page size, font sizes, table borders) in a file in the workspace and re-read it, rather than trusting recollection.
|
|
32
|
+
- When the user changes a decision mid-job (page size, heading style), record it at the point they said it, and apply it to batches already written.
|
|
33
|
+
|
|
34
|
+
## Smoke test after the join
|
|
35
|
+
|
|
36
|
+
`examples/end_to_end_smoke_test.md` is the checklist to run on the merged file before it becomes a deliverable.
|