Format Details

This document covers formatting rules that extend the base JSONHTL specification.

Markdown in Strings

String values within JSONHTL may contain markdown inline formatting. For example, a paragraph string may include backtick code, bold, or emphasis using standard markdown syntax.

Where markdown and JSONHTL objects express the same thing, both are valid. For example, these are equivalent:

{"para": ["See ", {"code": "config.yaml"}, " for details."]}
{"para": ["See `config.yaml` for details."]}

Similarly, a heading can be expressed as a JSONHTL block or as a markdown-formatted string within a para element, though the structured form is preferred for machine processing.

Lists and Line Breaks

Items in a para array are rendered inline (concatenated). To display items on separate lines, use separate para blocks.

WRONG — items run together:

{"para": ["1. First item", "2. Second item", "3. Third item"]}

Renders as: "1. First item2. Second item3. Third item"

CORRECT — each item on its own line:

{"para": ["1. First item"]},
{"para": ["2. Second item"]},
{"para": ["3. Third item"]}

This applies to both numbered lists and bullet lists. Each list item should be a separate para block.

Normalisation

Systems may normalise all content to one form (structured JSONHTL objects or markdown strings). During the current phase, both coexist. A normaliser tool is planned — see Future.

Content Shorthand

content may be a plain string instead of a list. The string "Just some text." is equivalent to [{"para": ["Just some text."]}]. Parsers should normalise to list form internally.

Likewise, where any value is specified as a list, a bare scalar is shorthand for a single-element list.

Tables

Tabular data uses the table block type. A table contains columns (list of column header strings) and rows (list of rows, where each row is a list of cell values in column order).

{
  "table": {
    "columns": [
      "Layer",
      "Role"
    ],
    "rows": [
      [
        "GPT",
        "thinking"
      ],
      [
        "Email",
        "communication"
      ],
      [
        "Notes DB",
        "memory"
      ]
    ]
  }
}

Cell values are strings. Inline markup (bold, code, etc.) within cell strings is permitted as with para text.

This structure maps directly to a Pandas DataFrame:

import pandas as pd
table = block["table"]
df = pd.DataFrame(table["rows"], columns=table["columns"])

And back again:

table_block = {"table": {"columns": list(df.columns), "rows": df.values.tolist()}}

Tables should be used for genuinely tabular data. For simple key‑value pairs where only two columns exist, a series of para blocks may be clearer. Use judgement.

Lists

Structured lists use the list block type. A list has an optional label (string), an optional ordered flag (boolean, default false), and an items array.

{
  "list": {
    "label": "issues",
    "ordered": false,
    "items": [
      "A plain string item",
      ["An item with ", {"code": "inline elements"}],
      {"id": "foo", "title": "An object item", "status": "open"}
    ]
  }
}

Each item may be:

• A string — plain text, markdown inline formatting permitted.

• A list of inline elements — same content model as para.

• An object — arbitrary key-value pairs. Renderers display each pair as key: value within the list item. Values that look like internal note paths (containing /, no spaces, not http) should be rendered as navigate links.

The label, if present, is rendered as a visible bold prefix immediately before the list. Renderers use <ul> for unordered and <ol> for ordered lists.

Human view: the label appears as bold text, items appear as a bullet or numbered list; object items show their properties inline.

Machine view: a dict with keys label, ordered, items; items are strings, inline arrays, or dicts and can be iterated and filtered directly.

version4
updated2026-03-06