# Dossier > Emit AI-written work reports as JSON: describe the measurements and claims, and the renderer derives the charts and totals. - Live: https://dossier.neorgon.com - Schema: https://dossier.neorgon.com/schema.json - Fixtures: https://dossier.neorgon.com/fixtures/ ## Emit this ```json { "dossier": { "version": 1, "title": "Rate limiter fix", "pages": [ { "title": "Overview", "blocks": [ { "type": "metrics", "title": "Measured results", "items": [ { "label": "p95 latency", "value": 182, "unit": "ms", "baseline": 340, "better": "lower" } ] } ] } ] } } ``` ## Blocks | Type | Purpose | Required fields | |------|---------|-----------------| | `heading` | A section break inside a page. Feeds the page outline. | text | | `prose` | A paragraph or two. Inline markdown only, never a block element. | text | | `callout` | One boxed sentence the reader must not scroll past. | text | | `quote` | Someone else's words, attributed. | text | | `steps` | A numbered procedure, in order. | items | | `kv` | A label and value list. The facts panel. | items | | `grid` | Lays its child blocks side by side. The only container. | blocks | | `metrics` | A row of headline numbers, each with its baseline and a derived delta. | items | | `chart` | Bars, lines, areas, a donut, a radar or a heatmap over stated series. | form, series | | `gauge` | One value against a range, as a ring or a banded meter. | value | | `scorecard` | Criteria with a verdict each. Pass, fail, partial. | criteria | | `table` | Rows and columns, sortable, with derived totals. | columns, rows | | `files` | The paths that changed, with churn per path and a derived total. | items | | `diff` | A unified patch, or a before and after of one file. | none | | `code` | A snippet, highlighted, with optional emphasised lines. | source | | `log` | Captured output, folded by default, with ANSI colour preserved. | source | | `checklist` | What was done, what was not, and why not. | items | | `timeline` | What happened when, in order, with a tone per entry. | items | | `findings` | Issues anchored to a file and a line, grouped by level. | items | | `tests` | A test run: counts, duration and the failures in full. | none | | `compare` | Two sides of the same thing, read together or wiped between. | kind, left, right | | `risks` | What could still go wrong, how bad, and what is in place. | items | | `actions` | What happens next, who owns it, and whether it is done. | items | | `decision` | The context, the choice, and what it costs. An ADR in one block. | context, decision, consequences | | `image` | A screenshot or a diagram, with a caption. | src, alt | | `embed` | Foreign HTML, rendered sandboxed in its own frame. | none | | `evidence` | A standalone list of artefacts, each with who checked it. | items | ## Rules - State what was measured. Do not compute from it. `delta`, `deltaPct`, `direction`, `totals`, `passed`, `done`, `stats`, `lines` and `id` are derived by the renderer, and a supplied one is reported and replaced. - Narrative fields (`text`, `note`, `detail`, `excerpt`) are plain strings. Write them normally. Only inline markdown renders: bold, italic, code and links. - Set `unit` on every number and `better` on every metric that has a baseline. Without `better` the arrow assumes higher is good, which is wrong for latency and error rates. - Nothing is rejected. An unknown block type renders as an error card naming the type, a malformed block renders as an error card in its place, and the rest of the report still renders. Unknown fields are reported once and carried through; put your own fields in `extra` to keep them quiet. - Say what you did not do. A skipped or blocked `checklist` item without a `note`, a `fail` in a `scorecard` without a `detail`, and a high `risks` entry without a `mitigation` are all reported. An unexplained gap reads as an oversight rather than a decision. ## Evidence, which is the point Any block takes an `evidence` array. The report header counts how many claims have none, so this is the field that decides whether a reader believes the rest. ```json { "type": "metrics", "items": [ { "label": "p95 latency", "value": 182, "unit": "ms", "baseline": 340, "better": "lower" } ], "evidence": [ { "label": "Load test run 8812, 200 rps for 60s", "kind": "run", "checkedBy": "code", "href": "https://example.invalid/runs/8812" }, { "label": "Assumed production traffic resembles staging", "kind": "other", "checkedBy": "llm", "confidence": "low" } ] } ``` `checkedBy` is the most useful field in the format and the one to be honest about: | Value | Means | |-------|-------| | `code` | A machine check that passed: a test run, a scan, a byte comparison. | | `human` | A person looked at it. | | `llm` | You asserted it. Rendered as the weaker thing it is, which is correct. | | `none` | Nothing checked it. Preferred to leaving the field out. | Mark your own reasoning `llm`. A report where everything claims `code` and one thing was actually inferred is worse than one that says so, because it makes the reader distrust the rest. ## Bringing other formats in These are read natively, so hand over the tool output rather than retyping it as prose: a `git diff --numstat` or `--name-status`, JUnit style XML, SARIF 2.1.0, a CTRF test report, a Lighthouse JSON report, a Conventional Commits log, Markdown, and plain HTML. The site detects which it got and converts it into blocks, then reports what it could not carry. ## Handing a report over - A link carries the whole report in its fragment: `#dz=`. Nothing is uploaded, and anyone with the link has the report. - `?src=` renders a document you host. - The one HTML file export opens with no account, no install and no network. That is the one to attach to a ticket. - Keep a link under about 8,000 characters. Above that some chat clients cut it, and the file is the better answer. ## Check it before you ship it ```bash node bin/validate.mjs ``` Exit codes: 0 clean, 1 findings, 2 could not run. ## What this cannot judge - Whether a number is correct. Only the format is checked. - Whether a claim is true. Evidence links are carried, not verified. - Whether a reader can understand it. Plain language is on you.