From bf2016ce85de4cd685bd67a735941289c7502ce2 Mon Sep 17 00:00:00 2001 From: msa46 Date: Mon, 3 Aug 2026 12:50:12 +0200 Subject: [PATCH] docs: plan internal migration comments --- .../2026-08-03-internal-migration-comments.md | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-03-internal-migration-comments.md diff --git a/docs/superpowers/plans/2026-08-03-internal-migration-comments.md b/docs/superpowers/plans/2026-08-03-internal-migration-comments.md new file mode 100644 index 0000000..90bfadd --- /dev/null +++ b/docs/superpowers/plans/2026-08-03-internal-migration-comments.md @@ -0,0 +1,266 @@ +# Internal Migration Comments Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Keep migration/editorial absence notes in the Markdown source while preventing them from appearing to public wiki readers. + +**Architecture:** Add a rendered-output invariant that examines visible `
` text and reports internal-only notice phrases. Then convert the approved notices to HTML comments, leaving the affected pages and headings intact while retaining historical and reader-actionable warnings. + +**Tech Stack:** Astro 7, Starlight 0.41, Markdown/MDX, Node.js 22 test runner, LinkeDOM. + +## Global Constraints + +- Historical-information cautions, the 2022 study-abroad warning, historical course-list notes, and reader-actionable network-use warnings remain public. +- Awaiting-content notices, missing-source-asset notices, and absent summaries/exams notices become HTML comments. +- Keep all affected pages, empty course pages, and section headings. +- Do not publish anything under `to-be-studied/`. +- Use test-first changes and run the full repository verification before completion. + +--- + +### Task 1: Reject internal-only notices in rendered page text + +**Files:** +- Modify: `tests/rendered-output.test.mjs` +- Modify: `scripts/lib/rendered-output.mjs` +- Modify: `scripts/check-rendered-output.mjs` + +**Interfaces:** +- Consumes: `checkRenderedOutput({ distRoot: string })` from `scripts/lib/rendered-output.mjs`. +- Produces: `internalNoticeIssues: Array<{ file: string, phrase: string }>` on the existing rendered-output result. + +- [ ] **Step 1: Write the failing rendered-behaviour test** + +Add this test to `tests/rendered-output.test.mjs`. The production change it catches is an internal migration notice being reintroduced as reader-visible page text; an HTML comment containing the same text must remain allowed. + +```js +test('reports internal-only migration notices only when they are visible', async () => { + await withRenderedSite(async (root) => { + await mkdir(join(root, 'visible'), { recursive: true }); + await mkdir(join(root, 'commented'), { recursive: true }); + await writeFile( + join(root, 'visible/index.html'), + '

Visible

This page is awaiting content.

', + ); + await writeFile( + join(root, 'commented/index.html'), + '

Commented

', + ); + + const result = await checkRenderedOutput({ distRoot: root }); + + assert.deepEqual(result.internalNoticeIssues, [ + { file: 'visible/index.html', phrase: 'awaiting content' }, + ]); + }); +}); +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: `node --test tests/rendered-output.test.mjs` + +Expected: FAIL because `result.internalNoticeIssues` is `undefined`. + +- [ ] **Step 3: Implement visible-text detection** + +In `scripts/lib/rendered-output.mjs`, add the internal-only phrase contract and scan only the rendered text under `
`: + +```js +const INTERNAL_ONLY_PUBLIC_PHRASES = [ + 'awaiting content', + 'missing source asset', + 'not present in the export', + 'not included in the export', +]; +``` + +Initialize `internalNoticeIssues = []`. In the existing HTML loop, after parsing the document, derive: + +```js +const visibleMainText = document.querySelector('main')?.textContent.toLowerCase() ?? ''; +for (const phrase of INTERNAL_ONLY_PUBLIC_PHRASES) { + if (visibleMainText.includes(phrase)) internalNoticeIssues.push({ file: path, phrase }); +} +``` + +Return `internalNoticeIssues` with the existing result fields. + +In `scripts/check-rendered-output.mjs`, append issues using: + +```js +...result.internalNoticeIssues.map( + ({ file, phrase }) => `${file} exposes internal-only notice text: ${phrase}`, +), +``` + +Update the success message to include `no internal-only notices`. + +- [ ] **Step 4: Run the focused test and verify GREEN** + +Run: `node --test tests/rendered-output.test.mjs` + +Expected: all rendered-output tests PASS. + +- [ ] **Step 5: Commit the rendered-output guard** + +```bash +git add tests/rendered-output.test.mjs scripts/lib/rendered-output.mjs scripts/check-rendered-output.mjs +git commit -m "test: reject public migration notices" +``` + +--- + +### Task 2: Move editorial absence notices into source comments + +**Files:** +- Modify: `tests/content-audit.test.mjs` +- Modify: `src/content/docs/bachelor/index.md` +- Modify: `src/content/docs/master-ai/index.md` +- Modify: `src/content/docs/master-dsdm/index.md` +- Modify: `src/content/docs/bachelor/year-1/index.md` +- Modify: `src/content/docs/bachelor/year-2/index.md` +- Modify: `src/content/docs/bachelor/year-2/honours-programme.md` +- Modify: `src/content/docs/bachelor/year-3/honours-programme.md` +- Modify: `src/content/docs/useful-information/handy-locations.md` +- Modify: `docs/migration-report.md` + +**Interfaces:** +- Consumes: the internal-only phrase contract added in Task 1 and the existing Starlight content tree. +- Produces: the same public routes and headings, with migration state retained only as HTML comments. + +- [ ] **Step 1: Replace the old source-content assertions with failing comment-contract tests** + +Replace `programme overviews disclose that promised summaries and exams were absent from the export` with: + +```js +test('programme overview migration omissions are maintainer-only comments', async () => { + for (const destination of [ + 'src/content/docs/bachelor/index.md', + 'src/content/docs/master-ai/index.md', + 'src/content/docs/master-dsdm/index.md', + ]) { + const content = await readFile(destination, 'utf8'); + assert.match( + content, + //, + ); + assert.doesNotMatch(content, /:::.*(?:summaries|exams)/i); + } +}); +``` + +Replace the three awaiting-content tests with one table-driven test: + +```js +test('empty migrated content keeps maintainer comments instead of public notes', async () => { + const expectations = [ + ['src/content/docs/bachelor/year-2/honours-programme.md', 1], + ['src/content/docs/bachelor/year-3/honours-programme.md', 1], + ['src/content/docs/useful-information/handy-locations.md', 2], + ]; + + for (const [destination, expectedComments] of expectations) { + const content = await readFile(destination, 'utf8'); + assert.equal( + content.match(//g)?.length, + expectedComments, + ); + assert.doesNotMatch(content, /:::note\[Awaiting content\]/); + } +}); +``` + +Add the missing-asset source contract: + +```js +test('missing schedule assets are documented only in maintainer comments', async () => { + for (const [destination, year] of [ + ['src/content/docs/bachelor/year-1/index.md', 'Year 1'], + ['src/content/docs/bachelor/year-2/index.md', 'Year 2'], + ]) { + const content = await readFile(destination, 'utf8'); + assert.match( + content, + new RegExp(``), + ); + assert.match(content, /## Schedule/); + assert.doesNotMatch(content, /:::danger\[Missing source asset\]/); + } +}); +``` + +- [ ] **Step 2: Run the content tests and verify RED** + +Run: `node --test tests/content-audit.test.mjs` + +Expected: FAIL because the approved notices are still public callouts or prose rather than HTML comments. + +- [ ] **Step 3: Convert programme omission prose to comments** + +On each programme overview, retain only the public programme-description sentence, then add: + +```md + +``` + +Do not remove the historical caution or the current Maastricht University education link. + +- [ ] **Step 4: Convert schedule asset notices to comments** + +Under each existing `## Schedule` heading, remove the visible schedule prose and danger callout and add the corresponding year-specific comment: + +```md + +``` + +Use `Year 2` in the second page. Preserve both headings and project links. + +- [ ] **Step 5: Convert empty-page and empty-section notices to comments** + +Replace each honours-programme callout with: + +```md + +``` + +Under both empty Handy Locations headings, replace the callout with: + +```md + +``` + +Preserve the historical cautions and all headings. + +- [ ] **Step 6: Reconcile the migration report** + +Update the affected rows in `docs/migration-report.md` so they state that summaries/exams, schedule assets, under-construction pages, and empty sections are retained as internal source comments rather than public notices. Do not change the 29-source/28-destination accounting or link-handling classifications. + +- [ ] **Step 7: Run focused tests and the full verification suite** + +Run: `node --test tests/content-audit.test.mjs tests/rendered-output.test.mjs` + +Expected: all focused tests PASS. + +Run: `npm run verify` + +Expected: Astro reports zero diagnostics; all Node tests pass; the content audit reports 29 sources, 28 destinations, and 28 published pages; the build succeeds; rendered-output and link checks report zero issues. + +- [ ] **Step 8: Inspect representative public routes** + +With the existing dev server running, reload and inspect: + +- `http://127.0.0.1:4321/bachelor/` +- `http://127.0.0.1:4321/bachelor/year-1/` +- `http://127.0.0.1:4321/bachelor/year-2/honours-programme/` +- `http://127.0.0.1:4321/useful-information/handy-locations/` +- `http://127.0.0.1:4321/bachelor/year-3/study-abroad/` + +Confirm the targeted editorial notices are absent, the affected routes/headings still exist, and the historical/2022 cautions remain visible. + +- [ ] **Step 9: Commit the source-comment conversion** + +```bash +git add tests/content-audit.test.mjs src/content/docs docs/migration-report.md +git commit -m "fix: keep migration notices internal" +```