Skip to main content

26-08-04-markleft-ai-markdown-review/readme

· 9 min read

My work has shifted from writing and reviewing code to writing and reviewing plans as well as code. Yet the tooling for reviewing AI-generated documents still feels painful.

AI rewrites Markdown easily. The hard part is reviewing whether a rewrite addressed your feedback.

I built Markleft: a local, Google Docs-like review interface where humans annotate rendered Markdown and AI can only append suggestions. This lets authors review proposed changes without losing the original document or the feedback that motivated them.

pelican

Want to see it in action? spoiler

<!-- truncate -->

Working with AI in Markdown and where it breaks

AI output seldom meets the author’s intent on the first attempt, so iteration is necessary. Today, iteration usually means providing feedback in a follow-up prompt, which leads to a rewrite of the whole document. The prompt that explains the intent behind the revision remains buried in the chat.

This is like sending a document attached to an email with written instructions and receiving a complete new document in return. That workflow has been solved for decades by comments and tracked changes in Word, or by Suggesting mode in Google Docs.

Let's play this through!

A prompt to Claude:

Please write a little story about a pelican on a bike in Markdown, and add a small SVG illustration of the pelican.

This may result in:

Markdown Rendered
### A Poor Pelican
Perry the pelican found a bright
red bicycle leaning against the pier—and,
after one curious glance,
decided it was exactly the sort of adventure
the morning required.
He wobbled past the fishing boats,
rang the tiny bell with his beak,
and rolled onto the beach—just in time for breakfast.
![pelican](./pelican-on-a-bycicle-v1.svg)

A Poor Pelican

Perry the pelican found a bright red bicycle leaning against the pier—and, after one curious glance, decided it was exactly the sort of adventure the morning required. He wobbled past the fishing boats, rang the tiny bell with his beak, and rolled onto the beach—just in time for breakfast.pelican [^image-8091-1865-55412-f262]

Step one: Formulate the critique

Turning your feedback into useful instructions is already a precision problem.

The headline promises the wrong story. Remove the em dashes, shorten the second sentence, fix the pelican's head, put each foot on a pedal, and make the bird look sportier.

That sounds specific. It is not.

The headline” works only because this toy document has one obvious headline. In a longer article, it becomes an invitation to guess.

The second sentence” makes both the Reviewer and the AI count sentences, then hope we count them the same way after an edit.

“The left leg” is worse: prose cannot point to the exact shape in the SVG that broke the illusion. That is why tools such as Figma allow comments directly on the design surface.

Now try giving the same kind of feedback on a multi-page document with repeated headings, nested tables, diagrams, and code.

Please update the second image in the third paragraph that displays the network architecture. Move the backup server to the top right, next to the second database server, in read-only mode.

Step two: Understand the change

Several approaches can help, but they share the same weakness: the AI produces a new document before the author can review the proposed changes in context.

We are still in the first iteration. An update executed by an LLM does not mean that our feedback was taken into account or that our intent was met. We are left with two tasks:

  1. Find the change. One round of “spot the difference” across the whole document. Git can show diffs, but changes hidden behind rendered Mermaid diagrams, tables, and SVGs are much harder to grasp.

  2. Recover the intent. Once we know what changed, we still have to remember which part of our original prompt the change was meant to address and decide whether the result actually satisfies the feedback.

The Idea - Suggestion Mode for Markdown

What is needed is a way to annotate parts of the document directly instead of describing their location vaguely, along with a way to review proposed changes against those annotations.

This is exactly what Markleft provides. It is based on three main components:

  1. A WYSIWYG Markdown editor for humans to create and edit comments, propose changes, and apply suggestions without thinking about the underlying format.
  2. A Markdown-compatible annotation spec consumable by AI, allowing users to comment on text, code, tables, Mermaid diagrams, images, and SVGs, and to propose suggestions.
  3. A prompt that tells the AI how to address the comments and instructs it to append suggestions only in Markleft.

And a cool name: when you add a remark to Markdown, it becomes a document with a mark-left. You can iterate until it becomes mark-right—okay, enough.

Markdown remains the document format and we use the HTML it describes as workspace around it.

Markleft

The spec uses ordinary Markdown constructs to enable suggestion mode.

How annotations and append-only suggestions travel through Markdown

Markleft - the editor

The Markleft editor operates on local Markdown files. It runs as a bookmarklet in Chrome, so you can open a Markdown file, activate the bookmarklet, and get a full-featured editor with comments and suggestions.

It reads the Markdown file, parses its footnotes into Markleft annotations, comments, and suggestions, and renders them.

If a user comments on a selection or places a marker inside an image, the editor injects a corresponding Markleft footnote. When the user saves the file, the editor compiles a prompt that explains Markleft to the AI and describes how to compose suggestions that address the comments.

The AI composes suggestions and appends them to the Markdown file. The editor detects those changes and renders the new suggestions with links to the comments they address. Text is diffed within corresponding rendered elements. List items and table cells are paired before their text is compared, avoiding one meaningless diff across an entire structure. Image-only replacements become a before-and-after slider.

See it in action

Markleft - the spec

Annotations are just Markdown footnotes

An annotation—like a comment—consists of two parts: an anchor that identifies what it comments on and the comment itself. Markdown has a concept of footnotes that most Markdown renderers support. An anchor uses the format [^id-of-the-footnote], while its definition appears on a separate line in the format [^id-of-the-footnote]: body of the footnote. To encode additional information—such as selected words or x/y coordinates inside an image—we use a schema in the footnote ID itself.

For a text range:

This sentence needs less ceremony.[^range-prev-12-chars-14824-a1b2]

[^range-prev-12-chars-14824-a1b2]: Make this more direct.

range-prev-12-chars says that the annotation covers the previous twelve visible, non-whitespace characters. The remaining components provide identity and a content fingerprint so MarkLeft can detect when an anchor has become stale.

Other IDs encode other kinds of anchors:

  • image-X-Y-* stores normalized image coordinates.
  • code-line-L-col-C-len-N-* identifies a code range.
  • block-* addresses the containing block.
  • comment-* represents a reply to another comment.

Because the comment is a footnote, Markdown tools preserve it even when they do not understand Markleft. To a normal renderer such as GitHub, it is just a footnote. To the AI and the Markleft editor, it identifies a point on an image or a highlighted sentence inside a text block.

Stable block IDs make structural changes addressable

To allow suggestions to target blocks reliably, we need stable identifiers. Markleft injects an HTML comment immediately before each real document block:

<!-- markleft:block id="b21fe459" -->

Suggestions are unreferenced, append-only footnotes

A suggestion is a footnote definition with a reserved ID and intentionally no inline footnote anchor in the original body.

[^suggestion-s2-update-block-babf825b]: replacement Markdown

The ID says:

  • this is suggestion s2;
  • the operation is update;
  • the target is block babf825b

Insert-before, insert-after, and delete operations use the same pattern. The last line of a suggestion body contains footnote anchors for the comments the suggestion addresses; that line is metadata, not part of the proposed content.

This is the crucial append-only property: an AI can add a proposal without receiving permission to alter the document it is reviewing.

Give it a try

You can try the Markleft editor by dropping this link into your bookmarks. Keep in mind that this is a small, locally run proof of concept from a vibe-coded weekend project, so expect rough edges.