docs: plan internal migration comments
This commit is contained in:
parent
48ff53bda8
commit
bf2016ce85
1 changed files with 266 additions and 0 deletions
266
docs/superpowers/plans/2026-08-03-internal-migration-comments.md
Normal file
266
docs/superpowers/plans/2026-08-03-internal-migration-comments.md
Normal file
|
|
@ -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 `<main>` 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'),
|
||||
'<main><h1>Visible</h1><p>This page is awaiting content.</p></main>',
|
||||
);
|
||||
await writeFile(
|
||||
join(root, 'commented/index.html'),
|
||||
'<main><h1>Commented</h1><!-- This page is awaiting content. --></main>',
|
||||
);
|
||||
|
||||
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 `<main>`:
|
||||
|
||||
```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,
|
||||
/<!-- Migration note: The previous wiki said course summaries and old exams were in corresponding folders, but those files were absent from the export\. -->/,
|
||||
);
|
||||
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(/<!-- Maintainer note: This source (?:page|section) was .*? -->/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(`<!-- Migration note: The source presented the ${year} schedule as study:dke-schedule\\.png, but the asset was absent from the export\\. -->`),
|
||||
);
|
||||
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
|
||||
<!-- Migration note: The previous wiki said course summaries and old exams were in corresponding folders, but those files were absent from the export. -->
|
||||
```
|
||||
|
||||
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
|
||||
<!-- Migration note: The source presented the Year 1 schedule as study:dke-schedule.png, but the asset was absent from the export. -->
|
||||
```
|
||||
|
||||
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
|
||||
<!-- Maintainer note: This source page was under construction and remains intentionally empty. -->
|
||||
```
|
||||
|
||||
Under both empty Handy Locations headings, replace the callout with:
|
||||
|
||||
```md
|
||||
<!-- Maintainer note: This source section was empty and is awaiting content. -->
|
||||
```
|
||||
|
||||
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"
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue