ChromaMark Specification

Base dialect: CommonMark + GFM (tables, strikethrough, autolinks) Language version: 0.2 License: CC BY-SA 4.0 File extensions: .cm · Info-string / fence tag: chromamark

This document is written in ChromaMark. On a plain markdown viewer (e.g. GitHub) the ChromaMark constructs degrade to readable text; for the full experience render it with @chromamark/renderer or try the playground.


The one-paragraph pitch

ChromaMark is a strict syntax superset of CommonMark + GFM that adds the three things agents actually need to communicate clearly — colored blocks, colored inline pills, and collapsible sections — plus a small set of opt-in extensions. It costs a fraction of the tokens of equivalent HTML and degrades to readable plain text anywhere it isn't natively rendered.

1 · Why

Agents stream a lot of status: pass/fail, warnings, diffs, long logs. The two existing options both hurt:

ChromaMark adds exactly those capabilities with the cheapest, least-ambiguous syntax we could find.

2 · Design principles

  1. Syntax superset of CommonMark + GFM. Every valid GFM document is valid ChromaMark. ChromaMark only adds syntax; it does not redefine Markdown. Raw HTML handling remains a renderer policy (§3).
  2. Token-lean. Each construct is measured against its HTML equivalent; if it isn't meaningfully cheaper, it doesn't earn a place.
  3. Graceful degradation. Under a plain markdown engine — or read as raw text — every construct stays legible. No construct hides its own content.
  4. Streaming-friendly. Every construct's opener precedes its content, so a renderer can begin styling with no lookahead — critical for token-by-token agent output.
  5. Unambiguous & safe. Reserved sigils never collide with CommonMark, and no feature requires raw HTML or script execution.
  6. Theme-owned color. Authors pick semantic colors by default; the renderer theme maps them to real values, so output stays correct in light/dark and never clashes.

3 · Relationship to CommonMark & GFM

ChromaMark extends CommonMark + GFM. Everything in the CommonMark spec and the GFM extensions (tables, strikethrough, autolinks) works unchanged. Raw HTML follows the renderer's policy: the reference convenience renderers disable it for safe handling of untrusted agent output, while the plugins honor their host Markdown renderer's setting. ChromaMark reserves four previously inert lexical niches:

Sigil ChromaMark meaning In plain CommonMark it was…
::: at line start container fence literal text
[! … ] inline pill / text / meter literal text
{++, {--, {~~, {== CriticMarkup diff literal text
↳ / > starting a table's first cell row-group depth literal text

Because each was previously meaningless, no existing document changes meaning.

4 · The color vocabulary

Blocks, pills, text, and meters all draw from one vocabulary, so there is a single thing to learn.

Semantic token Aliases Default hue Meaning
success ok, pass green good / passed / done
danger error, fail red bad / failed / blocking
warning warn amber caution / non-blocking
info note blue neutral information
tip hint teal advice / suggestion
muted skip gray de-emphasized / skipped
Semantic first, hex as escape hatch

Prefer semantic tokens — they respect the reader's theme. When a token genuinely won't do, use the escape hatch color=<name> or color=#RRGGBB (e.g. color=#6f42c1). Explicit colors are literal and bypass theming.

5 · Colored blocks — :::

Fenced containers, aligned with the widely-used ::: admonition convention (Pandoc, markdown-it-container, Docusaurus) so existing tooling gets you partway.

::: <kind> [attrs] [title]
…block content (markdown, may nest)…
:::

Live examples (these render as real callouts above/below):

::: success Deploy [!ok healthy] · 3/3 replicas
All 247 tests passed.
:::

::: warning Deprecation
`recon.flights` is removed in v3. Use `query_parameters` instead.
:::

::: block color=#6f42c1 Custom hue
Only when a semantic token won't do.
:::
Fallback behavior

On a plain markdown engine a colored block degrades to its literal ::: lines — the title and body text remain readable and nothing is hidden.

6 · Colored pills — [!…]

Inline badges. The reserved opener [! starts a pill and never collides with CommonMark (there, [!x] is just literal text — exactly the fallback we want).

[!<token-or-color> <label>]

Build PASS · lint 12 issues · deploy FAILED · coverage 87% · flaky SKIPPED · tag beta

Pills shine in tables:

Suite Result Time
unit PASS 4.1s
integration 3 of 88 61.0s
e2e SKIP —

Fallback: renders as the literal bracketed text, e.g. [!success PASS], which still communicates status.

7 · Collapsible sections — ::: details

A container kind, so it is block-level and streaming-friendly (the summary sits on the opener line, before the hidden body).

::: details [open] [tone] <summary>
…hidden content (markdown, may nest)…
:::
::: details danger Integration failures [!fail 3 of 88]
FAILED test_recon_merge_precedence — expected config to win
:::
A real collapsible — click to expand

This body is hidden until you expand it. It may contain any markdown, including nested blocks and code fences.

Fallback: renders as a real <details>/<summary> where supported. On a plain markdown engine it degrades to its literal ::: lines — the summary and body remain readable, and content is never hidden in a non-supporting renderer (principle 3).

8 · Extensions

The inline sigil family is deliberately uniform — one rule to learn:

Sigil Construct Fill?
[!…] pill / badge filled
[.…] colored text tint, no fill
[=…] progress / meter bar

8.1 · Fields (key/value) — ::: fields

Agents emit key/value status constantly. A fields block renders as a compact definition list.

::: fields
Region: eastus
Replicas: 3/3
Commit: `25d7426`
Status: [!ok healthy]
:::

8.2 · Inline colored text — [.…]

Tinted words with no badge background — for prose emphasis.

The gate is [.success green] but the [.danger critical] path is [.warning slow].

The gate is green but the critical path is slow.

8.3 · Progress meter — [=…]

Coverage [=success 87%]   Budget [=warning 72%]   Disk [=danger 96%]   Tasks [=info 3/10]

The label is the displayed value; a trailing NN% or A/B sets the fill.

8.4 · Diff — GFM diff block + inline CriticMarkup

Block-level diffs use the existing GFM fenced ```diff convention. For inline change-tracking, ChromaMark adopts CriticMarkup (© 2013 Gabe Weatherhead & Erik Hess) rather than inventing syntax:

CriticMarkup Meaning
{++insert++} insertion
{--delete--} deletion
{~~old~>new~~} substitution
{==highlight==} highlight / mark
{>>comment<<} comment

The field was flightsquery_parameters and is worth reviewing before merge.

8.5 · Collapsible table rows — ↳

Agent reports are full of parent/child relationships that a flat GFM table cannot express: a summary task and its sub-tasks, a failing suite and its cases, a resource and its dependents. A row group lets rows fold into the row above them.

A row whose first cell begins with a run of ↳ is a child row. It attaches to the nearest preceding row one level shallower, which becomes the group's toggle. Child rows are expanded by default, matching the static output, so a reader sees the whole table until they choose to fold part of it away.

| ID  | Diff             | Task                          |
| --- | ---------------- | ----------------------------- |
| 200 | [!warn MEDIUM]   | Pickpocket anyone 200 times   |
| ↳ 195 | [!success EASY] | Pickpocket from a man or woman |
| ↳↳ 196 | [!success EASY] | …10 times                    |
| 198 | [!success EASY]  | Steal from any stall 10 times |

State. Collapsing a row hides its entire subtree. Each group tracks its own state, so collapsing and reopening a parent restores its direct children in whatever state they already had — a nested group the reader collapsed stays collapsed when its parent reopens.

Malformed depth degrades, never errors. A row deeper than one level below its predecessor clamps to the deepest legal depth, and a child row with no eligible parent becomes an ordinary row. Both emit lint warnings. This matters because the common author of these tables is a language model: a miscounted marker must yield a slightly flatter table, never a dropped or unreachable row.

Fallback: a renderer without row-group support shows every row, in document order, with its literal ↳ prefix intact — which already reads as indentation. As with every other construct, nothing is hidden by a renderer that does not understand it (principle 3).

9 · Nesting

Containers nest. Use more colons for the outer fence so boundaries are unambiguous:

:::: danger Release blocked
Two gates failed:

::: details Failing gate 1 — tests
FAILED test_recon_merge_precedence
:::

::: details Failing gate 2 — coverage
Coverage 71% < 80% threshold.
:::
::::

Pills, text, and meters nest freely inside any block, list, heading, or table cell.

10 · Escaping

11 · Graceful degradation summary

Construct ChromaMark Plain-markdown fallback
Colored block ::: warning Title literal ::: lines; title + body readable
Pill [!success PASS] literal text [!success PASS]
Collapsible ::: details Sum… literal ::: lines; summary + body readable
Colored text [.danger x] literal text [.danger x]
Meter [=success 87%] literal text [=success 87%]
Fields ::: fields literal ::: lines of key: value
Table row group | ↳ 195 | … all rows shown; literal ↳ reads as indent

Nothing is ever hidden or lost when unsupported — a non-ChromaMark engine shows the literal source, which stays readable. Renderers MAY present richer fallbacks (e.g. a blockquote), but that is not required.

12 · Streaming behavior

Because every opener precedes its content and every inline construct is self-delimiting, a renderer can:

No construct requires seeing its end before it can begin styling.

13 · Grammar sketch (EBNF-ish)

Full grammar

The sketch below is a quick overview. The complete EBNF syntax companion for the extension layer — kept in sync with the parser by tests — lives in docs/grammar.ebnf. Use it to build third-party tooling (editors, validators, tree-sitter grammars) without reimplementing the parser.

document     = { gfm-block | container } ;
container    = fence-open , newline , { line } , fence-close ;
fence-open   = ":" , ":" , { ":" } , ws , kind , [ ws , attrs ] , [ ws , title ] ;
fence-close  = ":" , ":" , { ":" } ;              (* >= opener's colon count *)
kind         = tone | "block" | "details" | "fields" ;
tone         = "success" | "danger" | "warning" | "info" | "tip" | "muted" | alias ;
attrs        = { attr , ws } ;
attr         = "color=" , colorval | "open" ;
colorval     = hexcolor | named-color ;
hexcolor     = "#" , ( 3 | 6 | 8 ) * hexdig ;

pill         = "[" , "!" , spec , [ ws , label ] , "]" ;
text         = "[" , "." , spec , ws , label , "]" ;
meter        = "[" , "=" , spec , ws , value , "]" ;
spec         = tone | "color=" , colorval | hexcolor ;
label        = { any-char-except-"]" } ;
value        = number , [ "%" ] | number , "/" , number ;

critic       = "{++" , text , "++}" | "{--" , text , "--}"
             | "{~~" , text , "~>" , text , "~~}" | "{==" , text , "==}"
             | "{>>" , text , "<<}" ;

row-marker   = ( "↳" | ">" ) , { ws , ( "↳" | ">" ) } ;   (* depth = marker count *)

14 · Conformance levels

Level 0 — Text
valid CommonMark+GFM; degrades all ChromaMark constructs. Any existing renderer.
Level 1 — Static
renders colored blocks, pills, text, meters, fields; collapsibles shown expanded, table row groups shown flat.
Level 2 — Interactive
adds real collapse/expand — for both collapsibles and table row groups — and streaming styling.

15 · Reference implementations

Renderer
@chromamark/renderer — a markdown-it plugin (packages/renderer). Works in any webpage; ships the CSS theme.
VS Code
chromamark-vscode — reuses the renderer in the built-in markdown preview and adds syntax highlighting (packages/vscode).
Demo
the playground and gallery — a rendered reference of every construct with a light/dark toggle.

Appendix · A realistic agent report

## Deploy report — `service-recon` @ 25d7426

:::: success Deploy succeeded in 3m12s
::: fields
Region: eastus
Replicas: 3/3 [!ok healthy]
Coverage: [=success 87%]
:::
::::

| Stage  | Result          | Notes           |
| ------ | --------------- | --------------- |
| build  | [!pass]         | cached          |
| unit   | [!pass 247]     | 4.1s            |
| integ  | [!fail 3 of 88] | see trace below |
| lint   | [!warn 12]      | non-blocking    |
| deploy | [!pass]         | 3m12s           |

::: details danger Integration failures (3)
FAILED test_recon_merge_precedence — expected config to win
FAILED test_query_parameters_map  — key casing mismatch
FAILED test_seller_ids_removed    — legacy field still referenced
:::

Overall: [!warn SHIP WITH CAUTION]