QMDC: Guide for AI Agents [[qmdc_guide]]
Practical guide to the QMD.md format for AI agents
- version: 3.0
⚠️ Important: In QMD.md, field and object order is strictly preserved as written (insertion order). All parsers guarantee this.
Metamodel [[metamodel: Metamodel]]MetamodelMetamodel Metamodel 89%Describe Metamodel McpTool 88%Discovery Category 80%
Anchor Kinds [[anchor_kinds: text]]textMarkdown to Graph Explanation 79%Object SyntaxConcept 78%Metamodel Metamodel 78%
| kind | definition |
|---|---|
| Syntax | How QMD.md text maps to structure — objects, fields, arrays, text blocks, data types |
| Reference | The linking mechanism — [[#id]] and its variants |
| Workspace | Multi-file project structure — namespaces, cross-file resolution |
| Validation | Error detection, error types, pre-commit checks |
| CLI | The qmdc commands — parse, rebuild, lint, query, validate |
Link Roles [[link_roles: text]]textMetamodel Metamodel 83%
| role | meaning |
|---|---|
about |
This section describes or explains X |
depends |
This anchor relies on another anchor at runtime |
What Good QMD.md Looks Like [[good_qmdc: NarrativeDoc]]NarrativeDocGuide McpResource 85%Content Generator ContentGenerator 83%Write Your First QMD.md File Tutorial 82%
Principles [[principles: text]]textHuman Text, Machine Structure Explanation 74%Object SyntaxConcept 73%Content Generator ContentGenerator 71%
A good QMD.md document starts with real things in your domain — not document structure. Your headings should be Parsers, Contracts, Modules — not Sections, Examples, Pages. Those real things are your anchors. Everything else — explanations, examples, walkthroughs — is narrative that links back to anchors via about.
Keep anchor kinds coarse. 5–15 is the sweet spot. If you have 30 kinds, you're modeling the document, not the domain. If you have 3, you probably merged things that deserve separate identity.
Use depends between anchors to show the dependency chain. Use about on narrative sections to say what they explain. If a section isn't about any anchor, ask yourself why it exists.
IDs should be guessable — rust_parser, not parser_001. Types should be PascalCase domain nouns — Module, not ModuleSection. Text fields for anything longer than a line.
When you're unsure which things are anchors, what the right granularity is, or whether two concepts should be merged or split — stop and ask. Propose your anchor list and dependency chain to the human, get explicit approval before writing the document. A wrong metamodel is worse than no metamodel: it creates structure that actively misleads. Iterate on the anchors first, write content second.
Syntax [[syntax: Syntax]]SyntaxInvalid Reference Syntax DiagnosticRule 67%Components NarrativeDoc 67%Guide McpResource 67%
Overview [[syntax_overview: text]]textHuman Text, Machine Structure Explanation 84%QMD.md Format Specification Namespace 83%Why QMDC Explanation 82%
QMD.md is a format for writing structured data in Markdown. You write regular Markdown with headings and lists, and the parser turns it into a graph of objects with references.
Core idea:
- Headings (
##,###) = objects - Lists (
- key: value) = object fields [[id]]in a heading = object identifier[[#id]]in a value = reference to another object
Creating Objects [[creating_objects: text]]textWrite Your First QMD.md File Tutorial 81%Field Key CompletionContext 74%Heading SyntaxConcept 73%
## User [[alice]]
- name: Alice
- age: 30
- active: true
- score: 95.5
Result: object with __id: "alice", fields name, age, active, score.
Heading variants:
## Title [[id]]— explicit ID## Title [[id: Kind]]— with type (for validation)## Title [[:Kind]]— Kind only, ID auto-generated## Title— auto-generate ID from Title (lowercase, spaces →_)## [[id]] Title— ID before Title (also valid)
Data Types [[data_types: text]]textData Type SyntaxConcept 73%Type Mismatch ValidationError 70%Array SyntaxConcept 67%
## Config [[config]]
- text: Hello World # string
- number: 42 # number
- float: 3.14 # number
- bool: true # boolean (lowercase only)
- empty: null # null
- array: [a, b, c] # array (YAML notation)
Important: true/false/null must be lowercase only. True, FALSE, yes, no are strings!
Text Fields (Multiline Content) [[text_fields: text]]textField SyntaxConcept 74%Object SyntaxConcept 73%Comment SyntaxConcept 68%
## Article [[article]]
### Content [[content]]
This is **multi-line** content.
You can use Markdown formatting here:
- Lists work
- Bold, italic, etc.
Result: field content with multiline text (Markdown preserved).
References in text fields: References [[#id]] inside text fields are validated for existence (broken links detected), but remain part of the text — they are not resolved into objects.
When to Use Heading-Syntax Text Fields [[heading_syntax_rule: text]]textField SyntaxConcept 79%Dangling Field ValidationError 73%Object SyntaxConcept 72%
Rule of thumb: any field that could plausibly be more than a short scalar should use ### FieldName [[field: text]] heading syntax instead of - key: value.
Use heading-syntax (### Field [[field: text]]) for:
- descriptions, responsibilities, rationale
- assumptions, deltas, deferred items
- public surfaces, summaries, explanations
- anything that might span multiple lines or contain markdown formatting
Keep as inline fields (- key: value) for:
- status, language, repo, version
- references (
- ref: [[#something]]) - short scalars (names, IDs, booleans, numbers)
Example:
## Feature [[feature1: Feature]]
- status: planned
- priority: high
### Description [[description: text]]
This feature adds support for semantic search across
the entire workspace. It combines keyword matching with
dense vector embeddings for hybrid retrieval.
### Rationale [[rationale: text]]
Users need to find relevant objects without knowing
exact file paths or object IDs.
YAML Multiline Strings [[yaml_multiline: text]]textField SyntaxConcept 95%YAML Block SyntaxConcept 71%Array SyntaxConcept 69%
## Config [[config]]
- description: |
This is a multiline string
using YAML pipe syntax.
References like [[#something]] are NOT parsed here.
Important: References [[#id]] inside YAML pipe | blocks are NOT parsed by the QMDC parser — content remains plain text. This means they won't create edges in the graph or trigger broken_link errors in the target document.
Nested Objects [[nested_objects: text]]textWrite Your First QMD.md File Tutorial 77%Reference SyntaxConcept 72%Broken Parent ValidationError 68%
## User [[user]]
- name: Alice
### Address [[address]]
- street: Main St
- city: NYC
Result: two objects:
userwith fieldaddress: "[[#address]]"(reference)addresswith fieldsstreet,cityand__parent: "[[#user]]"
Key distinction: QMD.md has no nested JSON objects! Everything is flat, connections via references.
Arrays [[arrays: text]]textArray SyntaxConcept 81%Write Your First QMD.md File Tutorial 77%
Compact form (YAML):
- tags: [react, nodejs, typescript]
- ports: [8080, 8081, 8082]
Multiline YAML form:
- long_list: [
first_very_long_item,
second_very_long_item,
third_very_long_item
]
Expanded form (lists):
### Members [[members]]
- Alice
- Bob
- Charlie
Comma-separated references (no outer brackets):
- deps: [[#auth]], [[#db]], [[#cache]]
Syntax choice is preserved in __syntax for lossless round-trip.
Object Arrays via Subheadings [[object_arrays_subheadings: text]]textBroken Parent ValidationError 83%Reference SyntaxConcept 80%Array SyntaxConcept 76%
## Team [[team]]
### Members [[members: [User]]]
#### Alice [[alice]]
- role: admin
- email: alice@ex.com
#### Bob [[bob]]
- role: dev
- email: bob@ex.com
Result:
- Object
teamwith fieldmembers: ["[[#alice]]", "[[#bob]]"] - Two objects
aliceandbobwith__parent: "[[#team]]"and__parent_field: "members"
Object Arrays via Tables [[object_arrays_tables: text]]textArray SyntaxConcept 73%
## Team [[team]]
### Members [[members: [User]]]
| name | role | email |
| ------- | --------- | -------------- |
| Alice | admin | alice@ex.com |
| Bob | developer | bob@ex.com |
| Charlie | designer | charlie@ex.com |
Result: same thing — 3 objects with auto-generated IDs.
When to use tables:
- ✅ Many homogeneous objects with simple fields
- ✅ Tabular data (configurations, lists)
- ❌ Objects with nested structures
- ❌ Objects with multiline fields
Embedded YAML Blocks [[yaml_blocks: text]]textYAML Block SyntaxConcept 87%Field SyntaxConcept 67%Human Text, Machine Structure Explanation 67%
QMD.md supports embedded YAML blocks for migrating existing configurations.
## Server [[server]]
### Configuration [[config: yaml]]
```yaml
database:
host: localhost
port: 5432
replicas: 3
```
Result: field config contains parsed YAML as a nested object. Data accessible directly: server.config.database.host.
If YAML is invalid — field is saved as text with __parse_error, graph continues loading.
Auto-Detection Rules [[auto_detection: text]]textField SyntaxConcept 86%Multiple Definitions In Heading ValidationError 80%Type Mismatch ValidationError 76%
When the parser encounters a heading-syntax field, it determines the type:
- Has child subheadings with
[[field_id]]? → Object array - Has lists with
- key: value(valid keys)? → Nested object - Has lists WITHOUT colons
- value? → Primitive array - Has text but no valid lists? → Text field
When in doubt — explicitly specify the type:
### Description [[desc: text]] # text field
### Config [[config: object]] # nested object
### Items [[items: [Item]]] # object array
### Tags [[tags: array]] # primitive array
System Types [[system_types: text]]textObject SyntaxConcept 85%Explicit System Type ValidationError 76%Dump Index Command 75%
- about: Workspace
QMD.md uses system types with the __ prefix:
| Type | Description |
|---|---|
__Workspace |
Root project container |
__Namespace |
Logical grouping (subfolder) |
__Document |
Document container (auto-created if text blocks exist) |
__TextBlock |
Unstructured text (heading without [[id]] and without fields) |
User types: User, Config, Table, etc. (PascalCase).
Filtering: objects.filter(obj => !obj.__kind?.startsWith("__")) — business objects only.
__Document and __TextBlock are created by the parser automatically only. Explicit declaration [[id: __Document]] or [[id: __TextBlock]] is an error.
Lossless Round-Trip [[round_trip: text]]textObject SyntaxConcept 95%Parse Command 81%Parsing Algorithm Algorithm 78%
QMD.md supports lossless round-trip: QMD.md → JSON → QMD.md without data loss.
The parser stores metadata in system fields:
| Field | Description |
|---|---|
__types |
Field data types (string, number, boolean, null, array) |
__syntax |
Notation syntax (yaml_array, markdown_list, table, headers, yaml_multiline, yaml_object) |
__level |
Heading level (1-6) |
__has_explicit_id |
false if ID was auto-generated (absent if ID is explicit) |
__comments |
Comments with semantic binding to fields |
__labels |
Original heading labels for fields |
__code_fences |
Metadata about fenced code blocks in TextBlock |
__comments format: array of {after: "anchor", content: "raw markdown"}.
after: "__self"— comment on the object itselfafter: "field_name"— comment after a fieldcontent— raw markdown slice, parser does not interpret contents
__syntax values and rebuild:
| __syntax | Heading on rebuild |
|---|---|
multiline_text |
### Label [[field: text]] |
markdown_list |
### Label [[field: array]] |
yaml_object |
### Label [[field: yaml]] |
headers |
### Label [[field: [Kind]]] |
table |
### Label [[field: [Kind]]] |
Reference [[reference: Reference]]ReferenceReference SyntaxConcept 86%Show References ExtCommand 78%Invalid Reference Syntax DiagnosticRule 76%
- depends: Syntax
Overview [[reference_overview: text]]textReference SyntaxConcept 83%Write Your First QMD.md File Tutorial 76%Why QMDC Explanation 73%
References connect objects in the graph. They start with # inside [[...]].
Basic References [[basic_refs: text]]textReference SyntaxConcept 66%Write Your First QMD.md File Tutorial 65%Validation Errors SyntaxConcept 65%
## Order [[order1]]
- customer: [[#alice]]
- product: [[#laptop]]
Array References [[array_refs: text]]textArray SyntaxConcept 76%Reference SyntaxConcept 70%Write Your First QMD.md File Tutorial 69%
YAML reference arrays and expanded form:
## User [[alice]]
- roles: [[[#admin]], [[#dev]]]
## User [[bob]]
### Roles [[roles]]
- [[#dev]]
- [[#viewer]]
Field References [[field_refs: text]]textReference SyntaxConcept 92%Broken Parent ValidationError 77%Object SyntaxConcept 75%
References use full hierarchical dot-paths to target child objects and their fields:
## Team [[team]]
### Members [[members: [User]]]
#### Alice [[alice]]
- role: admin
## Report [[report]]
- author: [[#team.members.alice]]
- author_role: [[#team.members.alice.role]]
Syntax: [[#parent.field.child]] — hierarchical dot-path to a child object. [[#parent.field.child.field_name]] — references a field on that object.
For an array of results use *: [[#*object.array[field=value]]]
Kind-Qualified References [[kind_refs: text]]textAmbiguous Reference DiagnosticRule 82%Hover LSPFeature 74%Reference SyntaxConcept 74%
When two objects have the same ID but different Kind, the reference must specify Kind:
## Users [[users: Table]]
- name: users
## Users [[users: Entity]]
- type: domain_model
- table_ref: [[#Table:users]] # reference to table
- entity_ref: [[#Entity:users]] # reference to entity
- ambiguous: [[#users]] # ❌ ERROR!
Cross-Namespace References [[namespace_refs: text]]textReference SyntaxConcept 84%Reference Resolution Algorithm 77%Ambiguous Reference ValidationError 77%
- about: Workspace
## API Service [[api_service: Service]]
- database: [[#storage:users]] # different namespace
- user_table: [[#storage:Table:users]] # namespace + Kind
Full format: [[#workspace:namespace:Kind:id]]
All components are optional except id:
[[#id]]— local reference (current namespace)[[#Kind:id]]— with type (collision resolution)[[#namespace:id]]— different namespace[[#namespace:Kind:id]]— full form for cross-namespace
Reference Philosophy [[ref_philosophy: text]]textBroken Link ValidationError 78%Write Your First QMD.md File Tutorial 78%Reference SyntaxConcept 76%
- about: Validation
Reference problems are warnings, not errors. The graph continues loading:
- Object not found → reference remains a string, warning
- ID collision without Kind → unresolved reference, warning
- Broken links don't break the entire graph
Where References Are Not Parsed [[refs_not_parsed: text]]textReference SyntaxConcept 81%Field SyntaxConcept 78%Write Your First QMD.md File Tutorial 73%
References [[#id]] inside text fields are validated for existence (broken links detected), but remain part of the text — they are not resolved into objects.
References [[#id]] inside YAML pipe | blocks are NOT parsed — content remains plain text. They won't create edges in the graph or trigger broken_link errors.
References inside inline code (`<span class="broken-link">[[#ref]]</span>`) are NOT parsed.
References inside fenced code blocks with example modifier are NOT parsed.
Typed Edges [[typed_edges: text]]textReference SyntaxConcept 97%Markdown to Graph Explanation 77%QMDC for Agents Explanation 75%
Every edge in the graph carries an edge_type — a user-defined string describing the relationship.
For inline fields, edge_type equals the field name:
## Payment [[payment: Service]]
- depends: [[#auth]]
- database: [[#payments_db]]
Creates edges: (payment, depends, auth, edge_type="depends") and (payment, database, payments_db, edge_type="database").
For text field preambles, edge_type comes from the preamble key, while source_field is the text field name:
### Rationale [[rationale: text]]
- about: [[#checkout_flow]]
- depends: [[#order_svc]], [[#payment_svc]]
This service handles payments...
Creates edges:
(payment, source_field="rationale", checkout_flow, edge_type="about")(payment, source_field="rationale", order_svc, edge_type="depends")(payment, source_field="rationale", payment_svc, edge_type="depends")
Preamble rules:
- Must start at the beginning of the text field value
- ALL list items must be valid
- key: [[#ref]]fields (all-or-nothing) - Separated from the rest of the text by a blank line
- Preamble lines stay in the raw text value — no stripping
Edges table schema:
edges (source_id, source_field, target_id, edge_type, __workspace)
UNIQUE(source_id, source_field, target_id, edge_type)
Querying typed edges:
# All "depends" relationships
qmdc query . "SELECT s.__id, t.__id FROM edges e JOIN objects s ON e.source_id = s.__global_id JOIN objects t ON e.target_id = t.__global_id WHERE e.edge_type = 'depends'"
# Preamble edges (where edge_type differs from source_field)
qmdc query . "SELECT s.__id, e.source_field, t.__id, e.edge_type FROM edges e JOIN objects s ON e.source_id = s.__global_id JOIN objects t ON e.target_id = t.__global_id WHERE e.edge_type != e.source_field"
Workspace [[workspace: Workspace]]WorkspaceWorkspace SyntaxConcept 82%Content Generator ContentGenerator 82%Guide McpResource 81%
- depends: Reference
Structure [[workspace_structure: text]]textWorkspace SyntaxConcept 93%Content Generator ContentGenerator 82%Build a Small Workspace HowTo 80%
A workspace is a folder with multiple QMD.md files that can reference each other.
my-project/
├── readme.qmd.md # Workspace root (__Workspace)
├── users.qmd.md
├── storage/
│ ├── readme.qmd.md # Namespace "storage" (__Namespace)
│ ├── tables.qmd.md
│ └── indexes.qmd.md
└── api/
├── readme.qmd.md # Namespace "api" (__Namespace)
└── endpoints.qmd.md
Defining a Workspace [[defining_workspace: text]]textWorkspace In Wrong File DiagnosticRule 80%Workspace SyntaxConcept 80%Content Generator ContentGenerator 71%
File readme.qmd.md in project root:
# My Project [[myproject:__Workspace]]
- description: Project description
- version: 1.0
__Workspace in Kind indicates this is the workspace root.
Defining a Namespace [[defining_namespace: text]]textWorkspace SyntaxConcept 79%Build a Small Workspace HowTo 69%Semantic Storage Schema NarrativeDoc 66%
File storage/readme.qmd.md:
# Storage Layer [[storage:__Namespace]]
- description: Database schema and storage
__Namespace in Kind indicates this folder is a namespace.
All files in the folder automatically inherit workspace and namespace.
Cross-File References [[cross_file_refs: text]]textWorkspace SyntaxConcept 80%Markdown to Graph Explanation 74%Build a Small Workspace HowTo 74%
- about: Reference
File api/endpoints.qmd.md:
## Get Users [[get_users: Endpoint]]
- method: GET
- path: /api/users
- returns: [[#storage:Table:users]]
File storage/tables.qmd.md:
## Users [[users: Table]]
- columns: [[[#id_col]], [[#email_col]]]
The parser automatically:
- Finds all objects in all files
- Indexes them by
namespace:Kind:id - Validates all references
- Reports broken links
Object Metadata [[object_metadata: text]]textWorkspace SyntaxConcept 90%Build a Small Workspace HowTo 79%Dump Index Command 78%
When parsing a workspace, each object gets:
{
"__id": "users",
"__kind": "Table",
"__file": "storage/tables.qmd.md",
"__line": 5,
"__workspace": "[[#myproject]]",
"__namespace": "[[#storage]]",
"name": "users"
}
__file— file path__line— line number__workspace— reference to__Workspaceobject__namespace— reference to__Namespaceobject (ornullfor root)
Dynamic Blocks [[dynamic_blocks: text]]textDynamic Block SyntaxConcept 94%Query Your Markdown Graph HowTo 85%Run SQL Query ExtCommand 84%
- about: CLI
Dynamic blocks — a mechanism for executing SQL queries against workspace data and displaying results in documents.
Reusable query object:
## Get Tables [[get_tables: Query]]
- sql: SELECT __id, __label FROM objects WHERE __kind = 'Table'
Reference to Query:
Use a table code block with query: [[#get_tables]] to reference a Query object.
Inline SQL:
Use a table code block with sql: SELECT __id, __kind FROM objects for inline queries.
Scope parameter:
scope: workspace(default) — filter by current workspacescope: all— data from all workspaces
Block types: table (HTML table), diagram (D2/Mermaid, future), chart (charts, future).
Validation [[validation: Validation]]ValidationContent Generator ContentGenerator 86%Validate Workspace ExtCommand 77%Type Mismatch ValidationError 74%
Commands [[validation_commands: text]]textWorkspace Validate Command 88%Validate a Document HowTo 88%Content Generator ContentGenerator 87%
- about: CLI
# 1. Check file syntax
qmdc parse -i file.qmd.md > /dev/null || exit 1
# 2. Validate workspace (returns JSON array of errors)
qmdc workspace validate ./my-project
# If no errors — returns []
# If errors exist — returns array of error objects, exit 1
# 3. Check all files in a directory
for file in $(find ./docs -name "*.qmd.md"); do
qmdc parse -i "$file" > /dev/null || echo "❌ $file"
done
Error Format [[validation_error_format: text]]textWorkspace Validate Command 92%Validate Workspace ExtCommand 86%Validation Errors SyntaxConcept 86%
qmdc workspace validate returns a JSON array:
[
{
"type": "broken_link",
"message": "Object 'xyz' not found",
"file": "file.qmd.md",
"line": 5,
"objectId": "abc",
"reference": "[[#xyz]]",
"severity": "error"
}
]
Error Types [[validation_error_types: text]]textValidation Errors SyntaxConcept 86%Validate a Document HowTo 81%Workspace Validate Command 81%
| Code | Description |
|---|---|
broken_link |
Reference [[#id]] to a non-existent object |
duplicate_id |
Two objects with the same Kind:Id in one namespace |
ambiguous_reference |
Reference [[#id]] could point to multiple objects |
broken_parent |
Parent object not found for dot-ID declaration |
ambiguous_field_reference |
Dot-path resolves both as an object ID and as a field-path |
nested_workspace |
Workspace inside another workspace (forbidden) |
type_mismatch |
Explicit type [[field: Kind]] doesn't match content structure |
structured_in_textblock |
Structured element inside __TextBlock |
multiple_definitions |
Heading contains more than one [[...]] |
ordered_list_in_array |
Numbered list in heading-syntax array (bullet lists only) |
nested_subitems |
Nested lists - key:\n - item (forbidden) |
explicit_system_type |
Explicit declaration of [[id: __Document]] or [[id: __TextBlock]] |
mixed_field_keys |
Mix of valid and invalid keys in one object |
Pre-Commit Checklist [[pre_commit_checklist: text]]textContent Generator ContentGenerator 82%Object SyntaxConcept 78%Write Your First QMD.md File Tutorial 76%
Before saving a QMD.md file:
- ✅ All objects have
[[id]](or are explicitly auto-generated) - ✅ All field keys are valid (
[a-zA-Z][a-zA-Z0-9_]*) - ✅
true/false/nullin lowercase - ✅ No patterns like
- key:\n - item(use YAML or heading-syntax) - ✅ No numbered lists in heading-syntax arrays
- ✅ Verified via
qmdc parse -i file.qmd.md - ✅ If workspace — verified via
qmdc workspace validate . - ✅ All references exist
- ✅ No ambiguous references (Kind specified on collision)
What Is Normal [[validation_normals: text]]textWrite Your First QMD.md File Tutorial 71%Validate a Document HowTo 69%Content Generator ContentGenerator 68%
- ⚠️ Broken links in examples (users, orders, User, Table) — normal
- ⚠️ Broken links in types (text, id, Structure, Method) — normal
- ⚠️ References NOT parsed in inline code (
`<span class="broken-link">[[#ref]]</span>`) — normal - ⚠️ References NOT parsed in fenced code blocks with
examplemodifier — normal - ⚠️ References NOT parsed in YAML multiline
|blocks — normal
The example modifier for code blocks:
To show QMD.md code examples in documentation, use the example modifier:
{
"__id": "user",
"__label": "User",
"name": "Alice",
"profile": "[[#user_profile]]" // ← this reference is not parsed
}
In json example blocks, [[#id]] references remain text and don't create broken_link errors.
CLI [[cli: CLI]]CLI
- depends: Workspace, Validation
Parse — QMD.md → JSON [[cmd_parse: text]]textParse Command 93%Rebuild Command 83%Python Parser Parser 78%
Converts QMD.md to JSON.
# File → stdout
qmdc parse -i file.qmd.md
# Syntax check (exit 1 on error)
qmdc parse -i file.qmd.md > /dev/null
# Stdin → stdout
echo "## Test [[test]]" | qmdc parse
# Without metadata
qmdc parse -i file.qmd.md --no-comments --no-syntax
# Rust: output format (minimal, standard, full)
qmdc parse -i file.qmd.md --format full
Rebuild — JSON → QMD.md [[cmd_rebuild: text]]textRebuild Command 94%Parse Command 86%Python Parser Parser 80%
Converts JSON back to QMD.md (lossless round-trip).
qmdc rebuild -i data.json
qmdc rebuild -i data.json -o doc.qmd.md
Formatting (parse → rebuild) [[cmd_lint: text]]textRebuild Command 93%Formatting LSPFeature 81%Parse Command 73%
There is no separate lint command. Pipe through parse → rebuild to format QMD.md to canonical form:
# Canonical formatting (lossless round-trip)
qmdc parse -i doc.qmd.md | qmdc rebuild
Workspace Parse [[cmd_workspace_parse: text]]textWorkspace Parse Command 89%Parse Workspace ExtCommand 84%Get Tree McpTool 80%
- about: Workspace
Parses entire workspace to JSON.
qmdc workspace parse ./my-project -o workspace.json
qmdc workspace parse ./my-project --format full
Workspace Validate [[cmd_workspace_validate: text]]textWorkspace Validate Command 95%Validate Workspace ExtCommand 89%Diagnostics McpResource 84%
- about: Workspace, Validation
Validates workspace — broken links, duplicate IDs, ambiguous references.
# Returns JSON array of errors ([] if all ok)
qmdc workspace validate ./my-project
# Exit code: 0 if no errors, 1 if errors exist
Query — SQL Queries [[cmd_query: text]]textQuery Command 93%Run SQL Query ExtCommand 93%Query Your Markdown Graph HowTo 89%
- about: Workspace
SQL queries against workspace via SQLite.
# Find all objects of type Table
qmdc query ./my-project "SELECT __id, __kind, __file FROM objects WHERE __kind = 'Table'"
# Find all references to an object
qmdc query ./my-project "SELECT source_id, field_name FROM edges WHERE target_id = 'users'"
# Count objects by type
qmdc query ./my-project "SELECT __kind, COUNT(*) as count FROM objects GROUP BY __kind"
# Query via Query object (reference to [[id:Query]] in workspace)
qmdc query ./my-project "#all_services"
# JSON output format
qmdc query ./my-project "SELECT * FROM objects LIMIT 10" --format json
Available tables:
objects— all objects (__id,__kind,__label,__file,__line,dataJSON)edges— all graph edges (source_id,source_field,target_id,edge_type,__workspace)
Parsers [[parsers: text]]textComponents NarrativeDoc 88%Overview NarrativeDoc 88%Parsers Namespace 80%
Three parser implementations, all providing the same qmdc CLI:
- Python (
qmdc-py) — reference implementation,uv pip install -e ./qmdc-py - TypeScript (
qmdc-ts) — for Node.js/browser,npm install - Rust (
qmdc-rs) — production, LSP server,cargo build --release
Agent Workflow [[agent_workflow: text]]textParse Workspace ExtCommand 82%Content Generator ContentGenerator 80%Guide McpResource 79%
- about: Validation
# 1. Create/edit file
echo "## User [[user]]
- name: Alice" > users.qmd.md
# 2. Check syntax
qmdc parse -i users.qmd.md > /dev/null
if [ $? -ne 0 ]; then
echo "Syntax error!"
exit 1
fi
# 3. If workspace — validate references
qmdc workspace validate .
if [ $? -ne 0 ]; then
echo "Validation error!"
exit 1
fi
# 4. Format (optional)
qmdc lint -i users.qmd.md -o users.qmd.md
Common Errors [[common_errors: NarrativeDoc]]NarrativeDocError Envelope NarrativeDoc 68%Validate a Document HowTo 67%Content Generator ContentGenerator 66%
- about: Validation, Syntax
Missing Explicit ID [[err_missing_id: text]]textWrite Your First QMD.md File Tutorial 83%Heading SyntaxConcept 76%Broken Parent ValidationError 74%
- about: Syntax
## User
- name: Alice
ID is auto-generated from Title → __id: "user", but [[id]] is not explicitly set. Rebuild format may change. Always use explicit IDs:
## User [[user]]
- name: Alice
Invalid Field Key [[err_invalid_key: text]]textField SyntaxConcept 87%Invalid Map Entry ValidationError 74%Write Your First QMD.md File Tutorial 74%
- about: Syntax
## User [[user]]
- First Name: Alice # space in key → markdown text!
- my-key: value # hyphen in key → markdown text!
- 2name: value # starts with digit → markdown text!
Valid key: [a-zA-Z][a-zA-Z0-9_]* (starts with letter, only letters/digits/underscore).
If an object mixes valid and invalid keys — error mixed_field_keys is generated.
Correct:
## User [[user]]
- first_name: Alice
- firstName: Alice
- my_key: value
Case-Sensitive Types [[err_case_types: text]]textData Type SyntaxConcept 77%Write Your First QMD.md File Tutorial 73%
- about: Syntax
- active: True # will be string "True"!
- empty: NULL # will be string "NULL"!
Must be lowercase:
- active: true # boolean
- empty: null # null
Broken Link [[err_broken_link: text]]textBroken Link DiagnosticRule 80%Validate a Document HowTo 77%Write Your First QMD.md File Tutorial 72%
- about: Validation, Reference
## Order [[order1]]
- user: [[#nonexistent]] # object doesn't exist!
How to find:
qmdc workspace validate .
# Returns JSON with broken_link error
Check object existence before creating a reference:
qmdc query . "SELECT __id FROM objects WHERE __id = 'nonexistent'"
# → empty result = object not found
Ambiguous Reference [[err_ambiguous_ref: text]]textAmbiguous Reference DiagnosticRule 78%Validate a Document HowTo 70%Hover LSPFeature 70%
- about: Validation, Reference
## Users [[users: Table]]
...
## Users [[users: Entity]]
...
## Order [[order]]
- ref: [[#users]] # ambiguous!
Fix by specifying Kind:
- table_ref: [[#Table:users]]
- entity_ref: [[#Entity:users]]
Nested Lists in Fields [[err_nested_lists: text]]textArray SyntaxConcept 81%Write Your First QMD.md File Tutorial 77%Validate a Document HowTo 74%
- about: Syntax
## Config [[config]]
- items:
- first
- second
Fix with YAML array or heading-syntax:
## Config [[config]]
- items: [first, second]
Or:
## Config [[config]]
### Items [[items: array]]
- first
- second
Numbered Lists in Arrays [[err_numbered_lists: text]]textOrdered List In Array ValidationError 90%Array SyntaxConcept 66%
- about: Syntax
### Steps [[steps: array]]
1. First step
2. Second step
Only bullet lists are allowed:
### Steps [[steps: array]]
- First step
- Second step
Practical Examples [[examples: NarrativeDoc]]NarrativeDocContent Generator ContentGenerator 74%Guide McpResource 72%Recording Demos NarrativeDoc 69%
Database Schema [[uc_database: text]]textQuery Category 74%Run SQL Query ExtCommand 70%Query Your Markdown Graph HowTo 66%
Tables with columns, indexes, and references:
## Users [[users: Table]]
### Columns [[columns: [Column]]]
| name | type | nullable |
| ----- | ------- | -------- |
| id | bigint | false |
| email | varchar | false |
| age | int | true |
### Indexes [[indexes: [Index]]]
#### Email Index [[email_idx]]
- columns: [[[#users.columns[name=email]]]]
- unique: true
Microservice Architecture [[uc_microservices: text]]text
API Gateway with routes and dependencies:
## API Gateway [[gateway: Service]]
- port: 8080
- dependencies: [[[#user_service]], [[#order_service]]]
### Routes [[routes: [Route]]]
#### Users [[users_route]]
- path: /api/users/*
- target: [[#user_service]]
- methods: [GET, POST, PUT]
Configuration [[uc_config: text]]text
- about: Syntax
Database and cache settings:
## Production [[prod: Config]]
### Database [[db]]
- host: db.prod.example.com
- port: 5432
- ssl: true
### Cache [[cache]]
- provider: redis
- nodes: [redis1.prod, redis2.prod, redis3.prod]
- ttl: 3600