# report.json keys One cited report, format `"archilyzer-report"`, version 1, persisted to `transcripts/sites//reports//report.json` beside its `stills/` and `sources//`; a relative path in it is relative to that directory. A site's `reports` list in `site.json` is the published, ordered list — see [SITE.md](SITE.md); a report directory it does not name is a draft. The schema is `common/lib/report/schema.ts`; its citations and sources are the citation model's — see [CITATIONS.md](CITATIONS.md). A `notes.json` beside it holds the operator's notes on the article (umtool, `umtool notes`; [umtool/docs/notes.md](umtool/docs/notes.md)) and is never published. A **fact-check** (`"kind": "factcheck"`) is sections (chapters) of claims, each with a verdict and its findings. A **sweep** (`"kind": "sweep"`) is sections with no verdicts, or bodies that cite inline. Either may keep a **timeline** (`entries`): dated entries appended over time. Markdown fields (`summary`, an entry's `body`, a section's `body`, a claim's `findings`) cite with `[label](cite:)`. `common/lib/report/validate.ts` reports every problem with its JSON path: an unknown key, a reference that names nothing (a listed citation, a claim's source sentence, a `cite:` link, the subject), a section, claim or entry id used twice (they share one namespace: the report page's anchors) or named for one of the page's own anchors, a sweep's claim with a verdict, `updated` before `published` (or an entry's before its `date`), a date that is not one, a slide field out of bounds (below), and every citation problem CITATIONS.md lists. Whether a still or the report's video exists, whether the video fits the publish limit, and whether a quote matches its cues are checked when the site is composed. Regenerate this file with `pnpm --filter yt-dlp-transcript-common exec tsx bin/file-schemas-docs.ts`. ## The document | Key | Required | Description | |---|---|---| | `format` | yes | `"archilyzer-report"`. | | `version` | yes | `1`. | | `id` | yes | The report's id: a lowercase slug (`[a-z0-9][a-z0-9-]*`, at most 64), its directory name under `reports/` and the last segment of its page, `/reports//`. Must match the directory. | | `kind` | yes | `"factcheck"` — sections of claims, each with a verdict — or `"sweep"` — sections with no verdicts, or bodies with inline citations. | | `series` | no | The series the report belongs to: a recurring name for its kind of report. It is shown on its own line above the title, in the accent colour, at the head of the report's page, its exports and the report list (where a report with no series shows its kind instead), and is joined to the title as `: ` where one line names the report (a page title, a link, a cited-in entry). | | `title` | yes | The report's title. | | `subtitle` | no | A line under the title. | | `summary` | no | The report's summary, in markdown, shown before the sections. May cite inline: `[label](cite:<id>)`. | | `method` | no | How the report was checked, in markdown (short; it cites nothing): shown under "How it was checked" at the start of the claims in full. | | `published` | no | When the report was published: `YYYY-MM-DD` or an ISO 8601 date-time with a zone. | | `updated` | no | When it was last changed, in the same form; not before `published`. A report with a timeline is shown as updated at its newest entry's `date` or `updated` when that is later. | | `subject` | no | The document under review, when the report reviews one: `{ "source": "<id>" }`, an id in `sources`. | | `video` | no | A video of the report, shown at the head of its page under the title: `{ "src": "video.mp4", "poster": "poster.jpg", "caption": "…" }`. `src` is an mp4 and `poster` an image (png, jpg or webp), both relative to the report's directory; the caption is one line. Absent = none. | | `verdicts` | no | Overrides of the shared verdict vocabulary's labels and colours, by verdict (`CORROBORATED`, `PARTLY`, `CONTRADICTED`, `NOT_FOUND`, `UNTESTABLE`): `{ "label": "…", "color": "#rrggbb" }`, each key optional. Absent = the shared defaults. | | `sources` | no | The documents the report's `source` citations quote, by id — see [CITATIONS.md](CITATIONS.md). Absent = none. | | `citations` | no | The report's citations, by id — see [CITATIONS.md](CITATIONS.md). A citation is cited from markdown with `[label](cite:<id>)` and listed under the claims that rest on it. Absent = none. | | `sections` | yes | The report's sections, in order. | | `entries` | no | The report's timeline: dated entries, appended over time and shown newest first under "Timeline", before the claims — see `entries[]` below. With entries and a public `siteUrl`, the report publishes feeds of them (`feed.xml`, RSS 2.0; `feed.json`, JSON Feed 1.1). Absent = none. | | `slides` | no | How the report reads as slides (its page's Slides and Overview views, `slides.html`, `slides.pdf`) — see `slides` below. Absent = slides derived from its text. | #### `sections[]` | Key | Required | Description | |---|---|---| | `id` | yes | The section's id (letters, digits, `_ . : -`; at most 64): its anchor on the report page. Unique among the report's section, claim and entry ids, and none of the page's own anchors (`report-head`, `in-brief`, `found`, `claims`, `references`, `downloads`, `report-end`). | | `title` | yes | The section's heading. | | `body` | no | Markdown under the heading. May cite inline. | | `claims` | no | The section's claims, in order. Absent = none. | | `slide` | no | The section's slide — see `slide` below. Absent = derived from its text. | #### `sections[].claims[]` | Key | Required | Description | |---|---|---| | `id` | yes | The claim's id (letters, digits, `_ . : -`; at most 64): its anchor on the report page. Unique among the report's section, claim and entry ids, and none of the page's own anchors (`report-head`, `in-brief`, `found`, `claims`, `references`, `downloads`, `report-end`). | | `title` | no | A short headline for the claim (plain text, e.g. a phrase it turns on), shown above its text. Absent = the text alone. | | `text` | yes | The claim, as stated by the document under review (plain text). | | `verdict` | no | The ruling on the claim: `CORROBORATED`, `PARTLY`, `CONTRADICTED`, `NOT_FOUND`, `UNTESTABLE`. A fact-check's claim may leave it out (not yet ruled); a sweep's carries none. | | `gist` | no | The claim's finding in one line (plain text, at most 240 characters), shown beside its title in the overview of what the check found. Absent = the title alone. | | `flag` | no | A short mark on the claim (plain text, one line, at most 60 characters), shown as a pill on its card: e.g. that the document gives no source for it. Absent = none. | | `sourceQuote` | no | The document's own sentence making the claim: `{ "citation": "<id>" }`, naming a `source` citation (its still is shown with the claim). | | `findings` | no | What the evidence shows, in markdown, citing inline: `[label](cite:<id>)`. | | `citations` | no | The citations the claim rests on, in the order they are listed under it. Each must exist; none twice. | | `slide` | no | The claim's slide — see `slide` below. Absent = derived from the claim. | ## The timeline A report may keep a timeline: `entries`, dated blocks a reader sees newest first under **Timeline**, after the summary and before what the check found and the claims — on its page, in its exports and as one slide each. Entries of the same instant keep their order in the file. Each entry's id is its anchor (`#<id>`). Its citations are numbered with the rest, in the page's order. A report with a newer entry than its own `updated` is shown as updated then (the index, the header, the feeds). On a site with a public `siteUrl`, compose publishes the timeline as feeds beside the page — `/reports/<id>/feed.xml` (RSS 2.0) and `/reports/<id>/feed.json` (JSON Feed 1.1), one item per entry, its permalink the page at its anchor — and the page links them ("Subscribe", and `<link rel="alternate">`). A site with no `siteUrl` publishes no feed. #### `entries[]` | Key | Required | Description | |---|---|---| | `id` | yes | The entry's id (letters, digits, `_ . : -`; at most 64): its anchor on the report page and its permalink in the feeds. Unique among the report's section, claim and entry ids, and none of the page's own anchors (`report-head`, `in-brief`, `found`, `claims`, `references`, `downloads`, `report-end`). | | `date` | yes | When the entry was added: `YYYY-MM-DD` or an ISO 8601 date-time with a zone. The timeline is newest first by this date; entries of the same instant keep their order here. | | `title` | yes | The entry's heading (plain text, one line). | | `body` | yes | The entry, in markdown. May cite inline, `[label](cite:<id>)`, as a section's body does; its citations are numbered with the rest, in the page's order (the timeline comes after the summary). | | `updated` | no | When the entry was last changed, in the same form; not before its `date`. Absent = never. | ## Slides A report is also read as slides — its page's **Slides** view (`?rv=slides`), the **Overview** that pairs each part of the article with its slide (`?rv=iso`), `slides.html` and `slides.pdf` — built by `common/lib/report/slides.ts` from the same view as the article: a title slide, "In brief", one per timeline entry (newest first), "What the check found" (a fact-check), one slide per section and per claim, then a sources slide. With no slide fields a report still has slides, derived from its text: a section shows the first two sentences of its body (else its claims), a claim its verdict, gist and the evidence of its first listed citation. The fields below make them good. A slide's points cite only what the article cites elsewhere, and its `cite` names a citation of its own section or claim. Every slide links back to its place in the article (the section's or the claim's anchor). #### `slides` | Key | Required | Description | |---|---|---| | `hide` | no | `true` publishes no slides: the page offers no Slides or Overview view, and no `slides.html` or `slides.pdf` is exported. | | `title` | no | The title slide's line under the report's title (plain text, one line, at most 160 characters). Absent = the subtitle. | | `points` | no | The quick take's slide, as points (at most 5, each one line of at most 140 characters, the label of an inline citation counted; may cite inline a citation the report cites elsewhere). Absent = the summary's first paragraph. | | `closing` | no | The sources slide's closing line (plain text, one line, at most 160 characters). Absent = none. | #### `sections[].slide` and `sections[].claims[].slide` | Key | Required | Description | |---|---|---| | `title` | no | The slide's title (plain text, one line, at most 160 characters). Absent = the section's title; the claim's title, else its text. | | `points` | no | The slide's own words, as points (at most 5, each one line of at most 140 characters, the label of an inline citation counted). May cite inline, `[label](cite:<id>)`, a citation the report cites elsewhere. Absent = a section's slide shows the first two sentences of its body (else its claims); a claim's shows its gist. | | `cite` | no | The evidence the slide shows (a still, a post's capture, a clip's poster, with its quote): a citation of this section (its body or its claims) or of this claim (its sentence, findings or list). Absent = a claim's first listed citation; a section shows none. | | `layout` | no | How the slide is laid out: `points`, `evidence`, `quote`, `statement`. Absent = `points` for a section; `evidence` for a claim, `statement` when it has no citation to show. `evidence` and `quote` need a citation. | | `hide` | no | `true` leaves this section's or claim's slide out (a section's claims keep theirs). | ## The verdicts The shared vocabulary (`common/lib/report/verdicts.mjs` — the one copy; report-to-video's fact-check stamps read it too), in the order a tally lists them. A report's `verdicts` overrides a label (one line, at most 24 characters) or a colour (`#rgb` or `#rrggbb`), each on its own. | Verdict | Default label | Default colour | |---|---|---| | `CORROBORATED` | Corroborated | `#3fbf7f` | | `PARTLY` | Partly true | `#e3b23c` | | `CONTRADICTED` | Contradicted | `#e5534b` | | `NOT_FOUND` | Not found | `#8b93a7` | | `UNTESTABLE` | Untestable | `#7d8fd6` | ## Making a report from what exists `archilyzer reports convert <sweep|ask|manifest> <in> --out <report.json>` makes a report of a /sweep report (markdown: its `#` heading the title, each `##` section a section, each list item or paragraph that cites a claim), an /ask answer (its `[n @ mm:ss]` markers resolved against its sources) or a report-to-video manifest (chapter cards the sections, `claim` entries the claims, clips, stills and posts the citations), and writes it only when it validates. A /sweep or /ask citation carries one second: `--channels-dir <transcripts/channels>` widens it through the record's cues to whole sentences (else it is the second plus 10 s) and finds the channel that keeps a post cited by its platform link. `archilyzer reports to-manifest <report.json> --out <manifest.json>` goes the other way: a starter manifest — a title card, a chapter card per section, each claim's still and clips stamped with its verdict, its posts. The converters are `common/lib/report/convert*.ts`; the warnings say what a conversion left out.