fix(docx): avoid numbering generated letter paragraphs
From the PR description
Summary
- make generated DOCX section numbering opt-in through an optional
numberSectionstool argument - leave letters and other prose documents unnumbered by default
- stop assigning inferred legal numbers to ordinary body paragraphs
- preserve explicit bullets and manually typed numbering
- add DOCX XML regression tests for default and opt-in numbering
Problem
The DOCX generator always configured legal numbering and inferred a numbering level for every paragraph beneath a section heading. This caused ordinary demand-letter prose to render approximately as:
1. DEMAND FOR PAYMENT1.1 We represent the claimant.(a) Payment is required within ten days.
A prompt-only fix would be insufficient because the renderer itself applied numbering even when the model did not request it.
Behavior after this change
Documents are unnumbered by default. numberSections: true numbers section headings when explicitly requested or required by a workflow/template, while ordinary prose remains unnumbered. Explicit bullets use Word bullet formatting. Manually typed prefixes such as 1. This reference is intentional. remain literal text when automatic numbering is off.
The dispatcher and renderer both use === true, so missing, null, string, or otherwise malformed values cannot enable numbering accidentally.
Tests
New regression tests inspect word/document.xml (and word/numbering.xml for bullets) inside generated DOCX bytes and cover:
- demand-letter headings and body prose unnumbered by default
- opt-in legal numbering on headings only
- preservation of manually typed numbering when numbering is off
- explicit bullets rendered as bullets rather than legal clauses
Validation completed (rebased onto current main):
tsc --noEmitinbackend- cleannpm testinbackend- 711 passed, 24 skipped- The four new
documentGeneration.test.tscases all pass
Rebased onto current main
Re-verified the bug has not been addressed upstream in the meantime: documentOps.ts on main still calls legalNumbering(inferredLevel) for inferred paragraphs and still configures the numbering set unconditionally, and numberSections appears nowhere in backend/src.
The rebase was clean. Worth noting explicitly because it touches neighbouring code: this does not conflict with the docx text-value work merged in #328 - that PR's preserves numeric-looking text through an unrelated edit regression test still passes alongside the new numbering tests.
No database, frontend, provider, PDF reconstruction, or product-module changes are included.
Our analysis
Make DOCX section numbering opt-in — read the full analysis →
Think the analysis missed something the PR description covers?
Commits in this PR (1)
| SHA | Subject | Author | Date | |
|---|---|---|---|---|
8baa5529 | fix(docx): avoid numbering generated letter paragraphs | duncanmcqueen | 2026-08-21 | ↗ GitHub |
commit bodyThe DOCX generator configured legal numbering unconditionally and inferred a numbering level for every paragraph under a section heading, so ordinary demand-letter prose came out as "1. DEMAND FOR PAYMENT / 1.1 We represent the claimant. / (a) Payment is required...". Prompting alone could not fix it: the renderer numbered the output whether or not the model asked for numbering. - Numbering is now opt-in via an optional numberSections tool argument. Letters and other prose render unnumbered by default. - When requested, only section headings are numbered; body prose is not. - Explicit bullets keep Word bullet formatting, and manually typed prefixes like "1." stay literal text when automatic numbering is off. - Both the dispatcher and the renderer test `=== true`, so a missing, null, or malformed value cannot switch numbering on by accident. Adds DOCX XML regression tests that read word/document.xml (and word/numbering.xml for bullets) out of the generated bytes. Rebased onto main; no conflicts with the docx text-value work in #328, whose numeric-text regression test still passes alongside these. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | ||||
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-337.md from
inside the repo you want the changes in.