Repository navigation
feat: Markdown support — rendered for humans at /d/:id, byte-exact source at /raw - #18
Merged
Merged
Conversation
…urce at /raw Agents write plans in Markdown far more often than HTML, but a reviewer wants to open a URL and READ it, not stare at raw asterisks. A .md upload is now rendered to a self-contained HTML document served at /d/:id, while the exact uploaded bytes stay retrievable at /d/:id/raw as text/markdown — so the byte-for-byte promise holds for Markdown too. - Accepts `markdown`, or `html`+format:"md", or infers from a .md/.markdown/.mdown /.mkd filename, so the CLI can just send file bytes and let the server decide. - Rendered ONCE at upload into a companion R2 object (.rendered.html), never per request: rendering at request time would let a `marked` upgrade silently change an already-published document. - Rendering is NOT sanitising. Markdown permits raw HTML passthrough, so the RENDERED output goes through the same authoritative validator as a direct HTML upload — verified: <script>, onerror, <iframe> and javascript: links in .md all rejected 422 with the correct error codes. - Hash/size/dedup key on the SOURCE bytes, not the rendered form, so a source edit that happens to render identically is not mistaken for a duplicate. - source_format persisted (migration 0002, defaults 'html' so every existing version keeps its behaviour); exposed as X-AgentDraft-Format and in the response. - Self-contained document CSS (no remote fonts/CSS — the content-origin CSP forbids it and an offline-readable document is the point). Two defects found and fixed during verification: 1. GFM task lists render as <input type="checkbox"> by default — a FORM CONTROL the policy blocks, so an ordinary agent checklist was rejected by our own validator. The validator was right; the fix belongs in the renderer, which now substitutes inert Unicode ballot glyphs. Guarded by a test asserting no <input> is emitted. 2. Those glyph items showed BOTH a bullet disc and a ballot box. Confirmed in the DOM (computed list-style-type was "disc"), fixed with li:has(> .task-box). Note on visual QA: the aux vision model reported "unstyled native checkboxes with a green tick" and "inline code unstyled" — both FALSE. The DOM showed 0 <input> elements and 2 .task-box spans, and the test document contained no backticks at all. Only the duplicate-marker finding was real. Every claim was gated on a browser_console DOM read before acting. Verified live: 198/198 brutal-suite assertions (19 new Markdown ones), CLI .md upload -> rendered h1 in browser + /raw byte-for-byte MATCH, HTML uploads completely unaffected (/d and /raw still identical).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agents write plans in Markdown far more often than HTML, but a reviewer wants to open a URL and READ it. A
.mdupload is now rendered to a self-contained HTML document at/d/:id, while the exact uploaded bytes stay retrievable at/d/:id/rawastext/markdown— so the byte-for-byte promise holds for Markdown too.Design
markdown, orhtml+format:"md", or infers from a.mdfilename, so the CLI just sends file bytes and lets the server decide.markedupgrade silently change an already-published document.Two defects found while verifying
<input type=checkbox>— a form control the policy blocks — so an ordinary agent checklist was rejected by our own validator. The validator was right; the renderer now substitutes inert ballot glyphs.list-style-type: disc), fixed withli:has(> .task-box).Vision-QA honesty note
The aux vision model claimed "unstyled native checkboxes with a green tick" and "inline code unstyled" — both false. The DOM showed 0
<input>and 2.task-boxspans, and the test document had no backticks at all. Only the duplicate-marker finding was real. Every claim was gated on a DOM read before acting.How tested
<script>,onerror,<iframe>,javascript:links inside.mdall rejected 422 with correct codes.mdupload → rendered<h1>in browser +/rawbyte-for-byte MATCH/dand/rawstill identical