This document defines the mdcomments format as a strict profile over extended Markdown with footnotes.
document.md).[^c-1].c-*), referenced either by a host
marker or by sidecar anchor metadata.@author (date):.document.comments.md.A document conforms to mdcomments normal format if all of the following hold:
c-.c-* identifier has exactly one thread
definition in scope.A sidecar configuration conforms if:
anchor: metadata.Revenue grew by 15%[^c-rev1].
[^c-rev1]:
@alice (2026-02-10):
> Is this YoY?
@bob (2026-02-11):
> Yes. Added a clarifying note.mdcomments supports two anchor styles:
Point anchor (marker after token/span):
text[^c-id]
Range anchor (highlight + marker):
==selected text==[^c-id]
When ==...== is not recognized by a renderer, producers
may add an anchor: metadata line inside the thread
definition.
Example:
[^c-rev1]:
anchor: monthly revenue grew by 15%
@alice (2026-02-10):
> Is this YoY?Recommended naming:
document.mddocument.comments.mdGiven host document.md, a consumer:
document.comments.md if present.Host (document.md, markerless):
Revenue grew by 15%.Sidecar (document.comments.md):
[^c-rev1]:
anchor: "Revenue grew by 15%"
anchor_occurrence: 1
@alice (2026-02-10):
> Is this YoY?
@bob (2026-02-11):
> Yes. Added a clarifying note.In markerless sidecar mode, thread positioning is declared in sidecar metadata:
anchor: <text> — anchor text to match in host
content.anchor_occurrence: <n> — optional 1-based
occurrence index of anchor text in host.If anchor_occurrence is omitted, consumers SHOULD use
the first match.
The grammar below defines mdcomments-specific structures.
Non-mdcomments Markdown content is treated as
MarkdownLine.
Document = { Block } ;
Block = ThreadDefinition | MarkdownLine ;
ThreadDefinition = FootnoteLabel, ":", NL,
IndentedThreadContent ;
FootnoteLabel = "[^", CommentId, "]" ;
CommentId = "c-", IdChar, { IdChar } ;
IdChar = ALPHA | DIGIT | "-" | "_" | "." ;
IndentedThreadContent
= Indent, ThreadItem, { NL, Indent, ThreadItem }, [ NL ] ;
ThreadItem = AnchorMeta | EntryHeader | EntryBodyLine | Blank ;
AnchorMeta = "anchor:", SP, AnchorText ;
AnchorText = { AnyCharExceptNL } ;
EntryHeader = "@", Author, SP, "(", Date, ")", ":" ;
Author = AuthorChar, { AuthorChar } ;
AuthorChar = ALPHA | DIGIT | "-" | "_" | "." ;
Date = DIGIT, DIGIT, DIGIT, DIGIT, "-", DIGIT, DIGIT, "-", DIGIT, DIGIT ;
EntryBodyLine = ">", [ SP ], BodyText ;
BodyText = { AnyCharExceptNL } ;
MarkdownLine = { AnyCharExceptNL }, NL ;
Blank = "" ;
Indent = " " | "\t" ;
NL = "\n" ;
SP = " " ;
ALPHA = "A".."Z" | "a".."z" ;
DIGIT = "0".."9" ;
CommentMarker = "[^", CommentId, "]" ;
RangeAnchor = "==", RangeText, "==", CommentMarker ;
PointAnchor = InlineText, CommentMarker ;
Where RangeText and InlineText are
delegated to the host Markdown parser.
A validator SHOULD enforce:
YYYY-MM-DD.In markerless sidecar mode, validators SHOULD additionally enforce:
anchor: at minimum).A validator MAY warn for:
c- footnote ids used as commentsanchor: in range-less ambiguous anchors