On this page

Comment marker syntax

This page specifies the marker format Wymark writes into markdown files for comments. It is the contract for anyone – person, tool, or AI agent – who wants to read or write Wymark comments directly. The format is plain text and designed to be hand-editable.

The format is versioned as wymark.comments/1. A breaking change bumps the version; within a version the grammar only widens in ways a reader written against this page keeps tolerating. Where this page and the versioned specification could ever disagree, the specification is the normative one – this page is its readable face, not a second source of truth.

The marker pair

A comment wraps the text it is anchored to (“the anchor”) in two HTML comments:

text before <!--wymark: PAYLOAD-->anchor text<!--wymark-end--> text after
  • The opening marker is <!--wymark: PAYLOAD-->.
  • The closing marker is always the literal <!--wymark-end-->.
  • Each marker is a single physical line: no real newline characters inside a marker, ever. (An HTML comment ends at the first -->; a blank line inside one breaks rendering in every tool.)

Markers may sit in the middle of a line (inline anchors) or on their own line (whole-block anchors). Wymark emits markers tight against the anchor; a single space of padding on each side of each marker is also accepted, and is removed together with the markers when the comment is resolved.

The payload grammar

The payload is {json}@@body – an optional JSON metadata object, the literal separator @@, then the comment text:

<!--wymark: just a note-->
<!--wymark: {"author":"Jane Doe"}@@tighten this-->
<!--wymark: {"author":"Claude","time":"2026-06-08T09:15:00Z"}@@reworded, better?-->
  • With no leading {json}@@, the whole payload is an anonymous comment body.
  • The JSON object may carry author (string) and/or time (ISO-8601 string). Both are optional and independent. The author string is spliced verbatim into the marker, so it is subject to the same two forbidden sequences as the body.
  • The body is raw, unescaped text. The only forbidden sequences are --> and <!-- – either would close or reopen the HTML comment. Everything else is allowed, including a bare double hyphen -- (an em-dash-style aside like “tighten this – it runs long” is fine).
  • Authorship is self-declared, like in Word – there is no signing or verification.

Multi-line bodies

A line break inside a comment body is stored as the literal two-character sequence \n, keeping the marker on one physical line:

<!--wymark: {"author":"Editor"}@@Two problems:\nToo passive\nToo long-->

A literal backslash in a body is stored as \\, so a body that literally contains the two characters \n round-trips as \\n. A reader decodes exactly those two sequences – \n and \\ – and leaves every other backslash sequence alone.

Wymark’s UI decodes them for display. If some other tool writes a real newline into a marker, Wymark repairs it to the literal form when it next touches the file.

Threads

Replies are markers with the wymark-reply prefix, placed immediately after the opening marker (and after any earlier replies), before the anchor text:

The <!--wymark: {"author":"Jane Doe"}@@Tighten this--><!--wymark-reply: {"author":"Claude"}@@What about a metaphor?--><!--wymark-reply: {"author":"Jane Doe"}@@Yes, do that-->quick brown fox<!--wymark-end--> jumps.

Reply payloads use the same {json}@@body grammar. A thread is one unit: resolving the comment removes the open marker, every reply marker, and the end marker, leaving the anchor text.

Parsing markers

A marker is a marker only where a CommonMark parser produces an HTML node. The same bytes inside a fenced code block, a code span or a maths region are content, never markers, however exact they look – this page is full of such examples and none of them is a comment. So read markers in two steps: parse the document as CommonMark/GFM and take the HTML blocks and inline HTML it reports; then, inside those regions only, match the literal delimiters – <!--wymark:, <!--wymark-reply:, <!--wymark-sidecar, and <!--wymark-end--> – and end each payload at its first --> (a valid payload never contains one). Never discover markers with a regular expression over the raw file.

The second step is needed because a Wymark marker is shaped like an HTML comment but is not guaranteed to be a spec-valid one: a body may contain --, which the HTML spec forbids inside a comment, so a strict HTML-comment tokenizer can fail to recognize the marker. This only affects tokenizing, not display: a lenient CommonMark renderer (GitHub, VS Code, pandoc) still treats these as comments and hides them.

Pairing rules

There are no comment IDs. Markers pair positionally: reading the document top to bottom, each <!--wymark-end--> closes the nearest unclosed opening marker. Consequences:

  • Comments must not overlap or nest.
  • Moving an opening marker without its closing marker (or vice versa) re-pairs the markers and changes what the comments anchor to. Treat the pair as one unit.
  • An unpaired marker is surfaced in the editor as a warning rather than silently dropped.

Positional pairing is a deliberate choice over IDs: IDs can be duplicated by copy-paste and drift out of sync with their content, which produces the worst failure mode – a comment that confidently points at the wrong thing. Tools that need a durable handle to a specific comment should use content-based resolution (the marker’s byte position in a known document state), not an in-file identifier.

The sidecar pointer

A <!--wymark-sidecar ... --> line is a system marker, not a comment, and the only marker Wymark writes on its own. While a document’s AI sidecar is enabled and the document is open, its first line after any front matter is this comment, followed by a blank line. Its text tells a tool to read the review sidecar beside the document, and names that file.

It is not a comment in Wymark’s sense: it has no anchor, no thread and no closing marker, and its distinct prefix keeps it out of pairing. A reader must recognise it and exclude it from pairing, and should leave it out of anything that treats the file as prose. Do not write one, reply to one, move one or remove one. Wymark writes exactly one, rewrites it when the file is renamed, puts it back if it goes missing, and removes it when the sidecar is turned off or the document closes. A line left behind by a crash says of itself that if the sidecar is absent the document is all there is to read, so it stays true until Wymark next shows the document and reconciles it.

Quick reference

Construct Form
Comment open <!--wymark: {json}@@body--> (JSON part optional)
Reply <!--wymark-reply: {json}@@body-->, stacked after the open marker
Comment close <!--wymark-end-->
Metadata author (string), time (ISO-8601 string), both optional
Line breaks and backslashes in body literal \n for a line break, literal \\ for a backslash; decode exactly those two sequences
Forbidden in body & author -->, <!--, real newlines (a bare -- is allowed)
Parsing only inside the HTML nodes a CommonMark parser reports (never regex over the raw file); there, match literal delimiters and end payload at first -->; not always spec-valid HTML
Pairing positional, no IDs; no overlap or nesting
Sidecar pointer <!--wymark-sidecar ... -->, first line after any front matter; written and removed by Wymark only

# Comment marker syntax

This page specifies the marker format Wymark writes into markdown files for [comments](/docs/concepts/comments). It is the contract for anyone -- person, tool, or AI agent -- who wants to read or write Wymark comments directly. The format is plain text and designed to be hand-editable.

The format is versioned as **`wymark.comments/1`**. A breaking change bumps the version; within a version the grammar only widens in ways a reader written against this page keeps tolerating. Where this page and the versioned specification could ever disagree, the specification is the normative one -- this page is its readable face, not a second source of truth.

## The marker pair

A comment wraps the text it is anchored to ("the anchor") in two HTML comments:

```markdown
text before <!--wymark: PAYLOAD-->anchor text<!--wymark-end--> text after
```

- The opening marker is `<!--wymark: PAYLOAD-->`.
- The closing marker is always the literal `<!--wymark-end-->`.
- Each marker is a single physical line: no real newline characters inside a marker, ever. (An HTML comment ends at the first `-->`; a blank line inside one breaks rendering in every tool.)

Markers may sit in the middle of a line (inline anchors) or on their own line (whole-block anchors). Wymark emits markers tight against the anchor; a single space of padding on each side of each marker is also accepted, and is removed together with the markers when the comment is resolved.

## The payload grammar

The payload is `{json}@@body` -- an optional JSON metadata object, the literal separator `@@`, then the comment text:

```markdown
<!--wymark: just a note-->
<!--wymark: {"author":"Jane Doe"}@@tighten this-->
<!--wymark: {"author":"Claude","time":"2026-06-08T09:15:00Z"}@@reworded, better?-->
```

- With no leading `{json}@@`, the whole payload is an anonymous comment body.
- The JSON object may carry `author` (string) and/or `time` (ISO-8601 string). Both are optional and independent. The `author` string is spliced verbatim into the marker, so it is subject to the same two forbidden sequences as the body.
- The body is raw, unescaped text. The only forbidden sequences are `-->` and `<!--` -- either would close or reopen the HTML comment. Everything else is allowed, including a bare double hyphen `--` (an em-dash-style aside like "tighten this -- it runs long" is fine).
- Authorship is self-declared, like in Word -- there is no signing or verification.

### Multi-line bodies

A line break inside a comment body is stored as the literal two-character sequence `\n`, keeping the marker on one physical line:

```markdown
<!--wymark: {"author":"Editor"}@@Two problems:\nToo passive\nToo long-->
```

A literal backslash in a body is stored as `\\`, so a body that literally contains the two characters `\n` round-trips as `\\n`. A reader decodes exactly those two sequences -- `\n` and `\\` -- and leaves every other backslash sequence alone.

Wymark's UI decodes them for display. If some other tool writes a real newline into a marker, Wymark repairs it to the literal form when it next touches the file.

## Threads

Replies are markers with the `wymark-reply` prefix, placed immediately after the opening marker (and after any earlier replies), before the anchor text:

```markdown
The <!--wymark: {"author":"Jane Doe"}@@Tighten this--><!--wymark-reply: {"author":"Claude"}@@What about a metaphor?--><!--wymark-reply: {"author":"Jane Doe"}@@Yes, do that-->quick brown fox<!--wymark-end--> jumps.
```

Reply payloads use the same `{json}@@body` grammar. A thread is one unit: resolving the comment removes the open marker, every reply marker, and the end marker, leaving the anchor text.

## Parsing markers

A marker is a marker only where a CommonMark parser produces an HTML node. The same bytes inside a fenced code block, a code span or a maths region are content, never markers, however exact they look -- this page is full of such examples and none of them is a comment. So read markers in two steps: parse the document as CommonMark/GFM and take the HTML blocks and inline HTML it reports; then, inside those regions only, match the literal delimiters -- `<!--wymark:`, `<!--wymark-reply:`, `<!--wymark-sidecar`, and `<!--wymark-end-->` -- and end each payload at its *first* `-->` (a valid payload never contains one). **Never discover markers with a regular expression over the raw file.**

The second step is needed because a Wymark marker is *shaped* like an HTML comment but is not guaranteed to be a spec-valid one: a body may contain `--`, which the HTML spec forbids inside a comment, so a strict HTML-comment tokenizer can fail to recognize the marker. This only affects tokenizing, not display: a lenient CommonMark renderer (GitHub, VS Code, pandoc) still treats these as comments and hides them.

## Pairing rules

There are no comment IDs. Markers pair positionally: reading the document top to bottom, each `<!--wymark-end-->` closes the nearest unclosed opening marker. Consequences:

- Comments must not overlap or nest.
- Moving an opening marker without its closing marker (or vice versa) re-pairs the markers and changes what the comments anchor to. Treat the pair as one unit.
- An unpaired marker is surfaced in the editor as a warning rather than silently dropped.

Positional pairing is a deliberate choice over IDs: IDs can be duplicated by copy-paste and drift out of sync with their content, which produces the worst failure mode -- a comment that confidently points at the wrong thing. Tools that need a durable handle to a specific comment should use content-based resolution (the marker's byte position in a known document state), not an in-file identifier.

## The sidecar pointer

A `<!--wymark-sidecar ... -->` line is a system marker, not a comment, and the only marker Wymark writes on its own. While a document's [AI sidecar](/docs/guide/ai-sidecar) is enabled and the document is open, its first line after any front matter is this comment, followed by a blank line. Its text tells a tool to read the review sidecar beside the document, and names that file.

It is not a comment in Wymark's sense: it has no anchor, no thread and no closing marker, and its distinct prefix keeps it out of pairing. A reader must recognise it and exclude it from pairing, and should leave it out of anything that treats the file as prose. Do not write one, reply to one, move one or remove one. Wymark writes exactly one, rewrites it when the file is renamed, puts it back if it goes missing, and removes it when the sidecar is turned off or the document closes. A line left behind by a crash says of itself that if the sidecar is absent the document is all there is to read, so it stays true until Wymark next shows the document and reconciles it.

## Quick reference

| Construct | Form |
| --- | --- |
| Comment open | `<!--wymark: {json}@@body-->` (JSON part optional) |
| Reply | `<!--wymark-reply: {json}@@body-->`, stacked after the open marker |
| Comment close | `<!--wymark-end-->` |
| Metadata | `author` (string), `time` (ISO-8601 string), both optional |
| Line breaks and backslashes in body | literal `\n` for a line break, literal `\\` for a backslash; decode exactly those two sequences |
| Forbidden in body & author | `-->`, `<!--`, real newlines (a bare `--` is allowed) |
| Parsing | only inside the HTML nodes a CommonMark parser reports (never regex over the raw file); there, match literal delimiters and end payload at first `-->`; not always spec-valid HTML |
| Pairing | positional, no IDs; no overlap or nesting |
| Sidecar pointer | `<!--wymark-sidecar ... -->`, first line after any front matter; written and removed by Wymark only |