# PostMD — Document graphs

**Covers** — visualising a document held on PostMD. A request to visualise, diagram, map,
outline or structure one of these documents, or in Korean to 시각화·구조화·도식화 a document or
show its 문서 그래프, is answered by what is on this page: where the graph data goes, the fields,
the graph types and their relation vocabularies, how nodes anchor to the document, and how to
revise a graph.

A document graph is what an agent puts together from reading a document, anchored to the places
in it that each part comes from and drawn in a panel beside the text. A mermaid block is a diagram
the author wrote into the text, and the two are unrelated. See "What a document graph is" below.

**Not here** — there is no graph endpoint. The data travels inside the markdown, so it goes in
with the document calls in [/docs/api](/docs/api): through edit mode as described below, or with an
ordinary update. The map is [/llms.txt](/llms.txt).

Fetching the viewer address `/d/{docCode}` does not give you the document. The viewer is drawn
by a script, so a caller that does not run scripts receives a service notice instead, identical
for every document. Building a graph from that notice produces a graph of the notice. The
document comes from `GET /api/v1/documents/{docCode}/raw`, and a fetcher that summarises what it
downloads will paraphrase it, which is not enough to check an anchor character for character.

## What a document graph is

**A document graph is a map of one document.** Its subject is always the document in hand: what
parts it has, or what things it names, and how those stand to one another. Each node normally
carries an `anchor` naming a heading in that document, and selecting a node in the viewer moves the
reader to that part of the text. A node without one is drawn but leads nowhere, and the viewer does
not report it. The graph sits in a panel beside the document, not in the reading
order, and a reader uses it to see the shape of the material before or while reading it.

It is closer to an index or a table of contents than to an illustration. An index is also a
second apparatus whose subject is the document it accompanies, which is why it sits apart from
the text and why every entry points into it.

### What it is not

**A diagram drawn inside the text is a different thing.** A markdown document may contain mermaid
blocks, and PostMD renders them inline like any other content. They are part of what the author
wrote — a login sequence, a network, a state machine drawn at the point where the text discusses
it. A document graph is put together afterwards by an agent that has read the whole document, and
each node points back to the place its content comes from.

The consequences are not interchangeable.

| | Mermaid block | Document graph |
|---|---|---|
| Written by | The author, as part of the text | An agent, from reading the whole document |
| Where it appears | At one point in the reading order | In a panel beside the whole document |
| Nodes point at | Nothing; it is a picture | Headings, which the reader can jump to |
| Covers | One topic the author chose to draw | What a reader wants to understand, gathered from wherever the document holds it |

A document that already contains mermaid is therefore not already covered. Existing mermaid
blocks are useful source material, because the author has already stated some of the relations
in writing, but a diagram of one subsystem is not a map of the document that describes it.

**A picture made in the conversation is also a different thing.** It reaches the person who
asked and nobody else. A document graph is stored in the document, so it reaches everyone who
opens the document afterwards.

## What a reader is asking for

A request is usually short — "graph this document", 「문서를 그래프로 만들어 줘」 — and it leaves
open what the person wants to understand. The same document supports different graphs for
different readers: someone new to the field wants to know where to start, someone who works with
it wants the bodies behind a rule or what separates two schemes. Asking what they are trying to
understand, in their own words, usually settles both the type and what the graph covers.

People often cannot name a kind of diagram and may not want a long exchange. A question about what
they want to know works better than a list of graph types, and how far to take the conversation is
a judgement the context supports: sometimes one exchange settles it, sometimes the request already
says enough. What they said is worth keeping in the block's `question` field, where the viewer
shows it above the graph.

| The reader wants to know | The graph shows | Type |
|---|---|---|
| Where to start in something long | which parts have to be taken in before which | `prerequisite` |
| What happened when | the events the document dates, on a time axis | `timeline` |
| What the rule rests on and who applies it | the laws, bodies and schemes named, and how they relate | `knowledge-graph` |
| How a subject divides | the categories and what falls under each | `taxonomy` |
| How two terms they half-know connect | the concepts and the relation between each pair | `concept-map` |
| What is done in what order, and where it branches | the steps, gathered from every chapter that holds one | `procedure` |
| Where a concept comes up | the chapters that take up each concept that runs across them | `concept-chapter` |
| Why a particular result comes about | that result and its causes, grouped | `cause-effect` |
| How particular things compare | those things against the points that separate them | `comparison` |
| What supports a particular claim and what stands against it | the claim, its reasons and the objections | `argument` |
| Where particular things fall on two criteria | each thing in the cell, or at the point, that its two values give | `quadrant` |

Two more types serve narrower documents: `discourse` for an argued text whose sections support one
another, and `requirements-trace` for a specification whose requirements are met and verified by
other parts.

Of the first ten in the table, the first seven are settled by the document alone: given the same
document, an agent draws much the same graph each time. The last three need the question itself —
which result, which things, which claim. Asked for without one, repeated attempts on the same long
document chose a different subject each time. When nobody is there to ask, as in a batch run, the
first seven are the ones that come out the same way.

`quadrant` was added after those attempts and has not been measured the same way. It needs two
criteria and, for each thing, where the document puts it on both. Where the document itself sorts
things by two criteria — a risk register by likelihood and impact, options by cost and effect — it
is settled by the document. Otherwise the criteria come from the question, and a value the document
does not state is better left out than estimated.

A graph earns its space by putting together what reading in order keeps apart. What the document
already shows in one place, it shows better there.

| Already in the document | What a graph can add |
|---|---|
| A procedure drawn in one chapter | The steps spread across several chapters, joined into one flow |
| A comparison table in one section | Things described in different chapters, set side by side |
| The table of contents | A reading order that differs from the order of the chapters, or a concept that runs across them |

Several narrow graphs often answer more than one wide one. A long reference serves a newcomer and a
practitioner differently, and the viewer lists graphs in a menu.

## Division of work

PostMD holds no model of its own and performs no analysis. It renders, stores and serves. Deciding
what the nodes are, what connects them and what each connection means requires reading the
document, and that work belongs to the agent.

The path is the same in every case. A person asks an agent for a graph, the agent reads the
document and writes graph data into it, and the viewer draws whatever it finds. Nothing else is
needed: there is no separate endpoint, no upload channel and no server-side store for graphs.

The viewer offers no way to create or change a graph. A reader can only look at what the document
contains. A document holds a graph only if one was written into it, so for a document that has
none there is nothing to fetch and nothing for the viewer to draw.

## The whole procedure

Three calls, in this order. Read the rest of this page for what goes in step 2; this is the shape
of the work.

```
POST /api/v1/documents/{docCode}/edit/start      enter edit mode, take the session token
GET  /api/v1/documents/{docCode}/raw             the markdown, as one string
                                                  work out the graph from this text alone
POST /api/v1/documents/{docCode}/edit/content    the whole body, with the comment in it
```

The last call is `multipart/form-data` with one field, `file`, and carries the session token from
the first call in an `X-Edit-Session` header.

These calls carry no API key, and that is what gives you a session token to hand to the person.
Entered with a key, the hold belongs to the key and the person's browser does not show the working
copy. A document with a password takes the `X-Document-Password` header on each call instead.

If `start` answers `"hasDraft": true`, a working copy from an earlier session is still there, and
saving writes over it. `GET /edit/content` with the same header returns it, to continue from or to
set aside.

```
curl -X POST https://postmd.turink.com/api/v1/documents/{docCode}/edit/start
# {"data":{"editSession":"3f2a...", ...}}

curl -X POST https://postmd.turink.com/api/v1/documents/{docCode}/edit/content \
  -H "X-Edit-Session: 3f2a..." \
  -F "file=@document.md;type=text/markdown"
```

Give the person this address once, after the first call. It carries the session, so their viewer
shows the working copy rather than the published document.

```
https://postmd.turink.com/d/{docCode}?edit=3f2a...
```

**The published document does not change.** Anyone opening its address still sees what they saw
before. The person who asked for the graph sees the working copy in their viewer, which redraws
within about five seconds of each save, and applies it from there when they are satisfied.
The button they press for that is **Apply to document**, `문서에 반영` in Korean, and it calls
`/edit/publish`. That word appears nowhere on their screen, so the button's name is the one to
use when telling them the working copy is ready.

`/edit/publish` takes the same permissions as any other change to the document, so you can call
it yourself when that is what was asked for — a person who says to publish when it is done, or an
automated job whose last step is publishing. The working copy exists so that the choice can be
someone's rather than automatic, not because the call is restricted. The call is
`multipart/form-data` and takes `notesOnReplace`, as below; `keep` leaves any notes on the document
where they are.

```
curl -X POST https://postmd.turink.com/api/v1/documents/{docCode}/edit/publish \
  -H "X-Edit-Session: 3f2a..." \
  -F notesOnReplace=keep
```

Because the published copy is untouched, saving more than once costs nothing. Save, let them
look, change it, save again.

The full description of edit mode, including what happens when the session expires and how
another caller takes over, is in [/docs/api](/docs/api) under "Edit mode".

**A document with no local copy is the case this is for.** If the markdown is a file on the same
machine as you, editing that file is simpler: the person opens it in the local viewer at
`/local-viewer`, which redraws within a second of each save, and publishes from there. Edit mode
exists because a document you reached by its address has no file you can write to.

**What a browser would tell you is already in the text.** The viewer draws from the data and
nothing else, so checks on the string you are about to send settle what the reader will see:

- The JSON parses, `type` is a string and `nodes` is an array. A block that fails one of these is
  skipped.
- Every `from` and `to` names a node in the same block. An edge that does not is left out.
- Every `anchor` string appears as a heading in that markdown, character for character. A node whose
  anchor does not is still drawn, in grey, and leads nowhere.

The first two decide whether the graph and its lines appear; the third decides whether each node
takes the reader somewhere.

**One question per graph.** A document holds any number of them, and what settles how many is
whether each has enough to say: a graph of three nodes leaves a reader nothing to read, and a
graph holding two unrelated questions leaves a shape nobody can follow. A short document is
usually one graph; a long reference that serves a newcomer and a practitioner differently is
usually several. The `question` field says which question a graph answers.

## Placement

Graph data lives in an HTML comment inside the markdown file. The comment opens with the marker
`postmd:graph` on its own line, followed by a JSON object.

```
<!-- postmd:graph
{
  "type": "prerequisite",
  "nodes": [...],
  "edges": [...]
}
-->
```

The marker has to end its line. `<!-- postmd:graph {` on one line is not recognised.

A file may hold any number of these blocks, and each block is one graph. Keeping them in separate
comments rather than one array means a broken block costs only itself, and editing one graph does
not touch the others. Placement within the file makes no difference to the viewer; the end of the
file keeps the prose intact for anyone reading the raw markdown.

A block whose JSON does not parse is skipped without notice, and the remaining blocks still
draw. `-->` ends the comment, so it cannot appear anywhere inside the JSON.

Because the data is part of the document, it travels with the document. Uploading, updating,
downloading and reading use the endpoints already described in the API reference, unchanged.

## Field names

Korean and English field names carry the same meaning and may be mixed within one block. Where
both names for one field are present the Korean one is used. The English names are given first
here.

| Field | Required | Meaning |
|---|---|---|
| `type` / `종류` | yes | Graph type. See below |
| `name` / `이름` | no | Title shown in the viewer's graph menu |
| `question` / `질문` | no | The question this graph answers, in the reader's words. Shown above the graph |
| `attributes` / `속성` | for `comparison` | The points of comparison, in column order |
| `axes` / `축` | for `quadrant` | The two criteria, across first and then up. See below |
| `nodes` / `노드` | yes | Array of nodes |
| `edges` / `연결` | no | Array of connections |

A node:

| Field | Required | Meaning |
|---|---|---|
| `name` / `이름` | yes | Node label, and the identifier edges refer to |
| `anchor` / `닻` | no | Place in the document this node points at |
| `description` / `설명` | no | One line shown when the node is selected |
| `date` / `날짜` | for `timeline` | `yyyy`, `yyyy-mm` or `yyyy-mm-dd`, to the precision the document gives |
| `role` / `역할` | by type | What the node is within its graph. The values depend on the type; see below |
| `group` / `묶음` | no | A set the node belongs to. Colours a `knowledge-graph`, `timeline` or `quadrant`, and forms the bones of a `cause-effect` |
| `terms` / `찾을말` | no | Other names for a `concept-chapter` concept, searched along with its name |
| `values` / `값` | for `comparison`, `quadrant` | An object from each attribute, or each axis, to what the document says about this item |

An edge:

| Field | Required | Meaning |
|---|---|---|
| `from` / `시작` | yes | Node name |
| `to` / `끝` | yes | Node name |
| `relation` / `관계` | depends on type | Text drawn on the line |
| `directed` / `방향` | no | `true` by default. `false` draws a line with no arrowhead |

A node has no separate identifier. Its name is the identifier, and edges refer to nodes by name.
An agent that keeps identifiers and labels apart can let the two drift out of step, and a reader
of the raw markdown can then no longer tell what an edge points at.

Where a document holds two or more graphs of one type, give each of those blocks a `name`.
Without one the menu falls back to the type name and a number, which tells the reader nothing
about what separates them. The menu groups graphs by type, in the order each type first appears in
the file, and lists the graphs of one type in the order their blocks appear.

## Graph types

The `type` value settles what the nodes mean and how the viewer lays them out. Each type has a
space of its own, so a reader can tell the types apart before reading a label.

Use the English identifier. The Korean terms in the last column are accepted as equivalents.
Where the table says a relation name is not needed, one may still be given and it is drawn on the
line, except in `taxonomy`, whose tree draws its lines without words.

The first Korean term in the last column is the name the viewer shows in its menu and above the
graph, and the one to use when telling a Korean reader which graph to open.

| `type` | Nodes | Edges | Relation names | Drawn as | Also accepted |
|---|---|---|---|---|---|
| `prerequisite` | Topics the document covers | The source has to be read first | Not needed | Rows from the top, numbered in reading order | `선행 관계` |
| `timeline` | Events with a `date` | None | — | A horizontal time axis | `연대기`, `연표` |
| `knowledge-graph` | Bodies, rules, people and other things the document names | Named relation between two of them | Required | A network with no up or down; colour by `group` | `지식 그래프` |
| `taxonomy` | Categories the document distinguishes | The source is the wider category | Not needed | A tree growing to the right | `분류 체계` |
| `concept-map` | Concepts the document covers | Named relation between two concepts | Required | General above specific, relation words on the lines | `개념도` |
| `procedure` | Steps, with a `role` | The flow from one step to the next | A condition, on lines leaving a decision | A flowchart from the top | `절차 흐름도`, `순서도` |
| `concept-chapter` | Concepts that run across chapters | None | — | A grid of concepts against chapters | `개념 × 장`, `개념별 장` |
| `cause-effect` | One result and its causes, with a `role` | None | — | A fishbone with the result at the head | `원인과 결과`, `피시본` |
| `comparison` | The things compared, with `values` | None | — | A table | `비교표` |
| `quadrant` | The things placed, with `values` | None | — | Cells or a plane on two axes | `사분면`, `분포도` |
| `argument` | A claim, reasons and objections, with a `role` | From a reason or objection to what it bears on | Required, from a fixed set | A tree with the claim at the top | `논증도` |
| `discourse` | Parts of the document | The source supports the target | Required, from the RST set | Layers with the nuclei at the top | `문서 흐름`, `담화 구조` |
| `requirements-trace` | Requirements, and what meets them | Traceability relation, from the SysML set | Required, from the SysML set | Layers from the top | `요구사항 추적` |

### Choosing between two that look alike

Two pairs are mistaken for each other often enough to name here.

**`prerequisite` or `procedure`.** Both put things in an order. In `prerequisite` the order is one
of understanding: a reader has to take in the source before the target makes sense, and the graph
answers "where do I start reading". In `procedure` the order is one of doing: the steps a person
carries out, with the points where the path branches. A document about a sequence of work usually
wants the second, since the order of its steps is not the order in which to read about them.

**`concept-map` or `knowledge-graph`.** Both name their relations. `concept-map` holds ideas the
document teaches and the relations between them, so a node is something a reader comes to
understand. `knowledge-graph` holds things the document refers to — organisations, rules, people,
systems — so a node is something that exists outside the document and could be looked up.

If neither of a pair is clearly right, either will draw. The type only settles what the reader is
told the lines mean; it does not change whether the graph appears.

The types follow established models: concept maps after Novak, discourse structure after
Rhetorical Structure Theory (Mann and Thompson, 1988), requirements traceability after the SysML
requirements diagram, the flowchart symbols after ISO 5807, the fishbone after Ishikawa. Keeping
their terms rather than inventing new ones means the same graph means the same thing across
documents and across the agents that write them.

`layout` applies only to a type you define yourself. A listed type is placed by the rule that
suits it, and a `layout` given alongside one is ignored.

Entity-relationship diagrams, state diagrams and other forms are not listed. Where one can be put
as nodes and lines, a type you define yourself carries it.

### Types that carry more than nodes and lines

Role values have Korean equivalents, as field names do: `시작` start, `단계` step, `갈림` or `판단`
decision, `끝` end, `주장` or `결론` claim, `근거` reason, `반론` objection, `결과` effect, `원인` cause.

**`timeline`.** Each node needs a `date`. The document's own precision is the right one: a year
where it gives a year, a day where it gives a day. The axis gives each year a width that grows with
the number of events in it, so a busy stretch spreads out and an empty stretch stays narrow but
visible. Nodes that share a `group` share a colour.

**`procedure`.** A node's `role` is `start`, `step`, `decision` or `end`, and a node without one is a
step. A condition, such as "approved" or "returned for changes", goes in the `relation` of a line
leaving a decision. A line that goes back to an earlier step is expected, and the viewer draws it back up to that step.

**`argument`.** A node's `role` is `claim`, `reason` or `objection`. Lines run from a reason or an
objection to the claim or reason it bears on, and their `relation` is `support` or `attack`. The
relation sets the line: solid for support, dashed for attack, so the word itself is not drawn.

**`cause-effect`.** One node has the `role` `effect`; the rest are causes, and need no `role`
(`cause` is accepted too). Causes that share a
`group` sit on one bone, which carries the group's name. A cause with no `group` is a bone of its
own. No edges are needed.

**`comparison`.** `attributes` lists the points of comparison. Each node is one thing compared, and
its `values` maps an attribute to what the document says. A cell the document does not fill is best
left out rather than guessed; the viewer leaves it empty.

**`quadrant`.** `axes` holds two axes, each with a `name`; the first runs across, the second up.
Each node's `values` maps both axis names to where the document puts it. It is drawn one of two
ways, and both axes have to be of the same kind.

- **Cells.** Give each axis `values`, the categories in order: left to right across, bottom to top
  up, so the larger or higher end comes last. A node sits in the cell its two values name. This is
  the usual form, since it only asks for what the document says about each thing.
- **A plane.** Leave `values` out, and give each node a number on each axis. The node is a point.
  `range` (`[low, high]`) fixes the ends of an axis and `split` the line that divides it; without
  them the viewer uses the span of the numbers and its middle. Use this only where the document
  gives the numbers.

A node whose value is missing, or is not one of the axis's categories, or is not a number on a plane,
is listed beside the drawing as not placed, and the viewer records it under points to check.

```
"axes": [
  { "name": "Impact", "values": ["Low", "High"] },
  { "name": "Likelihood", "values": ["Low", "High"] }
],
"nodes": [
  { "name": "Server outage", "anchor": "Server outage",
    "values": { "Impact": "High", "Likelihood": "High" } }
]
```

The Korean field names are `축`, and within an axis `이름`, `값`, `범위` and `나눔`. Nodes that share a
`group` share a colour.

**`concept-chapter`.** Each node is one concept, and `terms` lists the other names it goes by in the
text. The viewer finds the name and the terms in each chapter and shades the cell by how often they
appear, so the data holds the concepts and nothing about chapters. A concept that appears in one
chapter only has little to show here.

The chapters are the document's top-level sections. Where the top level holds a single heading, as
when a document opens with its own title, the level below it is used. Links into the document are
not counted, so a table of contents does not colour every concept, and a section with almost no text
of its own gets no column.

### Units and direction in a discourse graph

One node is one section of the document, not one sentence or one clause. RST itself works at
clause level, and at that size a document of any length produces more nodes than can be drawn or
read. Anchor each node to a heading.

An edge runs from the satellite to the nucleus, which is the direction RST uses. The nucleus is
the part that carries the text; removing a satellite leaves the text standing, and removing a
nucleus does not.

The viewer works out which nodes are nuclei from the edges, so nothing marks them in the data. It
then places nuclei above their satellites, and the arrows point upward and read as "supports".

A relation between equals, such as a sequence or a list, takes `"directed": false`. Drawing an
arrow on a symmetric relation would state an order the text does not have.

## Relation vocabularies

Three types draw their relation names from a fixed set. An open vocabulary lets each document invent
its own wording, and graphs of the same type then stop being comparable. A name outside the set
still draws; the viewer records it under points to check.

Korean and English names are equivalent, and case does not matter.

`discourse` uses the RST relations. Nucleus-satellite:

`background` 배경, `circumstance` 상황, `elaboration` 정교화, `evidence` 근거, `justify` 정당화,
`motivation` 동기, `cause` 원인, `result` 결과, `purpose` 목적, `condition` 조건,
`concession` 양보, `antithesis` 대조주장, `solutionhood` 해결, `enablement` 실행지원,
`summary` 요약, `restatement` 재진술, `evaluation` 평가, `interpretation` 해석

Multinuclear, written with `"directed": false`:

`sequence` 순서, `list` 열거, `contrast` 대조, `joint` 병렬

`background`, `elaboration`, `evidence`, `cause`, `result`, `purpose`, `condition`, `contrast`,
`summary`, `sequence` and `list` cover most documents. The rest are available but call for finer
judgement, and two agents reading the same passage are more likely to disagree on them.

`requirements-trace` uses the SysML relations: `satisfy` 충족, `verify` 검증, `derive` 파생,
`refine` 정제, `trace` 추적.

`argument` uses `support` 지지 and `attack` 반박.

## Anchors

An anchor connects a node to a place in the document. Selecting the node moves the reader there,
and while the reader scrolls the viewer marks the node for the place they are in.

The viewer resolves an anchor in two steps. It first looks for a heading whose text matches, and
uses that heading when one is found. Failing that it treats the anchor as a quotation and searches
the rendered body text for it.

Write the heading text. Heading matching compares the whole heading, so a graph built on the
document's own headings resolves every time. Where two headings read the same, the first one in
the document is used.

Quotation matching is available for a node that has to point at a particular sentence, and it
carries the constraints below. There is no figure for how often a quotation written without
regard to them resolves, because that has not been measured.

A quotation has to be the plain text of the rendered page, not the markdown source:

- Copy the sentence unchanged. Runs of whitespace are normalised, but one different character ends
  the match.
- Leave out markdown syntax. Emphasis marks, backticks, link brackets and strikethrough become
  tags when the page renders and are absent from the text.
- Quote from prose. Code blocks and inline code are excluded from the search.
- Quote a whole sentence. A short fragment can occur in several places, and the viewer then has no
  way to tell which one was meant.

A node whose anchor resolves to nothing is still drawn, in grey, and selecting it moves nowhere.
The viewer records it under points to check. It is not removed, because the viewer does not edit
documents and a node dropped without trace would be harder to correct than one shown as broken.

## Types you define yourself

The list of types does not cover every document. A type the viewer does not know is drawn on the
condition that the block explains how to read it, because a diagram whose nodes and lines are
unexplained gives the reader nothing.

| Field | Required | Meaning |
|---|---|---|
| `about` / `그래프설명` | yes | What the graph shows, in one line |
| `nodeLegend` / `마디설명` | yes | What a node stands for |
| `edgeLegend` / `연결설명` | yes | What a line stands for |
| `layout` / `배치` | no | `layered` (default) or `force` |

```
<!-- postmd:graph
{
  "type": "Influence map",
  "about": "Shows what pushes each figure up or down",
  "nodeLegend": "Something that affects the result",
  "edgeLegend": "The source moves the target",
  "nodes": [...],
  "edges": [...]
}
-->
```

The `type` value is used as the name of the type in the menu, so write it as a reader should see
it. The viewer marks the graph as one this document defined. Of the findings in the table below it
reports the five that concern shape alone: cycles, isolated nodes, unresolved anchors, repeated
names and edges to missing nodes. The rest depend on a listed type's vocabulary, roles or fields,
and a type of your own has none the viewer knows.

A block with an unknown type and no explanation is skipped without notice.

A listed type carries a name and a legend the viewer maintains, so readers meet the same wording
in every document that uses it. A type of your own is what the listed ones do not cover: an
influence map, an entity-relationship diagram, anything whose nodes and lines mean something else.

## What the viewer reports

The viewer computes the structural properties of a graph rather than asking the agent for them.
A list of edges is enough to count them, and a count cannot disagree with the edges it came from.

Findings appear under points to check. None of them stops the graph from being drawn.

| Finding | Condition |
|---|---|
| Cycle | A path of directed edges returns to where it started. Undirected edges are excluded. Not reported for `procedure` or `concept-map`, where a loop is part of what they describe, nor for `knowledge-graph`, which has no up or down. In the other types drawn in rows, a cycle also changes the drawing: the rows cannot be ordered, so the graph is drawn as a network instead and `prerequisite` loses its numbered rows |
| Isolated node | A node no edge touches, in a type that uses edges |
| Unresolved anchor | An anchor found neither as a heading nor in the body text |
| Repeated name | Two nodes with the same name. The first is kept and the rest are reported |
| Edge to a missing node | An edge naming a node the block does not define. That edge is not drawn |
| Missing relation name | An edge with no `relation`, in a type that requires one |
| Relation name outside the defined set | A name absent from the vocabulary of that type |
| Role outside the defined set | A `role` value the type does not use |
| Event without a date, or with one that cannot be read | In a `timeline` |
| No effect, or more than one | In a `cause-effect` |
| No claim | In an `argument` |
| No attributes | In a `comparison` |
| Not two axes, or axes of two kinds | In a `quadrant` |
| Node not placed | In a `quadrant`: a value missing, not among the axis's categories, or not a number on a plane |
| Concept not found | A `concept-chapter` concept whose name and terms appear in no chapter |

Node names are compared exactly, including case and spacing, both for repeated names and for the
names an edge refers to. Relation names are compared without regard to case.

These are properties of the shape of the graph, not verdicts on the document. A document with a
cycle in it is not thereby a faulty document, and the viewer states what it found rather than
what it means.

## Limits

A graph is drawn up to 150 nodes. Above that the viewer reports the count and draws nothing, since
a graph that large is unreadable at any size a screen offers. `comparison`, `concept-chapter` and
`quadrant` are laid out as tables and cells rather than drawn and have no such limit, though a table of that many rows is no easier
to read.

Graphs of ten to thirty nodes read well.

Headings of different levels may appear in one graph, and mixing them carries its weight where a
subsection stands in a relation of its own to something outside its parent. A graph of safeguards,
for example, can put a subsection that defines one safeguard next to the chapter holding the risk
it answers.

## Revising a graph

A graph usually takes more than one attempt, and neither loop below puts an attempt in front of
readers.

Through edit mode, each `POST /edit/content` replaces the working copy and leaves the published
document alone. The person watching the viewer sees each save within about five seconds and says
when it is right.

On a file on the person's own machine, the local viewer redraws within a second of the file being
saved, with no request at all. Write the file, let them look, write it again.

## A complete example

````markdown
# Office backup rules

## Why these rules exist

Last year two people kept the same file only on their own laptops. One laptop failed and two
days of work were lost.

## What gets backed up

Working documents and design material. Anything that can be downloaded again is left out.

## Where it goes

One office drive and one external store. If one fails the other remains.

## When

Whenever a piece of work is finished. If the external store cannot be reached, the copy is made the
next working day.

<!-- postmd:graph
{
  "type": "procedure",
  "name": "Backing up a finished piece of work",
  "question": "What do I do when I finish something?",
  "nodes": [
    { "name": "Finish a piece of work", "anchor": "When", "role": "start" },
    { "name": "Copy to the office drive", "anchor": "Where it goes" },
    { "name": "External store reachable?", "anchor": "When", "role": "decision" },
    { "name": "Copy to the external store", "anchor": "Where it goes" },
    { "name": "Copy the next working day", "anchor": "When" },
    { "name": "Two copies exist", "anchor": "Why these rules exist", "role": "end" }
  ],
  "edges": [
    { "from": "Finish a piece of work", "to": "Copy to the office drive" },
    { "from": "Copy to the office drive", "to": "External store reachable?" },
    { "from": "External store reachable?", "to": "Copy to the external store", "relation": "yes" },
    { "from": "External store reachable?", "to": "Copy the next working day", "relation": "no" },
    { "from": "Copy the next working day", "to": "Copy to the external store" },
    { "from": "Copy to the external store", "to": "Two copies exist" }
  ]
}
-->
````

The viewer draws a flowchart from the top: the start and end as rounded boxes, the decision as a
diamond with "yes" and "no" on the two lines leaving it. The question appears above the graph.

## Stability of this format

The field names and type values on this page are a commitment. Later versions add fields and
types; they do not rename or remove what is here, so a document written today keeps drawing.
