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 or a structured list block.

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"]}

For real lists, prefer the structured list block when renderer support is available. Separate para blocks remain acceptable for simple notes and compatibility.

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"
      ]
    ]
  }
}

Each cell value is either a string or a list of inline elements (the same content model as para). A string cell may contain markdown inline markup (bold, code, italic) as in para text. A list cell may contain structured inline objects — most usefully link — so a table cell can hold a clickable link exactly as a paragraph can. Renderers must handle both cell forms; a bare string and a single-element inline list wrapping that string are equivalent.

{
  "table": {
    "columns": ["Ticket", "Summary"],
    "rows": [
      [
        [{"link": {"href": "jira/tickets/HCLPDRM-38781", "text": "HCLPDRM-38781"}}],
        "Portal UI and state detection"
      ]
    ]
  }
}

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()}}

The Pandas mapping above assumes scalar (string) cells. When a cell is an inline-element list, pd.DataFrame stores the list itself as the cell value; consumers doing tabular analysis should flatten such cells to their concatenated text first.

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.

Todo / Checklist Content

Exception: once the notes browser implements interactive checkbox toggling for status fields, the list-with-object-items pattern will be appropriate for interactive checklists. Until then, use a table.

Avoid — list with object items:

{
  "list": {
    "items": [
      {"status": "open", "title": "Bearer token auth", "detail": "Replace basic auth..."}
    ]
  }
}

Preferred pattern — table:

{
  "table": {
    "columns": ["Item", "Detail"],
    "rows": [
      ["Bearer token auth", "Replace basic auth when token is available"],
      ["Derive clientMachine", "Look up from cluster object instead of shell-out"]
    ]
  }
}

List object items ({status, title, detail}) render as raw key: value text, which is unsuitable for human-readable todo content. Use a table block instead for todo/future-work lists where each item has a title and description.

version 6  ·  updated 2026-07-30