commit a8c8f0d2c127be1411c396b33f8b449df6d61d7b
parent 0284f2cfd67668c8b0a95a66190755cf314e1ba8
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 8 Oct 2026 22:58:51 -0400
umtool: docs/sites.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
| A | umtool/docs/sites.md | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 85 insertions(+), 0 deletions(-)
diff --git a/umtool/docs/sites.md b/umtool/docs/sites.md
@@ -0,0 +1,85 @@
+# /sites — every site's articles
+
+Every report on every site under `transcripts/sites/` — published and drafts —
+read in place: no private site built, no port served. umtool **reads** the sites;
+the one file it writes there is a report's `notes.json` (docs/notes.md).
+
+| page | what |
+|---|---|
+| `/sites` | every site (private first), every article: status, updated, citations, open notes, poster, video project, source draft. Filters are links: `?site=`, `?status=published\|draft`, `?notes=open` |
+| `/sites/<site>` | its articles, its report videos (playable), the umtool projects they were cut in (takes, like/maybe/no), the workspace files they were written from (`?ws=&rel=` opens one) |
+| `/sites/<site>/<report>` | the article with its notes (below) |
+| `/sites/<site>/<report>?tab=source` | the article's workspace files, its own draft opened |
+| `/sites/<site>/<report>/evidence` | every citation, one per screen (`?c=<id>`) |
+
+## Where an article comes from
+
+- **Site and reports**: common's `listSites`/`getSite` (site.json, the editor's
+ defaults), `listReportDirs` (the enumerator the editor's Reports tab uses), each
+ `report.json` through the report validator. **Published** = named in site.json
+ `reports`, in that order; every other report directory is a **draft**.
+- **Source** (`lib/articles/sources.mjs`): the draft under
+ `~/reports/<ws>/polemics/drafts/` whose `id` or file name is the report id,
+ `polemic-` allowed on either side, preferring a workspace whose generator names
+ the site; the generator is the `*.py`/`*.mts` under `<ws>/polemics` or
+ `<ws>/site` that names the site or the report. Shown with its reason; written
+ into `notes.json` `source` on the first note. **Edit the draft, not
+ report.json** — the generator overwrites report.json.
+- **Video project** (`lib/articles/links.mjs`): a report-video manifest with a
+ top-level `"article": "<site>/<report>"` is linked to that article and nothing
+ else (build-video.mjs ignores the key). Otherwise a manifest `slug` equal to the
+ report id (± `polemic-`) in the draft's workspace, when exactly one matches;
+ several are listed as "possible".
+
+## Reading and noting
+
+The article renders in a ~70ch column with its notes in a rail (a drawer below
+1100px). Its video sits under the title in one slot
+(`components/articles/ArticleVideo.tsx`).
+
+| to note | do |
+|---|---|
+| a passage | select it → **Note** (or `n`) |
+| a section | **+ note** on its heading |
+| a citation | click it → the evidence panel → **+ note on this citation** |
+| the article | **+ whole article** |
+
+A passage note keeps its quote and 32 characters either side; it is found again
+in the text as it is now (`lib/annotations/anchor.mjs`: exact, then
+whitespace/quote/case-normalised, then by its surviving context) and marked.
+One whose quote is gone is listed **orphaned**. Keys, when not typing: `j`/`k`
+move, `r` resolves, `e` edits, `n` notes, `Esc` closes. `?status=` and `?note=`
+open the rail on a filter or a note (the decisions inbox links to the note).
+**Copy agent brief** copies `/api/notes/context` — the `umtool notes` digest.
+
+Open notes are `open-note` rows in `/browse/decisions` and on the dashboard.
+
+## Evidence
+
+A citation's panel: its quote, speaker, date, label and original link; ±6 cues
+of its record's `transcript.cues.json`, the cited ones marked (click one to seek);
+and media, best first:
+
+1. the site's **prepared** clip (`.export-index/sites/<site>/report-media/`);
+2. a clip **window** the editor fetched (`data/<id>/clips/`);
+3. a **saved** video or audio file in `data/<id>/` (through its media-tier link);
+4. otherwise the `fetch_clip` MCP line to fetch it through the editor. umtool
+ never runs yt-dlp, and its own fetch client needs a project manifest.
+
+A post shows its text and its capture screenshot. The walk
+(`/evidence`) is the same panel one citation at a time: `j`/`k` (or ←/→),
+`space` plays, `n` notes.
+
+## Routes (read-only)
+
+| route | serves |
+|---|---|
+| `/api/sites/media?site&report&file` | a file in the report dir (video.mp4, poster.jpg, stills/…), byte ranges |
+| `/api/sites/media?site&moment` | the prepared clip of a moment, via report-media/index.json |
+| `/api/sites/media?corpus=<abs>` | a media file lexically under CHANNELS_DIR |
+| `/api/sites/evidence?site&report&cite` | one citation's evidence (JSON) |
+| `/api/sites/workspace?ws&rel` | a listed workspace file; HTML under a CSP sandbox |
+
+`SITES_DIR` (else `TRANSCRIPTS_DIR/sites`, else the checkout's
+`transcripts/sites`) is where all of it is read; the e2e suite points it at its
+fixture (`e2e/fixtures/sites-fixture.mjs`).