opboxDocs
Sign inBook a demo
DocsDocument GenerationAI - Agent

Document Generation

Opbox generates DOCX documents from template packs using a patch-not-reconstruct model: the original DOCX is preserved byte-for-byte and the AI applies surgical edits to bookmarked anchors. This avoids round-tripping through any lossy intermediate representation.

Three generation modes:

ModeWhat the AI fillsSource
TEMPLATE_FILLVariables (names, dates, percentages)Matter payload + linked records
ASSISTED_DRAFTProse sections (clauses, narrative) into named bookmarksAI prompt + matter context
FULL_AUTHORThe whole document from scratchAI prompt + matter context (Phase 0+)

All three modes share the same coverage gate, the same patcher, and the same tamper-evident snapshot.

Mental Model

Template DOCX  +  Matter Payload + AI prompt
       │
       ├── Detection: identify variables, sections, tables ────┐
       │                                                       │
       ├── Coverage Gate (PASS / WARN / FAIL) ─────────────────┤
       │                                                       │
       ├── Templater Agent ─────────────────────────────────►  Patch Operations
       │   (Sonnet semantic mapping, Zod-validated)            │
       │                                                       │
       ├── Patcher (replaceText / insertBookmarkAndClear /    │
       │            replaceParagraphRange / replaceTable)  ◄───┘
       │
       └── Generated DOCX  +  Snapshot (checksum, payload, mode)

Bookmark Naming Convention

Templates declare anchors using bookmarks with a controlled prefix:

PrefixPurpose
opbox_section_<name>Prose section anchor. The patcher replaces the paragraph range between bookmark start and end with AI-rendered Tiptap content.
opbox_table_<name>Table anchor. The patcher replaces the entire <w:tbl> element.
opbox_var_<name>Variable anchor. The patcher inserts the resolved value and clears the placeholder text.

The BookmarksPanel in the document workbench lists all bookmarks in a DOCX with kind classification, click-to-scroll navigation, and per-row delete.

Coverage Gate

A pure-function coverage gate classifies generation readiness before any writes occur.

ResultMeaningGeneration behaviour
PASSEvery required variable resolves to a non-empty valueGenerate.
WARNAll required variables resolve but optional ones are missingGenerate, mark missing in snapshot.
FAILOne or more required variables are unresolvedReject 422.

/api/documents/packs/[packId]/generate and /preview both call the gate before doing any writes. FAIL short-circuits to 422 with a structured error listing the missing variables.

nullGetter emits [MISSING: name] markers - no silent empties. Means a downstream reviewer can grep the rendered doc to find unresolved spots even when the gate says WARN.

Patcher Operations

The DOCX patcher implements a small set of surgical operations against the source XML:

OpPurpose
insertBookmarkAndClearInsert content at a bookmark and clear any placeholder text in the bookmark range. Used by opbox_var_*.
insertTableBookmarkInsert a <w:tbl> at a bookmark. Used by opbox_table_*.
replaceParagraphRangeReplace the paragraph range between two bookmark markers. Used by opbox_section_*.
replaceTableReplace an entire existing <w:tbl> element.
replaceTextSingle-run text replacement (TEMPLATE_FILL fast path).
replaceParagraphTextCross-run text replacement with anchor-run rPr clone. Used when a placeholder spans multiple <w:r> elements.
removeBookmarkStrip both <w:bookmarkStart> and <w:bookmarkEnd> markers without touching content.
resolveBookmarkRangeHelper that finds bookmark start/end markers and returns the paragraph range.

A pre-patch normalisation step coalesces split placeholders so a {name} that Word has split across two runs (because of formatting) collapses back to a single replaceable run.

Templater Agent

The templater is a Sonnet-driven semantic mapper. It receives:

  • A list of detected placeholders ({full_name}, {registered_address_line_1}, ...)
  • The matter payload (already normalised by matter_get_payload)
  • The available variable registry from pack_get_template_variables

And returns (via forced tool-call, schema-validated):

  • A mapping of placeholder -> resolved value
  • Optional prose section content (Tiptap JSON)
  • Optional dynamic-table rows

Errors surface with a kind discriminator: api_error / no_tool_use / invalid_output / timeout. An empty API key is rejected upfront.

Tiptap to OOXML

A minimum-viable Tiptap-to-OOXML converter supports ASSISTED_DRAFT mode:

  • Paragraphs and headings
  • Marks (bold, italic, underline)
  • Alignment
  • Inherited paragraph properties

Deferred to later phases: lists, tables, hyperlinks, mentions.

Generation Snapshot

Every generated document writes a tamper-evident snapshot with:

FieldPurpose
snapshotChecksumSHA-256 of snapshotPayload. Changes if anything is rewritten.
snapshotPayloadThe exact inputs that produced the doc (template, payload, mappings, prose, tables).
templateChecksumThe template's checksum at generation time. Detects whether the template has since changed.
coverageResultPASS / WARN / FAIL.
missingVariablesList of unresolved required variables (empty on PASS, populated on WARN).
generationModeTEMPLATE_FILL / ASSISTED_DRAFT / FULL_AUTHOR.
generationStatusDerived from coverage + run outcome.

Snapshots make downstream audits trivial: every generated doc can be regenerated bit-identical from its snapshot, and any divergence between a snapshot and the rendered doc proves tampering.

Cloud-Fire Dispatch

The doc-gen runtime is "Claude Code connected to the Opbox MCP." Two ways to start it:

  1. Pull-only - any local Claude Code session running with an MCP key can drain the queue.
  2. Cloud-fire - Opbox POSTs a wake-up ping to a hosted agent provider on step entry; the hosted Claude Code claims the task autonomously.

Cloud-fire is configured at Settings > AI > Doc Generation Agent:

FieldPurpose
Providerclaude-routine (Anthropic Claude Code Routine) or openai-frontier (OpenAI hosted agent)
Fire URLThe provider's per-routine POST endpoint
Bearer tokenAuth for the fire URL. Encrypted at rest.
Routine ownerThe workspace member whose identity the hosted agent acts as. Use a service account in production.
Daily capProvider-imposed ceiling on fires per day (informational).

Test with the Test fire button. Disable globally with the Doc-generation agent enabled kill-switch; when off, steps that require generation fall back to NEEDS_MANUAL_GENERATION.

MCP Tool Surface for Doc-Gen

ToolPurpose
matter_get_payloadNormalised matter context (entity, stakeholders, line items, properties).
matter_get_stepStep config + current data.
matter_validate_before_generateRun coverage gate without generating.
pack_get_template_variablesVariable registry from the pack.
pack_get_prose_sectionsSection bookmarks + descriptions.
pack_get_dynamic_tablesDynamic-table bookmarks.
step_get_dataForm-step data already collected.
data_source_list / search / get_itemPluggable data-source registry (addons populate).

Plus the Agent Queue tools (agent_queue_*) for claim/lease/release.

These tools are registered under the doc-gen MCP category. Queue mutations gate at autonomy L1, reads at L0.

API

EndpointPurpose
POST /api/documents/packs/[packId]/generateRun generation with the matter payload. Coverage FAIL returns 422.
POST /api/documents/packs/[packId]/previewPreview without writing the generated doc.
GET /api/documents/generated/[id]Generated doc metadata + snapshot.
GET /api/documents/generated/[id]/downloadStream the rendered DOCX (or zipped multi-file).
GET /api/documents/doc/[id]/bookmarksList bookmarks in a DOCX.
DELETE /api/documents/doc/[id]/bookmarks/[name]Remove a bookmark (markers only - content untouched).
GET /api/documents/doc/[id]/stylesWord paragraph styles for the Format > Paragraph style menu.

See Also

We use cookies

Strictly necessary cookies keep you signed in and protect requests. We also use optional cookies for preferences and (when enabled) analytics. Learn more.