Nope, AI Didn't Do a Great Job on That Pelican—but at Least I Can See It Trying
Markdown has become a convenient working format for AI, but it is missing a review layer. MarkLeft keeps Markdown as the editable source, renders it as a document, stores comments inside it, and asks AI to append suggestions instead of silently rewriting the original.
I asked AI to write a little story about LLMs and a pelican on a bicycle. I also asked it to draw the pelican as an SVG.
This is what I got:
The head is strange. The legs do not convincingly reach opposite pedals. And "sporty" is not the first word that comes to mind.
The result is not the interesting part. The iteration is.
I can tell an AI to fix the image. It will happily return a new SVG. But the moment it does, the old version disappears from view. I can inspect a Git diff, but that only tells me which path coordinates changed. It does not help me answer the question I actually care about:
Did the new image address what I meant?
That is the problem MarkLeft is trying to solve.
The problem
There is a lively argument around the idea that HTML is becoming a better output medium for AI than Markdown. I understand the attraction. A browser can show typography, diagrams, SVGs, interactive tables, navigation, and controls. Markdown source cannot do any of that by itself.
But HTML solves the viewing problem by giving up too much of the editing experience.
1. Raw Markdown hides information that matters
Technical documents are more than their text. They contain diagrams, Mermaid graphs, tables, screenshots, and inline images. Codex and other tools can understand Markdown syntax, but the raw file still does not give a human the document as a composed visual artifact.
An image reference such as this:

is easy to edit, diff, and send to an AI. It is also a poor substitute for seeing the pelican.
This is the strongest argument for HTML: it renders.
2. HTML is a terrible document to edit by hand
HTML is an excellent target and a cumbersome source format. Even small documents quickly accumulate containers, attributes, styles, and behavior. The document may be pleasant to use while its source becomes unpleasant to touch.
That is why Markdown exists in the first place. It is portable, readable, diffable, and still understandable when every special tool disappears.
I do not want to choose between a good source document and a good reading surface.
3. Giving precise feedback is cumbersome
Suppose a section is too long. I can add an HTML comment:
<!-- shorten the section above -->
Or I can open another prompt and write:
The section called “A useful correction” is too long. Keep it short.
Neither approach is satisfying. The first pollutes the document with an ad-hoc convention. The second asks the AI to rediscover which text I meant. Feedback about a particular phrase, a table cell, a line of code, or one spot inside an image becomes even more fragile.
4. The response makes the change—and its intent—disappear
This is the most important problem.
After I give feedback, the AI normally rewrites the document. The new state replaces the old one. I can commit every intermediate file and inspect text diffs, but that is far too cumbersome for ordinary document iteration. A text diff also misses the prompt or comment that caused the change.
The most useful unit is not just:
before → after
It is:
original + comment → suggestion
The comment captures intent. Without it, I can see that something changed but not whether it changed for the right reason.
The idea
MarkLeft is a Google Docs-like review interface for Markdown where AI works only in suggestion mode.
The design has four parts:
- Render Markdown as the visual document it describes.
- Add a Markdown-compatible annotation layer for text, code, tables, Mermaid diagrams, images, and SVGs.
- Give AI an append-only way to propose changes and connect each proposal to the comments it addresses.
- Render those suggestions so a human can accept, reject, or refine them without losing the original.
The Markdown remains the canonical artifact. HTML is the generated workspace around it.
The flow, with one unfortunate pelican
The loop is intentionally simple. You can start with a document generated by AI, or with an architecture note, plan, or blog post that already exists.
Step 0: Generate something worth improving
For the pelican experiment, the starting prompt was approximately:
Create a short Markdown document with a little story about LLMs and a
pelican on a bicycle. Add an SVG named pelican-on-a-bycicle-v1.svg next
to the Markdown file, reference it relatively, and illustrate a pelican
riding a bicycle in the SVG.
The result was a valid Markdown document and a valid SVG. It was also visibly wrong in ways that would be difficult to explain without pointing at the image.
If you already have a document, skip this step. An architecture proposal that needs refinement is an even better example than a pelican.
Step 1: Open the Markdown in MarkLeft
MarkLeft renders the Markdown in the browser but keeps the local .md file as its source. Images are images. Mermaid code becomes a diagram. Tables become tables. The document can be read at the level at which it will eventually be used.
This is the part HTML gets right, without making HTML the file I have to maintain.
Step 2: Annotate what is wrong
I can select text and describe what I dislike. I can comment on a code range. For visuals, I can click directly on the part that needs work.
On the first pelican I left three comments:
- The head looks strange.
- Both legs should be on the pedals—one on each side.
- It should look more sporty.
Those are not instructions floating in a separate chat. They are anchored to normalized coordinates in the image and stored as regular Markdown footnotes.
Step 3: Save the annotated Markdown
After saving, the file contains the original document, stable block identities, and my comments. The annotations travel with the document. They survive another editor, a Git commit, an email attachment, or a call to an AI tool.
The file is still Markdown. It just has Mark(s)Left inside.
Step 4: Ask AI to address the comments
The editor provides a prompt that explains the annotation protocol and authorizes one narrowly defined operation: append suggestions.
The AI may create a new asset such as pelican-on-a-bycicle-v2.svg, but it must not rewrite the existing body. Instead, it appends a suggestion that targets the stable image block and lists the comments it intends to address.
[^suggestion-s2-update-block-babf825b]:

[^image-5233-2528-10665-4583]
[^image-5758-7169-24464-4583]
[^image-5175-4207-48874-4583]
The original reference to pelican-on-a-bycicle-v1.svg is untouched.
Step 5: Review the suggestion
MarkLeft renders the proposed image over the original. When the suggestion is active, a slider reveals the original below it. At 50%, one half shows the before state and the other half shows the proposal. The original image comments remain visible above both layers.
Nope, AI still did not become an illustrator. But now I can see it trying.
I can verify the head, follow each leg to a pedal, and decide whether the helmet and scarf satisfy “more sporty.” More importantly, I can judge every change against the comment that requested it.
If the proposal is good, I accept it. MarkLeft replaces the original block with the suggested Markdown and removes the suggestion. If it is wrong, I reject it or refine the comment.
Step 6: Repeat until slop becomes GOAT
Return to Step 2.
The loop is not “prompt once and trust the output.” It is a visible sequence of intent, proposal, and decision:
annotate → address with suggestions → review → accept or refine
Each iteration remains inspectable.
How MarkLeft works
The protocol uses ordinary Markdown constructs in deliberately unusual ways.
Anchors are encoded in footnote IDs
A comment body is stored as a footnote definition. Its reserved ID describes how to find the anchor again.
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 it is just a footnote. To MarkLeft it is a typed annotation.
Stable block IDs make structural changes addressable
Text anchors are good for comments. Suggestions need a stable structural target.
MarkLeft injects an HTML comment immediately before each real document block:
<!-- markleft:block id="babf825b" -->

Markdown renderers ignore the comment, Git preserves it, and the editor maps it to the rendered element. The identifier survives when blocks move up or down in the file, unlike a line number or “the third paragraph.”
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 final reference-only paragraph connects the suggestion to the comments it addresses and is 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.
How the MarkLeft editor renders suggestions
The editor first renders the original Markdown to HTML and stamps the rendered blocks with their stable IDs. It then parses the suggestion definitions and builds a review layer:
- Find the rendered block with the target ID.
- Render the replacement Markdown separately.
- Place the suggestion in the original block's position without changing the source body.
- Connect the suggestion card to the comments listed in its final paragraph.
- Show a useful diff for the type of content being changed.
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 the before/after slider used for the pelican.
Accepting an update replaces the original Markdown block and removes the suggestion definition. Rejecting removes only the suggestion. Until either action happens, both states and the intent connecting them remain in one portable file.
Markdown is the artifact; HTML is the workspace
The HTML-versus-Markdown debate asks which format AI should produce for humans.
MarkLeft takes a third route:
Markdown
+ stable block identities
+ typed footnote annotations
+ append-only suggestions
+ a rich HTML review surface
Markdown stays readable, editable, Git-friendly, portable, and easy for an LLM to process. The browser supplies the interaction that raw Markdown is missing. AI can make ambitious changes, but it cannot make them invisible.
That is the goal: not to make the pelican good on the first try, but to make every attempt understandable.