
Document automation combines structured data with controlled templates to produce repeatable drafts. Reliability comes from validating the inputs, making selection rules explicit, and checking both the document content and its rendering. Begin with a small document whose expected result a reviewer can explain.
Separate data, rules, and presentation
Give each field a definition, type, required/optional status, and source. Keep record identifiers distinct from display labels. Specify how missing values differ from empty strings and from zero. A template should not silently invent a delivery date, recipient, or approval.
Keep business rules reviewable. A rule such as “include a priority note when rush is true” belongs in the template logic or a documented decision layer. Fonts and margins belong to presentation. Authentication and approval belong to the surrounding workflow.
XSLT can select and format data into a document structure; the W3C XSLT specification describes the transformation language. The supplied small example uses XSLT 1.0 through lxml, so its requirements remain modest.
Generate a dispatch draft
The XML practice pack includes a fictional dispatch record and an HTML template. With Python 3.10 or later and lxml installed, run python generate_dispatch.py. Open dispatch.html; it should identify dispatch D1, recipient Studio Team, and two units of item A1. A priority note appears because rush is true.
<dispatch id="D1" rush="true">
<recipient>Studio Team</recipient>
<item><sku>A1</sku><quantity>2</quantity></item>
</dispatch>Change rush to false and rerun: the priority note disappears while recipient and item remain. Remove the recipient and generation fails validation. The generator accepts only the documented true/false spellings for rush, so a value such as “maybe” fails instead of selecting a branch accidentally.
This is a dispatch demonstration, not approved contract language or a production approval system. Its value is showing how one input change leads to a predictable output change.
Test the branches and boundaries
| Input change | Expected result | Review focus |
|---|---|---|
| rush=true | Priority note included | Exact wording and placement. |
| rush=false | Priority note absent | Remaining content still complete. |
| Recipient absent or blank | Generation rejected | No apparently valid draft issued. |
| Quantity negative | Validation rejected | Error identifies the invalid field. |
| Recipient contains an ampersand | Original text appears safely | No broken HTML or interpreted markup. |
| Long recipient or many items | All data remains readable | Wrapping, pagination, repeated labels. |
Run python selftest.py for the bundled automated checks. The visual long-content cases still require opening the output. Keep expected document examples alongside templates so a reviewer can see when wording, order, or conditional logic changes.
Control versions and release
Record the data snapshot, schema version, template version, generator version, and generation time with a draft. Assign the output a stable identifier. Write only after validation and transformation succeed; keep previous outputs clearly labeled so a failed run cannot be mistaken for a new document.
Decide who can change templates and who approves generated documents. For consequential agreements, qualified reviewers control the language and approval rules. A successful generation command establishes a technical result, not authorization to issue the document.
If humans edit a generated draft, retain that edited copy and its review status. Regeneration from unchanged source will recreate the template output and can lose those edits. Either incorporate an approved change into structured inputs or keep a documented final-edit stage. For HTML/PDF delivery, use the publishing guide’s output checks.