Files
lage-chat-control/migration/inventory/diff-report.md
T

159 lines
8.0 KiB
Markdown
Raw Normal View History

# Content-migration diff report
Legacy monolith `index.html` (591 tri-lingual span lines / 659 `data-l` spans per
language) → per-locale MDX under `src/content/sections/{en,fr,nl}/` + `src/data/tools.json`.
## How the inventories were produced
- `legacy-<lang>.txt` — the FULL legacy document reduced to one language
(every `data-l` run resolved), one line of whitespace-collapsed text per
block-level element. Chrome (topbar, hero, status banner, TOC, footer) is
included so nothing is hidden from this audit.
- `built-<lang>.txt` — same dump algorithm over `dist/index.html`,
`dist/fr/index.html`, `dist/nl/index.html` after `pnpm build`.
One deviation: `<noscript>` is skipped because parse5 exposes its children
as raw text (it is new chrome, absent from the legacy page anyway).
- `<b class="lbl">` / `<b class="tool-label">` elements are flushed to their
own line on both sides so tool-card prose lines stay comparable.
- Non-breaking spaces are folded into plain spaces on both sides.
- `node migration/compare.mjs` then segments each file into
head-chrome / narrative window / tail-chrome (narrative = first `PART 00 ·`
line through the manifesto paragraph) and walks the two narrative windows
**in order, line by line**. Every line must either match exactly or fall in
one of the categories below; anything else exits non-zero.
## Result
| lang | legacy narrative lines | built narrative lines | unexplained differences |
| ---- | ---------------------- | --------------------- | ----------------------- |
| en | 789 | 991 | **0** |
| fr | 789 | 991 | **0** |
| nl | 789 | 991 | **0** |
`991 = 789 6 (deferred legislative rows) + 208 (tool-card labels)` in every
language. Full machine output: `compare-report.json`.
## Explained differences, exhaustively
### 1. Tool-card labels are now real text (+208 lines per language)
In the legacy page the field labels ("What it's for / Why it matters /
For whom & when", `À quoi ça sert / Pourquoi / Pour qui & quand`,
`Waarvoor dient het / …`) and the `Install & use` summary were **CSS
`::before`/`::after` generated content**, so they never appeared in the DOM
text. `ToolCard.astro` renders them as real, accessible text:
54 cards × 3 field labels + 46 install summaries = 208 new lines per language.
The label strings are byte-identical to the legacy CSS `content` values.
### 2. Deferred legislative-status claims (6 lines per language)
Per the migration plan, adopted/extended claims are being rewritten from
verified sources separately. Dropped from the `#menace` comparison table
(rows 4 and 5), verbatim:
- EN: `Duration` / `Temporary, extended to 3 April 2028` / `Permanent` /
`Status (Jul 2026)` / `Adopted 9 July 2026` / `Under negotiation, 5th trilogue failed 29 June`
- FR: `Durée` / `Temporaire, prolongé jusqu'au 3 avril 2028` / `Permanent` /
`Statut (juil. 2026)` / `Adopté le 9 juillet 2026` / `En négociation, 5e trilogue échoué le 29 juin`
- NL: `Looptijd` / `Tijdelijk, verlengd tot 3 april 2028` / `Permanent` /
`Status (juli 2026)` / `Aangenomen op 9 juli 2026` / `In onderhandeling, 5e trialoog mislukt op 29 juni`
The status banner ("Dernière minute : 9 juillet 2026" block) was excluded
wholesale with the chrome (see §4); it is replaced by `StatusBanner.astro`
fed from verified sources in the parallel workstream.
Two borderline in-prose date claims were **kept** (judgment call — they are
narrative argument, not adopted/extended status): the 19 March 2026 joint
appeal, and the "kept scanning after the legal basis lapsed on 4 April 2026"
sentence, both in `#menace`.
### 3. Typographic quotes (smartypants)
Astro's default markdown pipeline (`markdown.smartypants`, ON by default)
typesets `"` as `“ ”` and `'` as `` in the rendered pages
(en: 138 lines affected, fr: 225, nl: 70). No other smartypants rule fires:
the legacy prose contains no ASCII `...` or `--` (its 3 `…` are literal and
pass through unchanged). This is the **only character-level transformation**
in the entire narrative.
It could not be disabled from inside this migration's ownership
(`astro.config.mjs` is off-limits). If byte-identical glyphs are required,
the config owner can add `markdown: { smartypants: false }` — one line — and
the compare gate can then drop its quote normalization.
### 4. Chrome not migrated (rebuilt separately)
Legacy head-chrome (36 lines/language) and tail-chrome (8 lines/language)
deliberately not migrated to MDX:
- topbar: brand `◆ EXIT CHAT CONTROL`, FR/NL/EN buttons, theme toggle;
- hero: stamp (`Mass surveillance · EU · 2026`), eyebrow, `Exit Chat Control_`
h1, lede paragraph, three profile meta tags;
- status banner: `Breaking: 9 July 2026` + the vote paragraph
(superseded content, see §2);
- TOC (24 lines) — duplicates the section titles that ARE migrated;
- footer: Resources / Sources / Act link lists, GitHub link, disclaimer
(`Facts last updated: 9 July 2026`).
The built pages already contain replacement chrome from the parallel
rebuild workstream (TopBar/Footer/StatusBanner components, new hero h1 +
description, skip-link, noscript note, rewritten source lists citing
official roll-call results). That text intentionally differs from the
legacy chrome and is **not** part of this migration's fidelity claim; it
sits outside the compared narrative window (built head-chrome: 6 lines,
tail-chrome: 9 lines).
- The legacy inline JS (lang/theme toggles, simpleicons CDN loader) was not
migrated by design. Icon rendering will be rebuilt offline later; the
legacy CDN slugs are preserved in `tools.json.iconSlug` (validated, §6).
### 5. NL gaps
None. Every one of the 649 `data-l` runs carries all three languages
(en/fr/nl counts are equal in every run), so the EN-fallback path never
fired. `extract-log.json → nlGaps: []`.
### 6. tools.json notes (language-neutral facts)
- 54 entries; every entry has `url` (first neutral footer link), full `links`
array (21 cards have 23 links), `monogram`, `color`, `profiles`
(the 🟢🟡🔴 footer note), `level` where the legacy card carried a level tag
(34 cards; the other 20 never had one).
- `iconSlug` was validated against the installed `simple-icons` package.
Renamed upstream and mapped: `tutanota → tuta`, `aegis → aegisauthenticator`.
Removed upstream and therefore set to null (6): `briar`, `molly`, `ivpn`,
`protonpass`, `nostr`, `standardnotes`. 5 cards never had an icon in the
legacy map (`t-bitchat`, `t-databrokers`, `t-mailbox`, `t-website`,
`t-yunohost`).
- 7 cards have localized display names (e.g. `Firefox (durci/hardened/gehard)`,
`Sauvegardes chiffrées / Encrypted backups / Versleutelde back-ups`).
`tools.json.name` stores the EN plain text; the localized display name is
rendered through the ToolCard `name` slot from each locale's MDX.
- One presentational attribute dropped: `style="margin:.6rem 0 .4rem"` on the
warning box inside `t-bitchat` (styling is rebuilt later; text intact).
### 7. Anchors
`anchors.json` lists all 82 `id=` values of the legacy file. 78 exist in
every built page: 23 section ids on `<section>` wrappers, 54 tool ids on
ToolCard `<article>` roots, `top` on the hero h1. The 4 missing are the
legacy control-widget ids `lang-fr`, `lang-nl`, `lang-en`, `theme`
(toggle buttons, never used as link targets — chrome rebuilt separately).
### 8. External domains for the allowlist review
The migrated content links to 75 unique external hostnames
(`external-domains.txt`). The parallel workstream's
`tests/unit/dist-links.test.ts` fails until these are reviewed and added to
`src/data/domains-allowlist.json` — that file requires review and is outside
this migration's ownership, so it was left untouched.
## Reproduce
```
node migration/extract.mjs # MDX + tools.json + anchors + legacy dumps
pnpm build
node migration/extract.mjs --built # built-page dumps
node migration/compare.mjs # exits non-zero on any unexplained diff
```