How an AI agent should author .lxdoc documents in markdown for LX Draft.
A .lxdoc file is a UTF-8 text file with three parts:
--- markers at the top)The simplest .lxdoc is just markdown wrapped in frontmatter:
---
format: "lxd/1.0"
title: "My Document"
revision: "v0.1"
status: "draft"
---
# My Document
This is the body. Standard Markdown works fine.
LX Draft stores a self-excluding SHA-256 hash in frontmatter.integrity to detect later
tampering (it underpins signature auto-clear-on-edit). As an agent, never write or guess the
integrity field — omit it entirely. LX Draft computes and injects the correct hash
automatically when the document is saved or edited in the app. A hash you invent would be wrong and flag
the document as modified. (Unsigned drafts don't need a hash at all; it only matters once a human/Lexis
signs the document.)
LX Draft is used two ways, and it affects governance (not how you write content):
.lxdoc file the user opens/saves on
their own drive (or Google Drive). The user self-signs; revision history is a table they maintain.lexis_controlled: true in its frontmatter.
As an agent you write the same markdown either way. Do not set lexis_controlled
yourself, and do not try to sign documents — placing a {signature:...} field is
enough; the human or Lexis performs the signing.
You MUST include these:
format: "lxd/1.0" # always this exact value
title: "Document Title" # required
revision: "v0.1" # use v0.1 for first draft
status: "draft" # draft | review | approved | released | obsolete
Add these whenever you have the information:
short_title: "Short Name" # for headers / breadcrumbs
document_number: "T-XXX-001" # if there's a doc numbering scheme
date_created: 2026-03-30 # YYYY-MM-DD
classification: "internal" # public | internal | commercial-in-confidence | restricted
template: "standard" # standard | proposal | test-report | datasheet | minutes | review
project: "Project Name"
prepared_for: "Client Name"
author: "Your Name"
font: "Poppins" # default body font; use Poppins unless told otherwise
These work as you'd expect from any Markdown:
# H1 through ###### H6 for headings**bold**, *italic*, ~~strikethrough~~, `inline code`- bullet list and 1. numbered list (nest with 2-space indent)| col | col | pipe tables (with header separator row)> blockquote```language[link text](https://url) for hyperlinks--- for horizontal rulesUse these for tips, warnings, important notes:
> [!NOTE]
> Informational note. Blue left border.
> [!TIP]
> Helpful tip. Green left border, "Tip:" prefix.
> [!IMPORTANT]
> Important caveat. Amber left border, "Important:" prefix.
> [!WARNING]
> Warning text. Red left border, "Warning:" prefix.
> [!STEP]
> Procedural step. Blue left border, no prefix.
For engineering tables (BOMs, test matrices, requirements), use CSV blocks instead of pipe tables:
```csv:bom
reference,part_number,quantity,manufacturer
U1,STM32F405RGT6,1,STMicroelectronics
C1,CL21B104KBCNNNC,10,Samsung
R1,RC0805FR-07100KL,4,Yageo
```
The label after csv: describes the data type (e.g. bom, requirements,
test-results). The editor renders these as editable grids.
Inline math: $R = V / I$ where $V$ is voltage.
Block math:
$$
P_{total} = \sum_{i=1}^{n} V_i \cdot I_i
$$
This statement needs a citation[^1].
This refers to an endnote[^note:pricing].
[^1]: Footnote text — typically appears at the bottom of the page.
[^note:pricing]: Endnote text — appears at the end of the document.
<!-- bookmark: thermal-section -->
See {ref:sec:introduction} for background.
Refer to {ref:bookmark:thermal-section|the thermal section} for details.
You can embed a live reference to a frontmatter field in the body. It renders the current value of
that field (and updates if the frontmatter changes), and resolves to the value in PDF/DOCX export. Use the
{field:NAME} token:
Document number: {field:document_number}
Revision {field:revision} — prepared for {field:prepared_for}.
Valid field names: title, short_title, document_number,
revision, author, status, classification,
date_created, project, prepared_for.
In the editor, a human inserts these via the Insert Field toolbar button; as an agent, just
write the {field:NAME} token in the markdown — it round-trips and resolves on export.
A signature field renders the signer's name (in a cursive font) once the document is signed, or a muted "Your signature here" placeholder when unsigned:
Signed: {signature:author}
Signing itself is a human/Lexis action (see "Standalone vs Lexis" above) — as an agent you place the
{signature:...} token where the signature should appear; you do not sign the document.
If you need a hard page break in the printed output:
<!-- pagebreak -->
<!-- columns: 2 -->
Content here flows into two columns.
<!-- /columns -->
<!-- textbox -->
Content inside a bordered box.
<!-- /textbox -->
For external images (URLs), use standard Markdown:

For embedded images, use the asset reference syntax:

Then add the image to a document-assets block at the end of the file (see "Embedded images" below).
Size, alignment and crop are persisted as an HTML comment immediately after the image. You normally don't need to write these (the human adjusts images by dragging in the editor), but they round-trip and the exporter honours them:
<!-- width=320 height=180 align=center crop=10,10,10,10 -->
width / height — display size in px.align — left | center | right | inline.
left/right float the image so body text wraps around it; center is a
centered block.crop — top,right,bottom,left inset in px (the visible region is the image minus
these insets). Omit if not cropped.Insert a self-updating TOC anywhere:
<!-- toc -->
The editor renders this as an actual TOC. In source form it stays as the directive.
Do not base64-encode media inside the .lxdoc. LLM output is unreliable for large binary blobs — you will silently truncate, corrupt, or hit token limits. Instead, deliver two things:
.lxdoc (or .md) file with asset:<id>
references in the body..zip archive containing the actual media files, named to match those
references.LX Draft's import dialog has a "Document + Archive" tab that does the embedding for you — it reads the archive, matches each ref to a file, base64-encodes server/browser-side, and produces a self-contained .lxdoc.
Strongly preferred — use bare asset:<id> references with no folder prefix:


<id> must match a filename stem somewhere in the bundle folder (e.g.
asset:photo-013435032 matches photo-013435032.jpg). or
. If you change the folder name later, every
reference breaks. Use  and let LX Draft handle the lookup.
Relative paths (./diagrams/field-plot.png) are accepted as a fallback — LX Draft matches them by
exact path then by basename — but they couple the document to the directory layout. Avoid them in agent output.
Absolute filesystem paths (C:\..., /home/...) can never resolve in a browser.
Don't use them.
The folder can be flat or have subfolders — both work, because LX Draft matches by filename stem. Filename
stems must be unique within the bundle when you use asset:<id> references.
my-report/ ← name doesn't matter; user picks this folder
├── photo-013435032.jpg ← matches asset:photo-013435032
├── photo-013449261.jpg ← matches asset:photo-013449261
└── pcb-rev-b.png ← matches asset:pcb-rev-b
If you have many media files of different types, put them in subfolders for your own organisation — the matcher still works:
my-report/
├── photos/
│ ├── photo-013435032.jpg ← still matches asset:photo-013435032
│ └── photo-013449261.jpg
└── diagrams/
└── pcb-rev-b.png ← still matches asset:pcb-rev-b
The asset:<id> reference does not include the subfolder.
Anything LX Draft can render or link: png, jpg/jpeg, gif,
webp, svg, bmp, tiff, pdf, mp4,
webm, mp3, wav, ogg. The MIME type is inferred from the
extension.
.lxdoc file (with asset: references, no document-assets block).You don't need to zip anything. Write the files into a folder; LX Draft reads only the files actually referenced by the document and ignores the rest.
If you absolutely cannot deliver media files (e.g. you only have URLs), use plain HTTPS image references. They'll work but won't be embedded — the document depends on the URL staying live.
The format is documented for completeness. Avoid using it from an agent:
<!-- document-assets
- id: pcb-rev-b
filename: pcb-layout-rev-b.png
mime: image/png
size: 145832
data: iVBORw0KGgoAAAANSUhEUgAA[... full base64 string, no line breaks ...]
/document-assets -->
The data: field must be the complete base64 (no line breaks, no truncation). The
size: field should match the original file's byte length.
The <!--[c1]-->...<!--[/c1]--> and
<!--{ch1:del}-->...<!--{/ch1}--> syntax is for review comments and tracked changes.
Do not generate these manually. Use the API endpoints if you need to add review data
programmatically.
Engineering documents are read for reference, not narrative. Structure beats prose. Use these tools deliberately:
## 1 Section — top-level. Numbered. Examples: Introduction, Scope, Requirements, Test Plan.### 1.1 Subsection — major topic within a section. Numbered.#### Subtopic — finer division. Usually unnumbered.##### Detail and ###### Note — rare; only for deep technical reference material.Don't skip levels. Going from ## straight to #### confuses readers
and breaks the table of contents.
Don't use # (H1) inside the body — the document title is in the frontmatter and
rendered as the cover. Body sections start at ##.
> [!IMPORTANT] callout.> [!WARNING].> [!TIP].> [!STEP].> [!NOTE].Don't overuse callouts. If every other paragraph is a callout, none of them stand out.
**bold** for terms being defined and absolutely critical phrases. Two or three per page
maximum.*italic* for proper nouns, document titles, and gentle emphasis.`inline code` for identifiers, filenames, signal names, command-line arguments, register
names — anything the reader might type or look up literally.| col | col |) — short inline tables, 3-6 rows, primarily textual content.csv:requirements, csv:bom,
csv:test-results, csv:registers etc. The label after csv: is
descriptive; use whatever fits.A typical LX engineering document has this structure:
---
[frontmatter]
---
<!-- toc -->
## 1 Introduction
Brief overview of the document and project.
## 2 Scope
What this document covers.
## 3 [Main content section]
### 3.1 Subsection
Content here.
### 3.2 Another subsection
```csv:requirements
id,description,criticality
REQ-001,System must boot in under 5 seconds,High
REQ-002,Battery life must exceed 8 hours,High
```
## 4 [Another main section]
## Appendix
### A. Additional Information
If you're an agent producing a document, you have three options:
Just write standard markdown. The user clicks "Import Markdown" in LX Draft and uploads the file. LX Draft wraps it in default frontmatter automatically.
Write the full file with frontmatter. The user clicks "Import Markdown" → "Upload File" and selects the .lxdoc. It imports as-is.
If you have programmatic access to the LX Draft API, use the stateless content-based
endpoint. You pass the FULL current document as content and the new section text as
content_to_apply; the response returns the rebuilt full document content for you to
apply (LX Draft no longer stores documents server-side, so there is no :id):
POST /api/agent/edit
Content-Type: application/json
{
"content": "<the full current .lxdoc text>",
"section_heading": "3 DFM Review",
"action": "replace", // replace | append | prepend
"content_to_apply": "New markdown content...",
"track_changes": true,
"agent_name": "Your Agent Name"
}
Response: { ok: true, content: "<rebuilt full document>", section, action, validation }.
The validator runs server-side; if the edit would make the document invalid you get a 422 with the issues to
correct. Related stateless endpoints: POST /api/agent/sections ({content} → list of
sections) and POST /api/spellcheck ({content}).
Stuff agents have actually done that broke the import or rendering. Don't repeat these:
layout: "page",
page_size: "A4", margins: "standard". LX Draft has a fixed schema; unknown fields
are silently ignored. If you think you need a layout option, you don't — page geometry is decided by the
renderer.Components not
Table 3: Components. The renderer numbers tables, figures, and equations automatically.# Title. The title goes in the
YAML frontmatter only. Body sections start at ## 1 Section.. Use
. The folder name doesn't survive into the import.document-assets. If you find yourself writing a
data: field by hand, stop. Deliver media as separate files alongside the .lxdoc and let the
user import the bundle. LLMs cannot reliably emit hundreds of KB of base64 in one shot — output gets
truncated silently.<!--[c1]-->...<!--[/c1]--> or
<!--{ch1:del}-->. Those are for human review tooling. Agents should never write them
manually.## → ####). Breaks the table of
contents and the document map.
LX Draft has a public validation endpoint. Run your .lxdoc through it before handing the document
to the user — you'll catch malformed YAML, missing required fields, broken asset references, and the rest of
the issues listed above.
From the command line (Node 18+, no npm install needed; the script lives in the LX Draft repo):
node scripts/lint-lxdoc.mjs path/to/your/doc.lxdoc
It exits 0 on success and 1 on validation errors, so you can use it in scripts.
As an HTTP endpoint (for agents that have internet but not the LX Draft repo):
POST https://lxdraft.lx-cloud.com/api/validate-public
Content-Type: text/plain
<the entire .lxdoc content as the request body>
Returns:
{
"valid": true,
"issues": [
{ "rule": "V003", "severity": "warning", "message": "...", "location": "..." }
]
}
valid: false means there is at least one error-severity issue. Fix all errors before delivery;
warnings are advisory.
Your output will be validated. To avoid rejection:
format: "lxd/1.0" must be presentasset:id, that asset must be in the document-assets blockformat: "lxd/1.0"## 1 Section format (number + space + name)asset:id (with assets block) or external URLs<!--[c1]--> etc.) unless you're using the API# Agent Writing Guide for LX Draft Documents
This guide tells AI agents how to write `.lxdoc` documents that LX Draft can render and edit. Give this guide to any agent that needs to produce engineering documents for the LX Design House team.
## What is an .lxdoc file?
A `.lxdoc` file is a UTF-8 text file with three parts:
1. **YAML frontmatter** — metadata and document properties (between `---` markers at the top)
2. **Markdown body** — the actual content using standard Markdown plus a few extensions
3. **Optional document-assets and document-meta blocks** — for embedded images and review data (you usually don't need these)
The simplest .lxdoc is just markdown wrapped in frontmatter:
```
---
format: "lxd/1.0"
title: "My Document"
revision: "v0.1"
status: "draft"
---
# My Document
This is the body. Standard Markdown works fine.
```
## The integrity hash — do NOT write it
LX Draft stores a self-excluding SHA-256 hash in `frontmatter.integrity` to
detect later tampering (it underpins signature auto-clear-on-edit). **As an
agent, never write or guess the `integrity` field — omit it entirely.** LX Draft
computes and injects the correct hash automatically when the document is saved
or edited in the app. A hash you invent would be wrong and flag the document as
modified. (Unsigned drafts don't need a hash at all; it only matters once a
human/Lexis signs the document.)
## Standalone vs Lexis (context you should know)
LX Draft is used two ways, and it affects governance (not how you write content):
- **Standalone** — the document is a plain `.lxdoc` file the user opens/saves on
their own drive (or Google Drive). The user self-signs; revision history is a
table they maintain.
- **Lexis-managed** — the document is a controlled project artifact. Lexis owns
its lifecycle (releases, version history, signing). A controlled document
carries `lexis_controlled: true` in its frontmatter.
As an agent you write the same markdown either way. Do **not** set
`lexis_controlled` yourself, and do **not** try to sign documents — placing a
`{signature:...}` field is enough; the human or Lexis performs the signing.
## Required frontmatter fields
You MUST include these:
```yaml
format: "lxd/1.0" # always this exact value
title: "Document Title" # required
revision: "v0.1" # use v0.1 for first draft
status: "draft" # draft | review | approved | released | obsolete
```
## Recommended frontmatter fields
Add these whenever you have the information:
```yaml
short_title: "Short Name" # for headers / breadcrumbs
document_number: "T-XXX-001" # if there's a doc numbering scheme
date_created: 2026-03-30 # YYYY-MM-DD
classification: "internal" # public | internal | commercial-in-confidence | restricted
template: "standard" # standard | proposal | test-report | datasheet | minutes | review
project: "Project Name"
prepared_for: "Client Name"
author: "Your Name"
font: "Poppins" # default body font; use Poppins unless told otherwise
```
## Standard Markdown features (just use them)
These work as you'd expect from any Markdown:
- `# H1` through `###### H6` for headings
- `**bold**`, `*italic*`, `~~strikethrough~~`, `` `inline code` ``
- `- bullet list` and `1. numbered list` (nest with 2-space indent)
- `| col | col |` pipe tables (with header separator row)
- `> blockquote`
- ` ```language ` fenced code blocks
- `[link text](https://url)` for hyperlinks
- `---` for horizontal rules
## LX-specific extensions (use these for engineering docs)
### Callout boxes
Use these for tips, warnings, important notes:
```markdown
> [!NOTE]
> Informational note. Blue left border.
> [!TIP]
> Helpful tip. Green left border, "Tip:" prefix.
> [!IMPORTANT]
> Important caveat. Amber left border, "Important:" prefix.
> [!WARNING]
> Warning text. Red left border, "Warning:" prefix.
> [!STEP]
> Procedural step. Blue left border, no prefix.
```
### Tables (rich CSV blocks)
For engineering tables (BOMs, test matrices, requirements), use CSV blocks instead of pipe tables:
````markdown
```csv:bom
reference,part_number,quantity,manufacturer
U1,STM32F405RGT6,1,STMicroelectronics
C1,CL21B104KBCNNNC,10,Samsung
R1,RC0805FR-07100KL,4,Yageo
```
````
The label after `csv:` describes the data type (e.g. `bom`, `requirements`, `test-results`). The editor renders these as editable grids.
### Equations (LaTeX)
```markdown
Inline math: $R = V / I$ where $V$ is voltage.
Block math:
$$
P_{total} = \sum_{i=1}^{n} V_i \cdot I_i
$$
```
### Footnotes and endnotes
```markdown
This statement needs a citation[^1].
This refers to an endnote[^note:pricing].
[^1]: Footnote text — typically appears at the bottom of the page.
[^note:pricing]: Endnote text — appears at the end of the document.
```
### Cross-references and bookmarks
```markdown
See {ref:sec:introduction} for background.
Refer to {ref:bookmark:thermal-section|the thermal section} for details.
```
### Document-property fields (live values from frontmatter)
You can embed a *live* reference to a frontmatter field in the body. It renders
the current value of that field (and updates if the frontmatter changes), and
resolves to the value in PDF/DOCX export. Use the `{field:NAME}` token:
```markdown
Document number: {field:document_number}
Revision {field:revision} — prepared for {field:prepared_for}.
```
Valid field names: `title`, `short_title`, `document_number`, `revision`,
`author`, `status`, `classification`, `date_created`, `project`, `prepared_for`.
In the editor, a human inserts these via the **Insert Field** toolbar button; as
an agent, just write the `{field:NAME}` token in the markdown — it round-trips
and resolves on export.
### Signature fields
A signature field renders the signer's name (in a cursive font) once the
document is signed, or a muted "Your signature here" placeholder when unsigned:
```markdown
Signed: {signature:author}
```
Signing itself is a human/Lexis action (see "Standalone vs Lexis" below) — as an
agent you place the `{signature:...}` token where the signature should appear;
you do not sign the document.
### Page breaks
If you need a hard page break in the printed output:
```markdown
```
### Multi-column sections
```markdown
Content here flows into two columns.
```
### Text boxes (bordered content)
```markdown
Content inside a bordered box.
```
### Images
For external images (URLs), use standard Markdown:
```markdown

```
For embedded images, use the asset reference syntax:
```markdown

```
Then add the image to a `document-assets` block at the end of the file (see "Embedded images" below).
**Size, alignment and crop** are persisted as an HTML comment immediately after
the image. You normally don't need to write these (the human adjusts images by
dragging in the editor), but they round-trip and the exporter honours them:
```markdown

```
- `width` / `height` — display size in px.
- `align` — `left` | `center` | `right` | `inline`. `left`/`right` float the
image so body text wraps around it; `center` is a centered block.
- `crop` — `top,right,bottom,left` inset in px (the visible region is the image
minus these insets). Omit if not cropped.
### Table of contents
Insert a self-updating TOC anywhere:
```markdown
```
The editor renders this as an actual TOC. In source form it stays as the directive.
## Media attachments (preferred: deliver a bundle, don't base64 yourself)
**Do not base64-encode media inside the .lxdoc.** LLM output is unreliable for large binary blobs — you will silently truncate, corrupt, or hit token limits. Instead, deliver two things:
1. A clean **`.lxdoc` (or `.md`) file** with `asset:` references in the body.
2. A **`.zip` archive** containing the actual media files, named to match those references.
LX Draft's import dialog has a "Document + Archive" tab that does the embedding for you — it reads the archive, matches each ref to a file, base64-encodes server/browser-side, and produces a self-contained .lxdoc.
### Reference syntax in the markdown
**Strongly preferred — use bare `asset:` references with no folder prefix:**
```markdown


```
- `` must match a filename **stem** somewhere in the bundle folder (e.g. `asset:photo-013435032` matches `photo-013435032.jpg`).
- The folder name is **irrelevant** — LX Draft strips the top-level folder when matching. Don't put the folder name in the reference.
- The id should be a slug: letters, digits, hyphens. No spaces, no slashes.
- **Don't write `` or ``.** If you change the folder name later, every reference breaks. Use `` and let LX Draft handle the lookup.
Relative paths (`./diagrams/field-plot.png`) are accepted as a fallback — LX Draft matches them by exact path then by basename — but they couple the document to the directory layout. Avoid them in agent output.
Absolute filesystem paths (`C:\...`, `/home/...`) can never resolve in a browser. Don't use them.
### Folder structure
The folder can be flat or have subfolders — both work, because LX Draft matches by filename stem. Filename stems must be unique within the bundle when you use `asset:` references.
```
my-report/ ← name doesn't matter; user picks this folder
├── photo-013435032.jpg ← matches asset:photo-013435032
├── photo-013449261.jpg ← matches asset:photo-013449261
└── pcb-rev-b.png ← matches asset:pcb-rev-b
```
If you have many media files of different types, put them in subfolders for your own organisation — the matcher still works:
```
my-report/
├── photos/
│ ├── photo-013435032.jpg ← still matches asset:photo-013435032
│ └── photo-013449261.jpg
└── diagrams/
└── pcb-rev-b.png ← still matches asset:pcb-rev-b
```
The `asset:` reference does **not** include the subfolder.
### Supported media types
Anything LX Draft can render or link: `png`, `jpg`/`jpeg`, `gif`, `webp`, `svg`, `bmp`, `tiff`, `pdf`, `mp4`, `webm`, `mp3`, `wav`, `ogg`. The MIME type is inferred from the extension.
### What to deliver to the human
> **You'll get the cleanest result if you deliver:**
> 1. The `.lxdoc` file (with `asset:` references, no `document-assets` block).
> 2. A folder of media files matching those references.
>
> The user uploads both via LX Draft's "Document + Archive" import tab — they can pick the folder directly (no zipping needed) or zip it first if their browser doesn't support folder upload.
You don't need to zip anything. Write the files into a folder; LX Draft reads only the files actually referenced by the document and ignores the rest.
If you absolutely cannot deliver media files (e.g. you only have URLs), use plain HTTPS image references. They'll work but won't be embedded — the document depends on the URL staying live.
### Manual base64 embedding (only if you have no other option)
The format is documented for completeness. **Avoid using it from an agent**:
```markdown
```
The `data:` field must be the complete base64 (no line breaks, no truncation). The `size:` field should match the original file's byte length.
## Review markers (do NOT use unless instructed)
The `...` and `...` syntax is for review comments and tracked changes. **Do not generate these manually.** Use the API endpoints if you need to add review data programmatically.
## How to use headings, paragraphs, and emphasis
Engineering documents are read for reference, not narrative. Structure beats prose. Use these tools deliberately:
**Heading levels carry meaning:**
- `## 1 Section` — top-level. Numbered. Examples: Introduction, Scope, Requirements, Test Plan.
- `### 1.1 Subsection` — major topic within a section. Numbered.
- `#### Subtopic` — finer division. Usually unnumbered.
- `##### Detail` and `###### Note` — rare; only for deep technical reference material.
**Don't skip levels.** Going from `##` straight to `####` confuses readers and breaks the table of contents.
**Don't use `#` (H1) inside the body** — the document title is in the frontmatter and rendered as the cover. Body sections start at `##`.
**Paragraphs vs callouts:**
- Plain prose → paragraphs. Most of the document.
- Critical information that must not be missed → `> [!IMPORTANT]` callout.
- A safety warning → `> [!WARNING]`.
- An optional pointer or rule of thumb → `> [!TIP]`.
- A procedural step inside an instruction list → `> [!STEP]`.
- Background context that interrupts the flow → `> [!NOTE]`.
Don't overuse callouts. If every other paragraph is a callout, none of them stand out.
**Emphasis:**
- `**bold**` for terms being defined and absolutely critical phrases. Two or three per page maximum.
- `*italic*` for proper nouns, document titles, and gentle emphasis.
- `` `inline code` `` for identifiers, filenames, signal names, command-line arguments, register names — anything the reader might type or look up literally.
- Don't bold whole sentences. Don't italicise for "this is important" — use a callout instead.
**Lists:**
- Use bullets for unordered facts (features, components, considerations).
- Use numbered lists for ordered steps or where the order matters and you'll refer to step N later.
- Indent with 2 spaces to nest. Don't go deeper than two levels — restructure if you find yourself nesting three deep.
**Tables vs CSV blocks:**
- Pipe tables (`| col | col |`) — short inline tables, 3-6 rows, primarily textual content.
- CSV blocks — engineering data: BOMs, requirements lists, test results, register maps, anything with >6 rows or that has structured columns. Use `csv:requirements`, `csv:bom`, `csv:test-results`, `csv:registers` etc. The label after `csv:` is descriptive; use whatever fits.
## Document structure for engineering documents
A typical LX engineering document has this structure:
```markdown
---
[frontmatter]
---
## 1 Introduction
Brief overview of the document and project.
## 2 Scope
What this document covers.
## 3 [Main content section]
### 3.1 Subsection
Content here.
### 3.2 Another subsection
```csv:requirements
id,description,criticality
REQ-001,System must boot in under 5 seconds,High
REQ-002,Battery life must exceed 8 hours,High
```
## 4 [Another main section]
## Appendix
### A. Additional Information
```
## Three ways to deliver a document to LX Draft
If you're an agent producing a document, you have three options:
### Option 1: Hand the user a .md file
Just write standard markdown. The user clicks "Import Markdown" in LX Draft and uploads the file. LX Draft wraps it in default frontmatter automatically.
### Option 2: Hand the user a complete .lxdoc file
Write the full file with frontmatter. The user clicks "Import Markdown" → "Upload File" and selects the .lxdoc. It imports as-is.
### Option 3: Use the agent-edit API directly
If you have programmatic access to the LX Draft API, use the **stateless**
content-based endpoint. You pass the FULL current document as `content` and the
new section text as `content_to_apply`; the response returns the rebuilt full
document `content` for you to apply (LX Draft no longer stores documents
server-side, so there is no `:id`):
```
POST /api/agent/edit
Content-Type: application/json
{
"content": "",
"section_heading": "3 DFM Review",
"action": "replace", // replace | append | prepend
"content_to_apply": "New markdown content...",
"track_changes": true,
"agent_name": "Your Agent Name"
}
```
Response: `{ ok: true, content: "", section, action, validation }`.
The validator runs server-side; if the edit would make the document invalid you
get a 422 with the issues to correct. Related stateless endpoints:
`POST /api/agent/sections` (`{content}` → list of sections) and
`POST /api/spellcheck` (`{content}`).
## Common mistakes to avoid
Stuff agents have actually done that broke the import or rendering. Don't repeat these:
- **Inventing frontmatter fields** like `layout: "page"`, `page_size: "A4"`, `margins: "standard"`. LX Draft has a fixed schema; unknown fields are silently ignored. If you think you need a layout option, you don't — page geometry is decided by the renderer.
- **Hard-coding numbers in captions.** Write `Components` not `Table 3: Components`. The renderer numbers tables, figures, and equations automatically.
- **Putting the document title in the body** as `# Title`. The title goes in the YAML frontmatter only. Body sections start at `## 1 Section`.
- **Using folder-prefixed image refs** like ``. Use ``. The folder name doesn't survive into the import.
- **Truncating base64 in `document-assets`.** If you find yourself writing a `data:` field by hand, stop. Deliver media as separate files alongside the .lxdoc and let the user import the bundle. LLMs cannot reliably emit hundreds of KB of base64 in one shot — output gets truncated silently.
- **Generating review markers** like `...` or ``. Those are for human review tooling. Agents should never write them manually.
- **Mixing pipe tables and CSV blocks for the same kind of data.** Pick one rule (e.g. "all requirements go in CSV blocks") and stick to it within a document.
- **Skipping heading levels** (`##` → `####`). Breaks the table of contents and the document map.
- **Leaving placeholder text** like "TODO: fill this in" or "[insert diagram here]" in a doc you hand to a human. Either complete it or omit it.
## Validate your output before delivery
LX Draft has a public validation endpoint. Run your `.lxdoc` through it before handing the document to the user — you'll catch malformed YAML, missing required fields, broken asset references, and the rest of the issues listed above.
**From the command line** (Node 18+, no npm install needed; the script lives in the LX Draft repo):
```
node scripts/lint-lxdoc.mjs path/to/your/doc.lxdoc
```
It exits 0 on success and 1 on validation errors, so you can use it in scripts.
**As an HTTP endpoint** (for agents that have internet but not the LX Draft repo):
```
POST https://lxdraft.lx-cloud.com/api/validate-public
Content-Type: text/plain
```
Returns:
```json
{
"valid": true,
"issues": [
{ "rule": "V003", "severity": "warning", "message": "...", "location": "..." }
]
}
```
`valid: false` means there is at least one error-severity issue. Fix all errors before delivery; warnings are advisory.
## Validation rules to follow
Your output will be validated. To avoid rejection:
- Frontmatter must be valid YAML
- `format: "lxd/1.0"` must be present
- Captions must NOT contain hardcoded numbers ("Table 3:" is wrong — write just "Components")
- Don't manually create review markers
- If you reference an `asset:id`, that asset must be in the document-assets block
- Don't use overlapping comment markers (you shouldn't be writing markers anyway)
## Quick checklist before delivering
- [ ] YAML frontmatter is valid and starts with `format: "lxd/1.0"`
- [ ] Title and revision are set
- [ ] Headings use `## 1 Section` format (number + space + name)
- [ ] Tables use CSV blocks for engineering data, pipe tables only for short inline tables
- [ ] No hardcoded numbers in captions
- [ ] Image references either use `asset:id` (with assets block) or external URLs
- [ ] No review markers (`` etc.) unless you're using the API