JSONHTL Linter Specification

Specification for a strict validator (linter) for JSONHTL documents. The linter is stricter than any renderer -- it flags anything not in the spec or a registered extension, even if renderers silently ignore it. The goal is to catch bad documents before they enter the store, not to fix them. When a document fails, the linter should tell the writer where to find the correct spec so they can update their source of information.

Related Notes

Code

The linter is implemented in two Python files on the DAP AWS instance:

Test Findings

2026-07-06 first test run -- 4 notes tested; gdata-server/todo failed with 2 unregistered inline types (em, strong) and 1 bare-string para. Decision pending: register or fix.

Schema Location and Sync Rule

The machine-readable schema is at JSONHTL_SCHEMA (JSON Schema Draft-07). The linter loads it dynamically from that note at runtime. This note and JSONHTL_SCHEMA must be kept in sync for structural changes. Semantic-only changes (e.g. adding a description to a field) may not require updating the other. When in doubt, update both.

Consumers -- Things That Use JSONHTL

Any structural change to the spec or schema may require updates in these:

The GUI renderer (notes_browser.py) currently accepts the wrong codeblock field name text as a fallback. This fallback should be removed once the linter is enforced at the write path. See Future Improvements.

What the Linter Checks

The linter first validates the document against the JSON Schema in JSONHTL_SCHEMA. It then applies additional checks that JSON Schema cannot express.

Document Level

Content

Blocks

para block

heading block

codeblock block

list block

table block

image / svg / details blocks (JSONHTL_SPEC base)

section block (nested container)

link inline element

code inline element

bold inline element (registered extension)

Checks Beyond JSON Schema

These cannot be expressed in JSON Schema and must be implemented in linter code:

Error Messages

When the linter finds a problem it should say what is wrong, what the correct form is, and where to find the spec. The intent is that an LLM or human writer can immediately update their source of information and not repeat the mistake.

Example for codeblock alias:

ERROR block 3 (codeblock): field 'text' is not valid -- the correct field name is 'body'. Where did you get this syntax from? The authoritative spec is JSONHTL_SCHEMA (note key) and gdata-server/linter. Update your source of information before writing further notes.

Example for unknown block type:

ERROR block 5: unknown block type 'bullet_list'. Valid types are: para, heading, codeblock, list, table, image, svg, details, section. Where did you get this syntax from? See JSONHTL_SCHEMA and gdata-server/linter.

Example for schema validation failure (generic):

ERROR block 2 (heading): missing required field 'level'. See JSONHTL_SCHEMA for the correct structure.

CLI Design

The linter is a Python command-line script. It fetches the schema from the live notes store (or accepts a local schema file for offline use), validates a document, and prints all errors to stdout.

# Lint a single note by key
python lint_jsonhtl.py --key gdata-server/linter

# Lint a local JSON file
python lint_jsonhtl.py --file /path/to/doc.json

# Walk all keys and lint each in turn
python lint_jsonhtl.py --all

# Use a local schema file instead of fetching from the store
python lint_jsonhtl.py --schema /path/to/schema.json --key some/note

# Exit code: 0 if clean, 1 if any errors found

The notes CLI loader (notes load) should enforce a linter pass before uploading a document. A --no-lint flag overrides this. This keeps bad documents out of the store at the write path.

Future Improvements

Implemented 2026-10-06

The gdata-server repository now provides lint_jsonhtl.py with --key, --file, --all, --schema, --url, --metadata and --links. It validates recursively using the live JSONHTL_SCHEMA by default and checks aliases, runnable fields/name uniqueness and table row widths. --metadata opts into all five notes-store fields; --links checks hrefs against the public key set and needs manual classification for private-store references. Offline mirror: jsonhtl_schema.json. Registry: README/jsonhtl-extensions. Write-path enforcement and automatic normalisation remain future work.

strong, em and italic are registered string-valued inline forms alongside bold. The schema accepts these consistently with all HTML and desktop consumers.

version 7  ·  updated 2026-10-06