Reference
Markdown reference
Every card field, link form, map setting, fallback, normalization rule, and silent failure md2hd understands.
A map is one or more markdown files. Each YAML frontmatter block becomes a node, shown as a card. Relationships and wikilinks become edges, shown as connections. Prose after a block becomes that node’s note.
The parser does not reject invalid input. A malformed block or misspelled setting still produces a result, often the wrong one. Use the silent-failure table when the map draws differently from what you wrote.
File structure
One file may contain the entire map:
---
type: map
title: Partnerships
---
---
id: riverside-council
type: organization
title: Riverside City Council
---
The note for this card.
---
id: dana-whitfield
type: person
title: Dana Whitfield
---
The note for Dana.
A line containing --- opens a block only when the next non-blank line looks
like a YAML key. The key must start with a letter or underscore and have a space
or line ending after its colon.
| Next line | Opens a block? |
|---|---|
id: acme | Yes |
rel: | Yes |
_id: acme | Yes |
id:acme | No |
2026: note | No |
Prose before the first block is dropped. A --- followed by prose stays inside
the current note as a horizontal rule. *** and ___ never open blocks.
Node fields
All fields are optional, but an explicit id, type, and title avoid
surprising fallbacks.
---
id: acme
type: organization
title: Acme Corp
subtitle: Municipal · 9 members
description: The account at the center of this map.
weight: lead
tags: [prospect, warm]
---
Everything after the block is the note. Markdown renders in the card panel.
| Field | Effect |
|---|---|
type | Card category. Defaults to note. map is reserved for configuration. |
id | Link target. Defaults to a slug of the title, then a file-and-block fallback. |
title / name | Card name. Defaults to the first heading anywhere in the note, then Untitled. |
subtitle | One line under the card title. |
description | Opening paragraph in the card panel. |
weight | Visual emphasis from 1 to 5. |
tags | Chips on the card and in the panel. Nested arrays are flattened. |
rel / rels / links | Named connections. |
| Any other key | A wikilink value becomes a connection. Other scalar values become metadata. |
Nested metadata objects do not render. Strings, numbers, dates, booleans, and arrays do.
IDs and slugging
IDs, titles used as fallback IDs, and both ends of every link pass through the same slug function:
trim
lowercase
remove characters outside A-Z, a-z, 0-9, underscore, space, and hyphen
turn spaces and underscores into hyphens
trim leading and trailing hyphens
| Input | Result |
|---|---|
my_node | my-node |
Acme Corp. | acme-corp |
Foo, em dash, Bar | foobar |
Ünïcode Name | ncode-name |
Accented and non-Latin letters are removed, not transliterated. Give those
titles an explicit ASCII id.
Duplicate IDs do not merge. Both cards draw, but only the later card is reachable by link. Treat a duplicate ID as an error.
Weight
Weight changes how strongly a card draws. It does not change the graph.
| Value | Weight |
|---|---|
lead, critical, primary, key | 5 |
major, high | 4 |
normal, medium, default | 3 |
minor, low | 2 |
faint, background, trivial | 1 |
Numbers and numeric strings are rounded and clamped to 1 through 5. An unrecognised word silently becomes 3.
Map configuration
Use exactly one block with type: map:
---
type: map
title: Partnerships
layout: LR
types:
organization: { label: Organization, color: "#4A9BFF" }
person: "#3FC8D4"
inverse:
works_at: employs
symmetric: [knows]
---
| Field | Effect |
|---|---|
title | Map name. |
layout | Exactly LR for left to right. Every other value gives top to bottom. |
types | Label and color for each node type. A color string is accepted as shorthand. |
inverse | Pairs two labels that describe the same directed relationship from opposite ends. |
symmetric | Labels with no direction. |
Those are the only configuration fields read. Plausible keys such as
direction, colors, palette, and rankdir do nothing.
Unlisted types receive palette colors in encounter order. A map needs no custom colors to remain readable.
Two map blocks do not merge. The last one replaces the earlier configuration.
Four ways to connect nodes
A rel map
rel:
employs: [dana, marcus]
runs: records-portal
A list of single-key maps also works:
rel:
- employs: dana
- runs: records-portal
A list of bare targets uses the label links:
rel: [dana, marcus]
A wikilink in frontmatter
Any non-reserved frontmatter key whose value is a wikilink becomes a named connection:
works_at: "[[acme]]"
attendees: ["[[dana]]", "[[marcus]]"]
An inline field in the note
owns:: [[records-portal]]
The label must start with a letter. Letters, numbers, underscores, and hyphens may follow.
A bare wikilink in the note
Talked to [[dana]] about the rollout.
This creates an untyped mentions connection. Use it only when “mentioned”
describes the relationship well enough. [[target|alias]] links to target;
the alias changes only displayed text.
A target with no matching ID becomes a gray dashed unresolved card. A self-link is dropped.
Direction normalization
Connections are normalised before they draw.
Passive labels flip
A label ending in by, _by, or -by loses that suffix and swaps its ends.
For example, portal owned_by dana becomes dana owned portal.
Declared inverse labels flip
If inverse[label] exists, md2hd replaces the label with its paired value and
swaps the ends.
inverse:
works_at: employs
dana works_at acme becomes acme employs dana.
Normalization runs up to three rounds. Rules can chain. Do not make an inverse
value end in _by, because the inverse rule and passive rule will both flip it.
Equivalent connections collapse
Connection identity uses source, target, and a simple stem of the label. The
stem lowercases, turns spaces, underscores, and hyphens into spaces, then strips
a trailing ed, s, or d.
Between the same two nodes, owns, owned, and own collapse to one
connection. The first spelling remains. Irregular verbs such as knew and
knows do not collapse.
The reverse voice is kept
One connection can read differently from each end. An organization employs a
person; that person works at the organization. md2hd keeps both labels when
they were written from opposite ends or declared under inverse.
Inverse labels are global. If assigned_to is paired with owns_action, every
owns_action connection in the map receives assigned to as its reverse
voice. Use different labels when the same word carries different meanings.
Symmetric connections
Labels under symmetric have no direction. Mirrored connections with the same
stem also fold into one symmetric connection. They draw without an arrowhead
and with a chevron at each end.
Bare mentions defer
A mentions connection is dropped when a named relationship already connects
the same two nodes in either direction.
What appears on the canvas
Open a card to focus it. Incoming connections line up on the left and outgoing connections on the right. Open the background to return to the full map.
Hover highlights cards within two connections. Search filters the map. Type filters hide a category. Dragging saves card positions. The connection-depth controls extend the focused view farther in either direction.
Switch the card panel between Note and Markdown. Editing markdown reparses the map as you type.
Silent failures
Every case below parses without a hard error.
| Symptom | Cause |
|---|---|
| A card is missing | The block did not open, often because key:value has no space. |
| Left-to-right layout is ignored | layout is not exactly LR. |
| Type colors are ignored | The map block uses an unread key such as colors. |
| Some configuration disappeared | A later type: map block replaced the first. |
| An arrow points backwards | A passive suffix or inverse pair flipped it. |
| A reverse label reads wrong elsewhere | An inverse label was reused with another meaning. |
| Two connections became one | Their directions and stemmed labels match. |
| A gray dashed card appears | A link target has no matching ID. |
| Two cards share a name | IDs are duplicated; they do not merge. |
| A card uses a subheading as its title | No title was set, so the first heading anywhere in the note won. |
Several cards say Untitled | They have no title or heading. Their fallback IDs may collide. |
| Gray unlabeled lines fill the map | Bare wikilinks created mentions connections. |
| Every card has normal weight | An unrecognized weight word fell back to 3. |
| A written relationship is missing | It pointed back to the same node. |
Verify a map
Inside the md2hd repository, run the real parser and read the connection list after normalization:
npx vite-node .agents/skills/writing-md2hd-maps/check-map.ts -- path/to/map.md
For a folder that represents one map, pass the folder. Do not check unrelated maps together; their map blocks would replace each other.
Before shipping a map, confirm:
- Every intended block opened.
- Exactly one map block exists.
- The map block uses only
title,layout,types,inverse, andsymmetric. - Every ID is unique.
- Every link target matches an ID after slugging.
- Every weight is a recognized word or a number from 1 through 5.
- Every inverse and symmetric label keeps the same meaning everywhere.
- Every bare wikilink is meant to draw an unlabeled
mentionsconnection.