commit e504009d8bc78f96f9a392095822c9845f055a8f
parent 1d2c642eb95328a122175c94e02b8ad3d8e6af7d
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 9 Oct 2026 13:50:32 -0400
plans: release 22 planned — one article, two shapes: slides from the same report.json, a reader switch, the isometric overview, slides exports
Ruling 2026-10-09: articles may be built a bit differently (report.json gains
slide fields); the isometric overview pairs each section with its slide.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/PLAN.md b/PLAN.md
@@ -688,6 +688,6 @@ The phases above are the AI track. Later work is planned and recorded one releas
| 19 | agents run the archive: ops/CLI/MCP (Track A), machine safety and tooling (Track B), OPERATING.md and the docs (Track C) | [`plans/release-19.md`](plans/release-19.md) | in flight |
| 20 | the data model and the index: recorded dates, Twitch ids, the caption-track bug closed | [`plans/release-20.md`](plans/release-20.md) | planned, after 19 |
| 21 | playable archives: local media attached to held videos (clips cut locally), per-video torrents played in the page, a home seeder of last resort behind a VPN; pilot TISM on jeralyzer-private | [`plans/release-21.md`](plans/release-21.md) | planned 2026-10-09 |
-| 22 | — | — | not yet planned |
+| 22 | one article, two shapes: slides from the same report.json (authoring fields + a derived default), a reader switch Article / Slides / Overview, the isometric overview pairing sections with slides, `slides.html`/`slides.pdf` exports | [`plans/release-22.md`](plans/release-22.md) | planned 2026-10-09 |
Work that landed on `main` without a plan of its own: [`plans/landed-2026-10.md`](plans/landed-2026-10.md).
diff --git a/plans/release-22.md b/plans/release-22.md
@@ -0,0 +1,118 @@
+# Release 22 — one article, two shapes: slides, and the isometric overview
+
+Written 2026-10-09. An article (a `report.json`) can be read as the article it is, as a slide deck, or in an
+**isometric overview** that lays the two side by side in one tilted 3-D plane — each section beside its slide —
+so a reader sees the same article both ways and enters whichever they prefer at the same place.
+
+## Rulings (operator, 2026-10-09)
+
+- Articles may be **built a bit differently** to make this work: `report.json` gains authoring fields for slides.
+- *Assumption, open to correction:* "isometric render" is the overview described here (a tilted plane pairing
+ each article section with its slide, click either to enter that view), not a separate visual style.
+
+## What is there (verified)
+
+- An article is `report.json` (`common/lib/report/schema.ts`, `validate.ts`, REPORT.md): `kind` `factcheck | sweep`,
+ title/subtitle/summary/method, `sections[]` → `claims[]` (`text`, `verdict`, `gist`, `findings`, `citations`),
+ markdown bodies citing `[label](cite:<id>)`, `video`, `sources`, `citations`.
+- Two renderers of one view model (`ReportPageView`, `common/lib/report/views.ts`): the export site's
+ `export/app/components/reports/ReportArticle.tsx` (route `export/app/reports/[reportId]/page.tsx`, static) and
+ umtool's `ArticleReader.tsx`/`ArticleBody.tsx` (`umtool/app/sites/[site]/[report]/page.tsx`, with its own lenient
+ resolver `umtool/lib/articles/article.ts` `articleView` for drafts).
+- Shared citation components: `common/components/citations/*` (`CitedMarkdown`, `InlineCite`, `CitationCard`,
+ `ReferenceList`), `common/components/report/VerdictChip.tsx`; markdown via `markdown-to-jsx`.
+- Exports: `REPORT_EXPORT_FORMATS` (`views.ts`), `common/publish/reportExports.ts` (`report.html` self-contained via
+ `common/lib/report/exportHtml.ts`, `report.pdf` by Chromium), `reportExportFiles.ts`, the downloads table in
+ `ReportArticle.tsx`, the revision-history commit (`reportHistory.ts`).
+- View toggles: the transcript `vm`/`vt` URL params (`common/components/urlState.ts`; absent = the default view).
+- **Naming hazard:** "deck" means umtool's video chrome overlay (`umtool/report-to-video/deck.mjs`, `chrome-deck.mjs`)
+ and several editor card stacks. This feature is **slides** everywhere: `slides`, `SlideView`, `?rv=slides`.
+
+## Design
+
+### The article gains slide fields (schema, backward-compatible, `version` stays 1)
+
+- `report.slides?`: `{ hide?: boolean, title?: string, closing?: string }` — the deck's own title-slide line and
+ closing line; `hide: true` publishes no slides view.
+- `section.slide?` and `claim.slide?`: `{ title?: string, points?: string[], cite?: <citation id>, layout?:
+ "points" | "evidence" | "quote" | "statement", hide?: boolean }`. `points` are the slide's own words (≤ 5, each
+ ≤ 140 chars, may cite inline); `cite` picks the evidence the slide shows (a still, a post shot, a clip's poster
+ and quote); `layout` overrides the default.
+- The validator checks them (counts, lengths, a `cite` that names a citation of that section/claim, inline cites
+ that resolve) and REPORT.md is regenerated (`docs files`). An article with no slide fields still gets slides,
+ derived as below; the fields make them good.
+
+### One builder: `buildReportSlides(view): SlideView[]` (`common/lib/report/slides.ts`, pure)
+
+Every slide carries an **anchor** (`{kind: "head" | "summary" | "found" | "section" | "claim" | "sources", id?}`)
+that is also an id in the article's DOM — the one mapping the toggle, the overview and notes all use.
+
+| slide | from | default layout |
+|---|---|---|
+| title | series, title, subtitle, dates, subject, `report.slides.title` | `title` |
+| the quick take | `summary`'s first paragraph (or `points`) | `statement` |
+| what the check found | the verdict tally (`foundGroups`) — factcheck only | `found` |
+| one per section | `section.slide.points`, else the body's first two sentences | `points` |
+| one per claim | claim `text`, `VerdictChip`, `gist`, the `cite`d (else first) citation's evidence | `evidence` |
+| sources | the reference count, the site link, `report.slides.closing` | `sources` |
+
+`hide` drops a slide; a claim with no citation renders `statement`. Long markdown never lands on a slide: a slide
+shows `points`/`gist`/one quote, and every slide links "Read this in the article" (its anchor).
+
+### The reader (export site and umtool, shared components in `common/components/report/slides/`)
+
+- **View switch** on the article header: `Article | Slides | Overview`, a segmented control in the transcript
+ `modeStrip` pattern. URL param `rv` (`slides`, `iso`; absent = article) plus the anchor in the hash, so a shared
+ link opens the same view at the same place. Switching keeps the place: the article's section in view ↔ that
+ section's slide. The article HTML stays the statically rendered page (search engines and no-JS readers get the
+ article); slides and the overview render client-side from the same `page.json`, loaded on demand.
+- **Slides** (`ReportSlides.tsx`): one 16:9 stage letterboxed in the viewport, scaled type; ←/→, PgUp/PgDn,
+ Home/End, Esc (back to the article at this slide's anchor), swipe on touch; a progress rail; slide number in
+ the hash (`#s-4`). Citations stay live (the `CitationsProvider` of the page): a cite opens its card. Per-site
+ accent and light/dark from the existing tokens. Phones: the stage fits the width; portrait shows the slide's
+ text at readable size with its evidence below.
+- **Overview** (`ReportOverview.tsx`): one plane tilted isometrically (CSS `rotateX(55deg) rotateZ(-45deg)`),
+ the article's sections as a column of page-blocks on the left, the slides as a column of 16:9 cards on the
+ right, a connector between each pair. Hover/focus a pair lights both; click the block to open the article at
+ it, the card to open the slides at it; arrow keys walk pairs. `prefers-reduced-motion`, narrow screens and
+ keyboard-first users get the same pairs flat (two columns, or stacked pairs on a phone) — the tilt is never the
+ only way to read it.
+- **umtool** `ArticleReader` gets the same switch over its lenient `articleView`, so a draft can be checked in all
+ three shapes before publishing; a slide shows the note count of its anchor, and the source tab's lint lists slide
+ problems (a section with no `points` whose body's first sentences run long, an over-long point).
+
+### Exports
+
+- `slides.html` (self-contained like `report.html`: inline style + a few lines of inline script for keys, images
+ inlined) and `slides.pdf` (Chromium, one 16:9 page per slide) join `REPORT_EXPORT_FORMATS`, the downloads table,
+ `evidence-pack.zip` and the revision commit's `exports.json`. Same 24 MiB rule.
+
+## Slices (Track E)
+
+| slice | class | what |
+|---|---|---|
+| **E1** schema + builder | `[unit]` | the slide fields in `schema.ts`/`validate.ts`, REPORT.md regenerated; `slides.ts` `buildReportSlides` with a table of tests (every default, every override, `hide`, a sweep with no tally, a claim with no citation, anchors unique and matching the article's ids); the demo fixture report gains slide fields |
+| **E2** slides view | `[unit]` + `[spec]` | `common/components/report/slides/` (`ReportSlides`, slide layouts, `ViewSwitch`), wired into `ReportArticle.tsx` and the export route; article anchors on sections/claims; `rv` + hash state |
+| **E3** overview | `[unit]` + `[spec]` | `ReportOverview` (isometric + flat fallbacks), pair navigation |
+| **E4** umtool | `[spec umtool]` | the switch in `ArticleReader`, note counts per slide, slide lint in the source tab |
+| **E5** exports | `[unit]` | `slides.html`, `slides.pdf`, downloads table, evidence pack, history |
+| **E6** records | `[none]` | this file's record, changelogs (export, umtool), REPORT.md, PUBLISH.md's exports paragraph |
+
+E1 → E2 → E3; E1 → E4; E1 → E5. e2e: the export report suite (`pnpm --filter export run e2e:report`) gains slides
+and overview cases over the fixture (switch keeps the place, keys, a cite opens its card, the reduced-motion
+overview, a phone viewport) — run once at E3 and once at E5; umtool's article specs once at E4.
+
+## Do not touch
+
+`umtool/report-to-video/**` (the Candace session's); the editor; `transcripts/**`. Published reports change only
+when a site is rebuilt — nothing here rewrites a report.json in the corpus.
+
+## Record
+
+### Track E
+
+## Rollout
+
+Rebuild and deploy each site with reports (the slides view is in the bundle, and `reports prepare` re-exports to
+add `slides.html`/`slides.pdf`). Authors add `slide` fields at their own pace; an article without them still has
+derived slides.