Compare commits

...
Sign in to create a new pull request.

24 commits

Author SHA1 Message Date
msa46
aa28e1ff21 Open exam PDFs in new tabs
Some checks failed
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2026-09-18 09:20:36 +02:00
msa46
edca813c20 Update laptop buying advice for 2026
Some checks failed
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2026-08-24 11:45:48 +02:00
msa46
c723d46ebf feat: compact programme selector 2026-08-16 12:03:59 +02:00
msa46
a0e9d2c042 feat: focus programme sidebar navigation 2026-08-16 11:57:16 +02:00
msa46
f9abeb4b3d docs: plan compact programme switch 2026-08-16 11:23:43 +02:00
msa46
287ba66e70 docs: design compact programme switch 2026-08-16 10:48:32 +02:00
msa46
1d4d832b6c fix: verify programme routes across deployment bases
Some checks failed
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled
2026-08-15 21:09:25 +02:00
msa46
8c471fc0fa feat: add route-aware programme navigation 2026-08-15 21:04:38 +02:00
msa46
6c3900a1d4 refactor: share common bachelor course content 2026-08-15 20:59:56 +02:00
msa46
bceac4ad17 feat: publish reviewed computer science courses 2026-08-15 20:58:12 +02:00
msa46
808f10037f feat: redirect legacy bachelor routes to data science 2026-08-15 20:55:56 +02:00
msa46
10eead03da refactor: give data science canonical programme routes 2026-08-15 20:53:56 +02:00
msa46
16a33159fe docs: plan programme course navigation implementation 2026-08-15 20:51:46 +02:00
msa46
b8126a5a21 docs: design programme course navigation 2026-08-15 20:41:56 +02:00
msa46
84d213a9f6 feat: integrate privacy-first Matomo analytics
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-08-15 15:06:36 +02:00
msa46
8395e5b934 docs: plan privacy-first Matomo integration 2026-08-15 15:06:26 +02:00
msa46
f007aad4c1 feat: add consent-gated Matomo controller 2026-08-15 15:03:00 +02:00
msa46
c9ac8e1e21 docs: design consent-gated Matomo tracking 2026-08-15 14:30:39 +02:00
msa46
55136f51d0 docs: record useful guides publication 2026-08-11 18:57:08 +02:00
msa46
a9554ec756 content: expose useful guides in navigation 2026-08-11 18:54:52 +02:00
msa46
f5b7dfa949 content: publish practical student guides 2026-08-11 18:53:52 +02:00
msa46
8ed1892a3f content: update laptop buying guidance 2026-08-11 18:51:16 +02:00
msa46
05874e0fe6 docs: plan useful guides publication 2026-08-11 18:44:03 +02:00
msa46
25e5a4b739 docs: design useful guides publication 2026-08-11 18:32:36 +02:00
127 changed files with 4609 additions and 267 deletions

View file

@ -63,3 +63,17 @@ SITE=https://example.github.io BASE=/Incognito-Wiki npm run build
Netlify reads `netlify.toml`, runs `npm run build`, and publishes `dist`. Configure `SITE` and `BASE` as environment values for the selected production hostname and path. Netlify reads `netlify.toml`, runs `npm run build`, and publishes `dist`. Configure `SITE` and `BASE` as environment values for the selected production hostname and path.
The removable workflow at `.github/workflows/deploy.yml` optionally deploys the GitHub mirror to Pages after a push to `main` or a manual dispatch. Set the public repository variables `SITE_URL` and `BASE_PATH` on GitHub. The workflow is GitHub-specific; Forgejo ignores it, and removing the workflow or `netlify.toml` does not affect local development or verification. The removable workflow at `.github/workflows/deploy.yml` optionally deploys the GitHub mirror to Pages after a push to `main` or a manual dispatch. Set the public repository variables `SITE_URL` and `BASE_PATH` on GitHub. The workflow is GitHub-specific; Forgejo ignores it, and removing the workflow or `netlify.toml` does not affect local development or verification.
## Matomo analytics
The wiki uses self-hosted Matomo only after a visitor explicitly accepts analytics. The client is loaded from `https://analytics.msvincognito.nl/`, uses site ID `1`, and disables analytics cookies. Visitors can decline without losing functionality and can reopen **Privacy settings** to withdraw consent.
Before deploying analytics changes, the Matomo administrator must verify:
- IP anonymization is enabled;
- raw logs and analytics reports use documented, proportionate retention periods;
- administrator access is restricted and reviewed;
- analytics data is not reused for advertising or cross-site profiling; and
- the [MSV Incognito privacy policy](https://msvincognito.nl/privacy-policy) accurately states the controller, purpose, data categories, retention, withdrawal process, and data-subject rights.
The frontend consent gate is only one part of GDPR and ePrivacy compliance. Revisit the legal and server configuration when Matomo features or processing purposes change.

View file

@ -1,13 +1,30 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight'; import starlight from '@astrojs/starlight';
import { unified } from '@astrojs/markdown-remark';
import { sidebar } from './src/config/sidebar.mjs'; import { sidebar } from './src/config/sidebar.mjs';
import { legacyBachelorRedirects } from './src/config/legacy-bachelor-redirects.mjs';
import { baseAwareLinks } from './src/config/base-aware-markdown.mjs';
import { openExamLinksInNewTab } from './src/config/open-exam-links.mjs';
const site = process.env.SITE || 'http://localhost:4321'; const site = process.env.SITE || 'http://localhost:4321';
const base = process.env.BASE || '/'; const base = process.env.BASE || '/';
const normalizedBase = base === '/'
? ''
: `/${base.replace(/^\/+|\/+$/g, '')}`;
const matomoConsentScript = `${normalizedBase}/matomo-consent.js`;
export default defineConfig({ export default defineConfig({
site, site,
base, base,
redirects: Object.fromEntries(Object.entries(legacyBachelorRedirects).map(([from, to]) => [from, `${normalizedBase}${to}`])),
markdown: {
processor: unified({
remarkPlugins: [
[baseAwareLinks, { base }],
openExamLinksInNewTab,
],
}),
},
integrations: [ integrations: [
starlight({ starlight({
title: 'Incognito Wiki', title: 'Incognito Wiki',
@ -22,6 +39,22 @@ export default defineConfig({
}, },
favicon: '/favicon.ico', favicon: '/favicon.ico',
customCss: ['./src/styles/incognito.css'], customCss: ['./src/styles/incognito.css'],
routeMiddleware: ['./src/starlight-route-data.ts'],
components: {
Sidebar: './src/components/Sidebar.astro',
},
markdown: {
processedDirs: ['./src/content/shared-courses/'],
},
head: [
{
tag: 'script',
attrs: {
src: matomoConsentScript,
defer: true,
},
},
],
social: [ social: [
{ {
icon: 'external', icon: 'external',

View file

@ -2,64 +2,64 @@ page_id source_url status destination captured_source reason
sidebar https://msvincognito.nl/wiki/sidebar excluded to-be-studied/previous-wiki/2026-08-03/pages/sidebar.txt Outside the Bachelor, Master AI, and Master DSDM course-detail scope. sidebar https://msvincognito.nl/wiki/sidebar excluded to-be-studied/previous-wiki/2026-08-03/pages/sidebar.txt Outside the Bachelor, Master AI, and Master DSDM course-detail scope.
start https://msvincognito.nl/wiki/start represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/start.txt Already represented by the selected export migration. start https://msvincognito.nl/wiki/start represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/start.txt Already represented by the selected export migration.
study https://msvincognito.nl/wiki/study represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/study.txt Already represented by the selected export migration. study https://msvincognito.nl/wiki/study represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/study.txt Already represented by the selected export migration.
study:bachelor https://msvincognito.nl/wiki/study/bachelor represented src/content/docs/bachelor/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor.txt Already represented by the selected export migration. study:bachelor https://msvincognito.nl/wiki/study/bachelor represented src/content/docs/data-science-and-ai/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor.txt Already represented by the selected export migration.
study:bachelor:year_1 https://msvincognito.nl/wiki/study/bachelor/year_1 represented src/content/docs/bachelor/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1.txt Already represented by the selected export migration. study:bachelor:year_1 https://msvincognito.nl/wiki/study/bachelor/year_1 represented src/content/docs/data-science-and-ai/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1.txt Already represented by the selected export migration.
study:bachelor:year_1:block_1:discrete_mathematics https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/discrete_mathematics migrated src/content/docs/bachelor/year-1/block-1/discrete-mathematics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/discrete_mathematics.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_1:discrete_mathematics https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/discrete_mathematics migrated src/content/docs/data-science-and-ai/year-1/block-1/discrete-mathematics.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/discrete_mathematics.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_1:introduction_to_data_science_and_artifical_intelligence https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence migrated src/content/docs/bachelor/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_1:introduction_to_data_science_and_artifical_intelligence https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence migrated src/content/docs/data-science-and-ai/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_1:procedural_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/procedural_programming migrated src/content/docs/bachelor/year-1/block-1/procedural-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/procedural_programming.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_1:procedural_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/procedural_programming migrated src/content/docs/data-science-and-ai/year-1/block-1/procedural-programming.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/procedural_programming.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_2:calculus https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/calculus migrated src/content/docs/bachelor/year-1/block-2/calculus.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/calculus.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_2:calculus https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/calculus migrated src/content/docs/data-science-and-ai/year-1/block-2/calculus.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/calculus.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_2:logic https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/logic migrated src/content/docs/bachelor/year-1/block-2/logic.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/logic.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_2:logic https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/logic migrated src/content/docs/data-science-and-ai/year-1/block-2/logic.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/logic.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_2:objects_in_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/objects_in_programming migrated src/content/docs/bachelor/year-1/block-2/objects-in-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/objects_in_programming.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_2:objects_in_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/objects_in_programming migrated src/content/docs/data-science-and-ai/year-1/block-2/objects-in-programming.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/objects_in_programming.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_3/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_1:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_3/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_1:block_4:data_structures_and_algorithms https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/data_structures_and_algorithms migrated src/content/docs/bachelor/year-1/block-4/data-structures-and-algorithms.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/data_structures_and_algorithms.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_4:data_structures_and_algorithms https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/data_structures_and_algorithms migrated src/content/docs/data-science-and-ai/year-1/block-4/data-structures-and-algorithms.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/data_structures_and_algorithms.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_4:linear_algebra https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/linear_algebra migrated src/content/docs/bachelor/year-1/block-4/linear-algebra.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/linear_algebra.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_4:linear_algebra https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/linear_algebra migrated src/content/docs/data-science-and-ai/year-1/block-4/linear-algebra.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/linear_algebra.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_4:principles_of_data_science https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/principles_of_data_science migrated src/content/docs/bachelor/year-1/block-4/principles-of-data-science.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/principles_of_data_science.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_4:principles_of_data_science https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/principles_of_data_science migrated src/content/docs/data-science-and-ai/year-1/block-4/principles-of-data-science.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/principles_of_data_science.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_5:computational_and_cognitive_neuroscience https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience migrated src/content/docs/bachelor/year-1/block-5/computational-and-cognitive-neuroscience.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_5:computational_and_cognitive_neuroscience https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience migrated src/content/docs/data-science-and-ai/year-1/block-5/computational-and-cognitive-neuroscience.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_5:numerical_methods https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/numerical_methods migrated src/content/docs/bachelor/year-1/block-5/numerical-methods.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/numerical_methods.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_5:numerical_methods https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/numerical_methods migrated src/content/docs/data-science-and-ai/year-1/block-5/numerical-methods.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/numerical_methods.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_5:software_engineering https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/software_engineering migrated src/content/docs/bachelor/year-1/block-5/software-engineering.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/software_engineering.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_1:block_5:software_engineering https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/software_engineering migrated src/content/docs/data-science-and-ai/year-1/block-5/software-engineering.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/software_engineering.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_1:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_6/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_1:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_6/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_1:project_1-1 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-1 represented src/content/docs/bachelor/year-1/project-1-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-1.txt Already represented by the selected export migration. study:bachelor:year_1:project_1-1 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-1 represented src/content/docs/data-science-and-ai/year-1/project-1-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-1.txt Already represented by the selected export migration.
study:bachelor:year_1:project_1-2 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-2 represented src/content/docs/bachelor/year-1/project-1-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-2.txt Already represented by the selected export migration. study:bachelor:year_1:project_1-2 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-2 represented src/content/docs/data-science-and-ai/year-1/project-1-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-2.txt Already represented by the selected export migration.
study:bachelor:year_2 https://msvincognito.nl/wiki/study/bachelor/year_2 represented src/content/docs/bachelor/year-2/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2.txt Already represented by the selected export migration. study:bachelor:year_2 https://msvincognito.nl/wiki/study/bachelor/year_2 represented src/content/docs/data-science-and-ai/year-2/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2.txt Already represented by the selected export migration.
study:bachelor:year_2:block_1:databases https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/databases migrated src/content/docs/bachelor/year-2/block-1/databases.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/databases.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_1:databases https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/databases migrated src/content/docs/data-science-and-ai/year-2/block-1/databases.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/databases.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_1:graph_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/graph_theory migrated src/content/docs/bachelor/year-2/block-1/graph-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/graph_theory.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_1:graph_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/graph_theory migrated src/content/docs/data-science-and-ai/year-2/block-1/graph-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/graph_theory.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_1:probability_and_statistics https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/probability_and_statistics migrated src/content/docs/bachelor/year-2/block-1/probability-and-statistics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/probability_and_statistics.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_1:probability_and_statistics https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/probability_and_statistics migrated src/content/docs/data-science-and-ai/year-2/block-1/probability-and-statistics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/probability_and_statistics.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_2:machine_learning https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/machine_learning migrated src/content/docs/bachelor/year-2/block-2/machine-learning.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/machine_learning.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_2:machine_learning https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/machine_learning migrated src/content/docs/data-science-and-ai/year-2/block-2/machine-learning.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/machine_learning.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_2:reasoning_techniques https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/reasoning_techniques migrated src/content/docs/bachelor/year-2/block-2/reasoning-techniques.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/reasoning_techniques.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_2:reasoning_techniques https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/reasoning_techniques migrated src/content/docs/data-science-and-ai/year-2/block-2/reasoning-techniques.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/reasoning_techniques.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_2:simulation_and_statisical_analysis https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/simulation_and_statisical_analysis migrated src/content/docs/bachelor/year-2/block-2/simulation-and-statisical-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/simulation_and_statisical_analysis.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_2:simulation_and_statisical_analysis https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/simulation_and_statisical_analysis migrated src/content/docs/data-science-and-ai/year-2/block-2/simulation-and-statisical-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/simulation_and_statisical_analysis.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_3/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_2:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_3/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_2:block_4:human_computer_interaction_and_affective_computing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing migrated src/content/docs/bachelor/year-2/block-4/human-computer-interaction-and-affective-computing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_4:human_computer_interaction_and_affective_computing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing migrated src/content/docs/data-science-and-ai/year-2/block-4/human-computer-interaction-and-affective-computing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_4:mathematical_modelling https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/mathematical_modelling migrated src/content/docs/bachelor/year-2/block-4/mathematical-modelling.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/mathematical_modelling.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_4:mathematical_modelling https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/mathematical_modelling migrated src/content/docs/data-science-and-ai/year-2/block-4/mathematical-modelling.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/mathematical_modelling.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_4:natural_language_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/natural_language_processing migrated src/content/docs/bachelor/year-2/block-4/natural-language-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/natural_language_processing.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_4:natural_language_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/natural_language_processing migrated src/content/docs/data-science-and-ai/year-2/block-4/natural-language-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/natural_language_processing.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_5:game_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/game_theory migrated src/content/docs/bachelor/year-2/block-5/game-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/game_theory.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_5:game_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/game_theory migrated src/content/docs/data-science-and-ai/year-2/block-5/game-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/game_theory.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_5:introduction_to_image_and_video_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing migrated src/content/docs/bachelor/year-2/block-5/introduction-to-image-and-video-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_5:introduction_to_image_and_video_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing migrated src/content/docs/data-science-and-ai/year-2/block-5/introduction-to-image-and-video-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_5:linear_programming https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/linear_programming migrated src/content/docs/bachelor/year-2/block-5/linear-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/linear_programming.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_5:linear_programming https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/linear_programming migrated src/content/docs/data-science-and-ai/year-2/block-5/linear-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/linear_programming.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_5:philosophy_and_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence migrated src/content/docs/bachelor/year-2/block-5/philosophy-and-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_2:block_5:philosophy_and_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence migrated src/content/docs/data-science-and-ai/year-2/block-5/philosophy-and-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_2:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_6/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_2:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_6/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_2:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_2/honours_programme represented src/content/docs/bachelor/year-2/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/honours_programme.txt Already represented by the selected export migration. study:bachelor:year_2:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_2/honours_programme represented src/content/docs/data-science-and-ai/year-2/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/honours_programme.txt Already represented by the selected export migration.
study:bachelor:year_2:project_2-1 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-1 represented src/content/docs/bachelor/year-2/project-2-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-1.txt Already represented by the selected export migration. study:bachelor:year_2:project_2-1 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-1 represented src/content/docs/data-science-and-ai/year-2/project-2-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-1.txt Already represented by the selected export migration.
study:bachelor:year_2:project_2-2 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-2 represented src/content/docs/bachelor/year-2/project-2-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-2.txt Already represented by the selected export migration. study:bachelor:year_2:project_2-2 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-2 represented src/content/docs/data-science-and-ai/year-2/project-2-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-2.txt Already represented by the selected export migration.
study:bachelor:year_3 https://msvincognito.nl/wiki/study/bachelor/year_3 represented src/content/docs/bachelor/year-3/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3.txt Already represented by the selected export migration. study:bachelor:year_3 https://msvincognito.nl/wiki/study/bachelor/year_3 represented src/content/docs/data-science-and-ai/year-3/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3.txt Already represented by the selected export migration.
study:bachelor:year_3:bachelors_thesis https://msvincognito.nl/wiki/study/bachelor/year_3/bachelors_thesis represented src/content/docs/bachelor/year-3/bachelors-thesis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/bachelors_thesis.txt Already represented by the selected export migration. study:bachelor:year_3:bachelors_thesis https://msvincognito.nl/wiki/study/bachelor/year_3/bachelors_thesis represented src/content/docs/data-science-and-ai/year-3/bachelors-thesis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/bachelors_thesis.txt Already represented by the selected export migration.
study:bachelor:year_3:block_1:prolog https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/prolog migrated src/content/docs/bachelor/year-3/block-1/prolog.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/prolog.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_1:prolog https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/prolog migrated src/content/docs/data-science-and-ai/year-3/block-1/prolog.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/prolog.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_1:robotics_and_embedded_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/robotics_and_embedded_systems migrated src/content/docs/bachelor/year-3/block-1/robotics-and-embedded-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/robotics_and_embedded_systems.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_1:robotics_and_embedded_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/robotics_and_embedded_systems migrated src/content/docs/data-science-and-ai/year-3/block-1/robotics-and-embedded-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/robotics_and_embedded_systems.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_1:semantic_web https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/semantic_web migrated src/content/docs/bachelor/year-3/block-1/semantic-web.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/semantic_web.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_1:semantic_web https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/semantic_web migrated src/content/docs/data-science-and-ai/year-3/block-1/semantic-web.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/semantic_web.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_1:software_and_systems_verification https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/software_and_systems_verification migrated src/content/docs/bachelor/year-3/block-1/software-and-systems-verification.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/software_and_systems_verification.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_1:software_and_systems_verification https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/software_and_systems_verification migrated src/content/docs/data-science-and-ai/year-3/block-1/software-and-systems-verification.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/software_and_systems_verification.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:introduction_to_bio-informatics https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/introduction_to_bio-informatics migrated src/content/docs/bachelor/year-3/block-2/introduction-to-bio-informatics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/introduction_to_bio-informatics.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:introduction_to_bio-informatics https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/introduction_to_bio-informatics migrated src/content/docs/data-science-and-ai/year-3/block-2/introduction-to-bio-informatics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/introduction_to_bio-informatics.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:logic_for_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/logic_for_artificial_intelligence migrated src/content/docs/bachelor/year-3/block-2/logic-for-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/logic_for_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:logic_for_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/logic_for_artificial_intelligence migrated src/content/docs/data-science-and-ai/year-3/block-2/logic-for-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/logic_for_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:parallel_programming https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/parallel_programming migrated src/content/docs/bachelor/year-3/block-2/parallel-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/parallel_programming.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:parallel_programming https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/parallel_programming migrated src/content/docs/data-science-and-ai/year-3/block-2/parallel-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/parallel_programming.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:quantum_computation https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/quantum_computation migrated src/content/docs/bachelor/year-3/block-2/quantum-computation.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/quantum_computation.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:quantum_computation https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/quantum_computation migrated src/content/docs/data-science-and-ai/year-3/block-2/quantum-computation.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/quantum_computation.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:recommender_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/recommender_systems migrated src/content/docs/bachelor/year-3/block-2/recommender-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/recommender_systems.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:recommender_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/recommender_systems migrated src/content/docs/data-science-and-ai/year-3/block-2/recommender-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/recommender_systems.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_2:secure_web_applications https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/secure_web_applications migrated src/content/docs/bachelor/year-3/block-2/secure-web-applications.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/secure_web_applications.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_2:secure_web_applications https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/secure_web_applications migrated src/content/docs/data-science-and-ai/year-3/block-2/secure-web-applications.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/secure_web_applications.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_3/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_3:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_3/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_3:block_4:data_analysis https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/data_analysis migrated src/content/docs/bachelor/year-3/block-4/data-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/data_analysis.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_4:data_analysis https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/data_analysis migrated src/content/docs/data-science-and-ai/year-3/block-4/data-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/data_analysis.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_4:intelligent_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/intelligent_systems migrated src/content/docs/bachelor/year-3/block-4/intelligent-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/intelligent_systems.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_4:intelligent_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/intelligent_systems migrated src/content/docs/data-science-and-ai/year-3/block-4/intelligent-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/intelligent_systems.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_4:operations_research_case_studies https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/operations_research_case_studies migrated src/content/docs/bachelor/year-3/block-4/operations-research-case-studies.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/operations_research_case_studies.txt Substantive public course page migrated from the live DokuWiki. study:bachelor:year_3:block_4:operations_research_case_studies https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/operations_research_case_studies migrated src/content/docs/data-science-and-ai/year-3/block-4/operations-research-case-studies.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/operations_research_case_studies.txt Substantive public course page migrated from the live DokuWiki.
study:bachelor:year_3:block_5:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_5/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_5/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_3:block_5:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_5/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_5/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_3:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_6/placeholder.txt Explicit placeholder page with no course details. study:bachelor:year_3:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_6/placeholder.txt Explicit placeholder page with no course details.
study:bachelor:year_3:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_3/honours_programme represented src/content/docs/bachelor/year-3/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/honours_programme.txt Already represented by the selected export migration. study:bachelor:year_3:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_3/honours_programme represented src/content/docs/data-science-and-ai/year-3/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/honours_programme.txt Already represented by the selected export migration.
study:bachelor:year_3:project_3-1 https://msvincognito.nl/wiki/study/bachelor/year_3/project_3-1 represented src/content/docs/bachelor/year-3/project-3-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/project_3-1.txt Already represented by the selected export migration. study:bachelor:year_3:project_3-1 https://msvincognito.nl/wiki/study/bachelor/year_3/project_3-1 represented src/content/docs/data-science-and-ai/year-3/project-3-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/project_3-1.txt Already represented by the selected export migration.
study:bachelor:year_3:study_abroad https://msvincognito.nl/wiki/study/bachelor/year_3/study_abroad represented src/content/docs/bachelor/year-3/study-abroad.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/study_abroad.txt Already represented by the selected export migration. study:bachelor:year_3:study_abroad https://msvincognito.nl/wiki/study/bachelor/year_3/study_abroad represented src/content/docs/data-science-and-ai/year-3/study-abroad.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/study_abroad.txt Already represented by the selected export migration.
study:master_ai https://msvincognito.nl/wiki/study/master_ai represented src/content/docs/master-ai/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai.txt Already represented by the selected export migration. study:master_ai https://msvincognito.nl/wiki/study/master_ai represented src/content/docs/master-ai/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai.txt Already represented by the selected export migration.
study:master_ai:year_1 https://msvincognito.nl/wiki/study/master_ai/year_1 represented src/content/docs/master-ai/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1.txt Already represented by the selected export migration. study:master_ai:year_1 https://msvincognito.nl/wiki/study/master_ai/year_1 represented src/content/docs/master-ai/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1.txt Already represented by the selected export migration.
study:master_ai:year_1:block_1:foundations_of_agents https://msvincognito.nl/wiki/study/master_ai/year_1/block_1/foundations_of_agents migrated src/content/docs/master-ai/year-1/block-1/foundations-of-agents.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1/block_1/foundations_of_agents.txt Substantive public course page migrated from the live DokuWiki. study:master_ai:year_1:block_1:foundations_of_agents https://msvincognito.nl/wiki/study/master_ai/year_1/block_1/foundations_of_agents migrated src/content/docs/master-ai/year-1/block-1/foundations-of-agents.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1/block_1/foundations_of_agents.txt Substantive public course page migrated from the live DokuWiki.

1 page_id source_url status destination captured_source reason
2 sidebar https://msvincognito.nl/wiki/sidebar excluded to-be-studied/previous-wiki/2026-08-03/pages/sidebar.txt Outside the Bachelor, Master AI, and Master DSDM course-detail scope.
3 start https://msvincognito.nl/wiki/start represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/start.txt Already represented by the selected export migration.
4 study https://msvincognito.nl/wiki/study represented src/content/docs/index.mdx to-be-studied/previous-wiki/2026-08-03/pages/study.txt Already represented by the selected export migration.
5 study:bachelor https://msvincognito.nl/wiki/study/bachelor represented src/content/docs/bachelor/index.md src/content/docs/data-science-and-ai/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor.txt Already represented by the selected export migration.
6 study:bachelor:year_1 https://msvincognito.nl/wiki/study/bachelor/year_1 represented src/content/docs/bachelor/year-1/index.md src/content/docs/data-science-and-ai/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1.txt Already represented by the selected export migration.
7 study:bachelor:year_1:block_1:discrete_mathematics https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/discrete_mathematics migrated src/content/docs/bachelor/year-1/block-1/discrete-mathematics.md src/content/docs/data-science-and-ai/year-1/block-1/discrete-mathematics.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/discrete_mathematics.txt Substantive public course page migrated from the live DokuWiki.
8 study:bachelor:year_1:block_1:introduction_to_data_science_and_artifical_intelligence https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence migrated src/content/docs/bachelor/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md src/content/docs/data-science-and-ai/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence.txt Substantive public course page migrated from the live DokuWiki.
9 study:bachelor:year_1:block_1:procedural_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/procedural_programming migrated src/content/docs/bachelor/year-1/block-1/procedural-programming.md src/content/docs/data-science-and-ai/year-1/block-1/procedural-programming.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_1/procedural_programming.txt Substantive public course page migrated from the live DokuWiki.
10 study:bachelor:year_1:block_2:calculus https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/calculus migrated src/content/docs/bachelor/year-1/block-2/calculus.md src/content/docs/data-science-and-ai/year-1/block-2/calculus.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/calculus.txt Substantive public course page migrated from the live DokuWiki.
11 study:bachelor:year_1:block_2:logic https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/logic migrated src/content/docs/bachelor/year-1/block-2/logic.md src/content/docs/data-science-and-ai/year-1/block-2/logic.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/logic.txt Substantive public course page migrated from the live DokuWiki.
12 study:bachelor:year_1:block_2:objects_in_programming https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/objects_in_programming migrated src/content/docs/bachelor/year-1/block-2/objects-in-programming.md src/content/docs/data-science-and-ai/year-1/block-2/objects-in-programming.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_2/objects_in_programming.txt Substantive public course page migrated from the live DokuWiki.
13 study:bachelor:year_1:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_3/placeholder.txt Explicit placeholder page with no course details.
14 study:bachelor:year_1:block_4:data_structures_and_algorithms https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/data_structures_and_algorithms migrated src/content/docs/bachelor/year-1/block-4/data-structures-and-algorithms.md src/content/docs/data-science-and-ai/year-1/block-4/data-structures-and-algorithms.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/data_structures_and_algorithms.txt Substantive public course page migrated from the live DokuWiki.
15 study:bachelor:year_1:block_4:linear_algebra https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/linear_algebra migrated src/content/docs/bachelor/year-1/block-4/linear-algebra.md src/content/docs/data-science-and-ai/year-1/block-4/linear-algebra.mdx to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/linear_algebra.txt Substantive public course page migrated from the live DokuWiki.
16 study:bachelor:year_1:block_4:principles_of_data_science https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/principles_of_data_science migrated src/content/docs/bachelor/year-1/block-4/principles-of-data-science.md src/content/docs/data-science-and-ai/year-1/block-4/principles-of-data-science.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_4/principles_of_data_science.txt Substantive public course page migrated from the live DokuWiki.
17 study:bachelor:year_1:block_5:computational_and_cognitive_neuroscience https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience migrated src/content/docs/bachelor/year-1/block-5/computational-and-cognitive-neuroscience.md src/content/docs/data-science-and-ai/year-1/block-5/computational-and-cognitive-neuroscience.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience.txt Substantive public course page migrated from the live DokuWiki.
18 study:bachelor:year_1:block_5:numerical_methods https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/numerical_methods migrated src/content/docs/bachelor/year-1/block-5/numerical-methods.md src/content/docs/data-science-and-ai/year-1/block-5/numerical-methods.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/numerical_methods.txt Substantive public course page migrated from the live DokuWiki.
19 study:bachelor:year_1:block_5:software_engineering https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/software_engineering migrated src/content/docs/bachelor/year-1/block-5/software-engineering.md src/content/docs/data-science-and-ai/year-1/block-5/software-engineering.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_5/software_engineering.txt Substantive public course page migrated from the live DokuWiki.
20 study:bachelor:year_1:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_1/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/block_6/placeholder.txt Explicit placeholder page with no course details.
21 study:bachelor:year_1:project_1-1 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-1 represented src/content/docs/bachelor/year-1/project-1-1.md src/content/docs/data-science-and-ai/year-1/project-1-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-1.txt Already represented by the selected export migration.
22 study:bachelor:year_1:project_1-2 https://msvincognito.nl/wiki/study/bachelor/year_1/project_1-2 represented src/content/docs/bachelor/year-1/project-1-2.md src/content/docs/data-science-and-ai/year-1/project-1-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_1/project_1-2.txt Already represented by the selected export migration.
23 study:bachelor:year_2 https://msvincognito.nl/wiki/study/bachelor/year_2 represented src/content/docs/bachelor/year-2/index.md src/content/docs/data-science-and-ai/year-2/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2.txt Already represented by the selected export migration.
24 study:bachelor:year_2:block_1:databases https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/databases migrated src/content/docs/bachelor/year-2/block-1/databases.md src/content/docs/data-science-and-ai/year-2/block-1/databases.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/databases.txt Substantive public course page migrated from the live DokuWiki.
25 study:bachelor:year_2:block_1:graph_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/graph_theory migrated src/content/docs/bachelor/year-2/block-1/graph-theory.md src/content/docs/data-science-and-ai/year-2/block-1/graph-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/graph_theory.txt Substantive public course page migrated from the live DokuWiki.
26 study:bachelor:year_2:block_1:probability_and_statistics https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/probability_and_statistics migrated src/content/docs/bachelor/year-2/block-1/probability-and-statistics.md src/content/docs/data-science-and-ai/year-2/block-1/probability-and-statistics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_1/probability_and_statistics.txt Substantive public course page migrated from the live DokuWiki.
27 study:bachelor:year_2:block_2:machine_learning https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/machine_learning migrated src/content/docs/bachelor/year-2/block-2/machine-learning.md src/content/docs/data-science-and-ai/year-2/block-2/machine-learning.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/machine_learning.txt Substantive public course page migrated from the live DokuWiki.
28 study:bachelor:year_2:block_2:reasoning_techniques https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/reasoning_techniques migrated src/content/docs/bachelor/year-2/block-2/reasoning-techniques.md src/content/docs/data-science-and-ai/year-2/block-2/reasoning-techniques.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/reasoning_techniques.txt Substantive public course page migrated from the live DokuWiki.
29 study:bachelor:year_2:block_2:simulation_and_statisical_analysis https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/simulation_and_statisical_analysis migrated src/content/docs/bachelor/year-2/block-2/simulation-and-statisical-analysis.md src/content/docs/data-science-and-ai/year-2/block-2/simulation-and-statisical-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_2/simulation_and_statisical_analysis.txt Substantive public course page migrated from the live DokuWiki.
30 study:bachelor:year_2:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_3/placeholder.txt Explicit placeholder page with no course details.
31 study:bachelor:year_2:block_4:human_computer_interaction_and_affective_computing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing migrated src/content/docs/bachelor/year-2/block-4/human-computer-interaction-and-affective-computing.md src/content/docs/data-science-and-ai/year-2/block-4/human-computer-interaction-and-affective-computing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing.txt Substantive public course page migrated from the live DokuWiki.
32 study:bachelor:year_2:block_4:mathematical_modelling https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/mathematical_modelling migrated src/content/docs/bachelor/year-2/block-4/mathematical-modelling.md src/content/docs/data-science-and-ai/year-2/block-4/mathematical-modelling.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/mathematical_modelling.txt Substantive public course page migrated from the live DokuWiki.
33 study:bachelor:year_2:block_4:natural_language_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/natural_language_processing migrated src/content/docs/bachelor/year-2/block-4/natural-language-processing.md src/content/docs/data-science-and-ai/year-2/block-4/natural-language-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_4/natural_language_processing.txt Substantive public course page migrated from the live DokuWiki.
34 study:bachelor:year_2:block_5:game_theory https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/game_theory migrated src/content/docs/bachelor/year-2/block-5/game-theory.md src/content/docs/data-science-and-ai/year-2/block-5/game-theory.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/game_theory.txt Substantive public course page migrated from the live DokuWiki.
35 study:bachelor:year_2:block_5:introduction_to_image_and_video_processing https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing migrated src/content/docs/bachelor/year-2/block-5/introduction-to-image-and-video-processing.md src/content/docs/data-science-and-ai/year-2/block-5/introduction-to-image-and-video-processing.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing.txt Substantive public course page migrated from the live DokuWiki.
36 study:bachelor:year_2:block_5:linear_programming https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/linear_programming migrated src/content/docs/bachelor/year-2/block-5/linear-programming.md src/content/docs/data-science-and-ai/year-2/block-5/linear-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/linear_programming.txt Substantive public course page migrated from the live DokuWiki.
37 study:bachelor:year_2:block_5:philosophy_and_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence migrated src/content/docs/bachelor/year-2/block-5/philosophy-and-artificial-intelligence.md src/content/docs/data-science-and-ai/year-2/block-5/philosophy-and-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki.
38 study:bachelor:year_2:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_2/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/block_6/placeholder.txt Explicit placeholder page with no course details.
39 study:bachelor:year_2:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_2/honours_programme represented src/content/docs/bachelor/year-2/honours-programme.md src/content/docs/data-science-and-ai/year-2/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/honours_programme.txt Already represented by the selected export migration.
40 study:bachelor:year_2:project_2-1 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-1 represented src/content/docs/bachelor/year-2/project-2-1.md src/content/docs/data-science-and-ai/year-2/project-2-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-1.txt Already represented by the selected export migration.
41 study:bachelor:year_2:project_2-2 https://msvincognito.nl/wiki/study/bachelor/year_2/project_2-2 represented src/content/docs/bachelor/year-2/project-2-2.md src/content/docs/data-science-and-ai/year-2/project-2-2.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_2/project_2-2.txt Already represented by the selected export migration.
42 study:bachelor:year_3 https://msvincognito.nl/wiki/study/bachelor/year_3 represented src/content/docs/bachelor/year-3/index.md src/content/docs/data-science-and-ai/year-3/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3.txt Already represented by the selected export migration.
43 study:bachelor:year_3:bachelors_thesis https://msvincognito.nl/wiki/study/bachelor/year_3/bachelors_thesis represented src/content/docs/bachelor/year-3/bachelors-thesis.md src/content/docs/data-science-and-ai/year-3/bachelors-thesis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/bachelors_thesis.txt Already represented by the selected export migration.
44 study:bachelor:year_3:block_1:prolog https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/prolog migrated src/content/docs/bachelor/year-3/block-1/prolog.md src/content/docs/data-science-and-ai/year-3/block-1/prolog.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/prolog.txt Substantive public course page migrated from the live DokuWiki.
45 study:bachelor:year_3:block_1:robotics_and_embedded_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/robotics_and_embedded_systems migrated src/content/docs/bachelor/year-3/block-1/robotics-and-embedded-systems.md src/content/docs/data-science-and-ai/year-3/block-1/robotics-and-embedded-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/robotics_and_embedded_systems.txt Substantive public course page migrated from the live DokuWiki.
46 study:bachelor:year_3:block_1:semantic_web https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/semantic_web migrated src/content/docs/bachelor/year-3/block-1/semantic-web.md src/content/docs/data-science-and-ai/year-3/block-1/semantic-web.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/semantic_web.txt Substantive public course page migrated from the live DokuWiki.
47 study:bachelor:year_3:block_1:software_and_systems_verification https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/software_and_systems_verification migrated src/content/docs/bachelor/year-3/block-1/software-and-systems-verification.md src/content/docs/data-science-and-ai/year-3/block-1/software-and-systems-verification.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_1/software_and_systems_verification.txt Substantive public course page migrated from the live DokuWiki.
48 study:bachelor:year_3:block_2:introduction_to_bio-informatics https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/introduction_to_bio-informatics migrated src/content/docs/bachelor/year-3/block-2/introduction-to-bio-informatics.md src/content/docs/data-science-and-ai/year-3/block-2/introduction-to-bio-informatics.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/introduction_to_bio-informatics.txt Substantive public course page migrated from the live DokuWiki.
49 study:bachelor:year_3:block_2:logic_for_artificial_intelligence https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/logic_for_artificial_intelligence migrated src/content/docs/bachelor/year-3/block-2/logic-for-artificial-intelligence.md src/content/docs/data-science-and-ai/year-3/block-2/logic-for-artificial-intelligence.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/logic_for_artificial_intelligence.txt Substantive public course page migrated from the live DokuWiki.
50 study:bachelor:year_3:block_2:parallel_programming https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/parallel_programming migrated src/content/docs/bachelor/year-3/block-2/parallel-programming.md src/content/docs/data-science-and-ai/year-3/block-2/parallel-programming.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/parallel_programming.txt Substantive public course page migrated from the live DokuWiki.
51 study:bachelor:year_3:block_2:quantum_computation https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/quantum_computation migrated src/content/docs/bachelor/year-3/block-2/quantum-computation.md src/content/docs/data-science-and-ai/year-3/block-2/quantum-computation.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/quantum_computation.txt Substantive public course page migrated from the live DokuWiki.
52 study:bachelor:year_3:block_2:recommender_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/recommender_systems migrated src/content/docs/bachelor/year-3/block-2/recommender-systems.md src/content/docs/data-science-and-ai/year-3/block-2/recommender-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/recommender_systems.txt Substantive public course page migrated from the live DokuWiki.
53 study:bachelor:year_3:block_2:secure_web_applications https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/secure_web_applications migrated src/content/docs/bachelor/year-3/block-2/secure-web-applications.md src/content/docs/data-science-and-ai/year-3/block-2/secure-web-applications.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_2/secure_web_applications.txt Substantive public course page migrated from the live DokuWiki.
54 study:bachelor:year_3:block_3:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_3/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_3/placeholder.txt Explicit placeholder page with no course details.
55 study:bachelor:year_3:block_4:data_analysis https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/data_analysis migrated src/content/docs/bachelor/year-3/block-4/data-analysis.md src/content/docs/data-science-and-ai/year-3/block-4/data-analysis.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/data_analysis.txt Substantive public course page migrated from the live DokuWiki.
56 study:bachelor:year_3:block_4:intelligent_systems https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/intelligent_systems migrated src/content/docs/bachelor/year-3/block-4/intelligent-systems.md src/content/docs/data-science-and-ai/year-3/block-4/intelligent-systems.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/intelligent_systems.txt Substantive public course page migrated from the live DokuWiki.
57 study:bachelor:year_3:block_4:operations_research_case_studies https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/operations_research_case_studies migrated src/content/docs/bachelor/year-3/block-4/operations-research-case-studies.md src/content/docs/data-science-and-ai/year-3/block-4/operations-research-case-studies.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_4/operations_research_case_studies.txt Substantive public course page migrated from the live DokuWiki.
58 study:bachelor:year_3:block_5:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_5/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_5/placeholder.txt Explicit placeholder page with no course details.
59 study:bachelor:year_3:block_6:placeholder https://msvincognito.nl/wiki/study/bachelor/year_3/block_6/placeholder empty to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/block_6/placeholder.txt Explicit placeholder page with no course details.
60 study:bachelor:year_3:honours_programme https://msvincognito.nl/wiki/study/bachelor/year_3/honours_programme represented src/content/docs/bachelor/year-3/honours-programme.md src/content/docs/data-science-and-ai/year-3/honours-programme.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/honours_programme.txt Already represented by the selected export migration.
61 study:bachelor:year_3:project_3-1 https://msvincognito.nl/wiki/study/bachelor/year_3/project_3-1 represented src/content/docs/bachelor/year-3/project-3-1.md src/content/docs/data-science-and-ai/year-3/project-3-1.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/project_3-1.txt Already represented by the selected export migration.
62 study:bachelor:year_3:study_abroad https://msvincognito.nl/wiki/study/bachelor/year_3/study_abroad represented src/content/docs/bachelor/year-3/study-abroad.md src/content/docs/data-science-and-ai/year-3/study-abroad.md to-be-studied/previous-wiki/2026-08-03/pages/study/bachelor/year_3/study_abroad.txt Already represented by the selected export migration.
63 study:master_ai https://msvincognito.nl/wiki/study/master_ai represented src/content/docs/master-ai/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai.txt Already represented by the selected export migration.
64 study:master_ai:year_1 https://msvincognito.nl/wiki/study/master_ai/year_1 represented src/content/docs/master-ai/year-1/index.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1.txt Already represented by the selected export migration.
65 study:master_ai:year_1:block_1:foundations_of_agents https://msvincognito.nl/wiki/study/master_ai/year_1/block_1/foundations_of_agents migrated src/content/docs/master-ai/year-1/block-1/foundations-of-agents.md to-be-studied/previous-wiki/2026-08-03/pages/study/master_ai/year_1/block_1/foundations_of_agents.txt Substantive public course page migrated from the live DokuWiki.

View file

@ -5,19 +5,19 @@
{ "source": "pages/start.txt", "destination": "src/content/docs/index.mdx", "mode": "page" }, { "source": "pages/start.txt", "destination": "src/content/docs/index.mdx", "mode": "page" },
{ "source": "pages/study.txt", "destination": "src/content/docs/index.mdx", "mode": "merge" }, { "source": "pages/study.txt", "destination": "src/content/docs/index.mdx", "mode": "merge" },
{ "source": "pages/study/msv_incognito.txt", "destination": "src/content/docs/about-incognito.md", "mode": "page" }, { "source": "pages/study/msv_incognito.txt", "destination": "src/content/docs/about-incognito.md", "mode": "page" },
{ "source": "pages/study/bachelor.txt", "destination": "src/content/docs/bachelor/index.md", "mode": "page" }, { "source": "pages/study/bachelor.txt", "destination": "src/content/docs/data-science-and-ai/index.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_1.txt", "destination": "src/content/docs/bachelor/year-1/index.md", "mode": "page" }, { "source": "pages/study/bachelor/year_1.txt", "destination": "src/content/docs/data-science-and-ai/year-1/index.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_1/project_1-1.txt", "destination": "src/content/docs/bachelor/year-1/project-1-1.md", "mode": "page" }, { "source": "pages/study/bachelor/year_1/project_1-1.txt", "destination": "src/content/docs/data-science-and-ai/year-1/project-1-1.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_1/project_1-2.txt", "destination": "src/content/docs/bachelor/year-1/project-1-2.md", "mode": "page" }, { "source": "pages/study/bachelor/year_1/project_1-2.txt", "destination": "src/content/docs/data-science-and-ai/year-1/project-1-2.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_2.txt", "destination": "src/content/docs/bachelor/year-2/index.md", "mode": "page" }, { "source": "pages/study/bachelor/year_2.txt", "destination": "src/content/docs/data-science-and-ai/year-2/index.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_2/honours_programme.txt", "destination": "src/content/docs/bachelor/year-2/honours-programme.md", "mode": "page" }, { "source": "pages/study/bachelor/year_2/honours_programme.txt", "destination": "src/content/docs/data-science-and-ai/year-2/honours-programme.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_2/project_2-1.txt", "destination": "src/content/docs/bachelor/year-2/project-2-1.md", "mode": "page" }, { "source": "pages/study/bachelor/year_2/project_2-1.txt", "destination": "src/content/docs/data-science-and-ai/year-2/project-2-1.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_2/project_2-2.txt", "destination": "src/content/docs/bachelor/year-2/project-2-2.md", "mode": "page" }, { "source": "pages/study/bachelor/year_2/project_2-2.txt", "destination": "src/content/docs/data-science-and-ai/year-2/project-2-2.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_3.txt", "destination": "src/content/docs/bachelor/year-3/index.md", "mode": "page" }, { "source": "pages/study/bachelor/year_3.txt", "destination": "src/content/docs/data-science-and-ai/year-3/index.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_3/bachelors_thesis.txt", "destination": "src/content/docs/bachelor/year-3/bachelors-thesis.md", "mode": "page" }, { "source": "pages/study/bachelor/year_3/bachelors_thesis.txt", "destination": "src/content/docs/data-science-and-ai/year-3/bachelors-thesis.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_3/honours_programme.txt", "destination": "src/content/docs/bachelor/year-3/honours-programme.md", "mode": "page" }, { "source": "pages/study/bachelor/year_3/honours_programme.txt", "destination": "src/content/docs/data-science-and-ai/year-3/honours-programme.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_3/project_3-1.txt", "destination": "src/content/docs/bachelor/year-3/project-3-1.md", "mode": "page" }, { "source": "pages/study/bachelor/year_3/project_3-1.txt", "destination": "src/content/docs/data-science-and-ai/year-3/project-3-1.md", "mode": "page" },
{ "source": "pages/study/bachelor/year_3/study_abroad.txt", "destination": "src/content/docs/bachelor/year-3/study-abroad.md", "mode": "page" }, { "source": "pages/study/bachelor/year_3/study_abroad.txt", "destination": "src/content/docs/data-science-and-ai/year-3/study-abroad.md", "mode": "page" },
{ "source": "pages/study/master_ai.txt", "destination": "src/content/docs/master-ai/index.md", "mode": "page" }, { "source": "pages/study/master_ai.txt", "destination": "src/content/docs/master-ai/index.md", "mode": "page" },
{ "source": "pages/study/master_ai/year_1.txt", "destination": "src/content/docs/master-ai/year-1/index.md", "mode": "page" }, { "source": "pages/study/master_ai/year_1.txt", "destination": "src/content/docs/master-ai/year-1/index.md", "mode": "page" },
{ "source": "pages/study/master_ai/year_1/project_mai_1.txt", "destination": "src/content/docs/master-ai/year-1/research-project-1.md", "mode": "page" }, { "source": "pages/study/master_ai/year_1/project_mai_1.txt", "destination": "src/content/docs/master-ai/year-1/research-project-1.md", "mode": "page" },

View file

@ -1,6 +1,6 @@
# DokuWiki migration report # DokuWiki migration report
This report accounts for the 29 selected DokuWiki source pages and their 28 unique Starlight destinations. `pages/start.txt` and `pages/study.txt` were intentionally combined on the landing page. Generic DokuWiki documentation was outside the selected source scope. Newer live-wiki captures remain research material under `to-be-studied/` and are not published. Present-day association references use the current Department of Advanced Computing Sciences name; legacy department and programme names remain only in clearly historical material. This report accounts for the 29 selected DokuWiki source pages and their 28 unique Starlight destinations. `pages/start.txt` and `pages/study.txt` were intentionally combined on the landing page. Generic DokuWiki documentation was outside the selected source scope. Most newer live-wiki captures remain research material under `to-be-studied/`; three reviewed practical guides are published as supplemental content, and newer laptop recommendations were merged into the existing laptop destination. Present-day association references use the current Department of Advanced Computing Sciences name; legacy department and programme names remain only in clearly historical material.
## Source inventory ## Source inventory
@ -11,19 +11,19 @@ This report accounts for the 29 selected DokuWiki source pages and their 28 uniq
| `pages/start.txt` | `src/content/docs/index.mdx` | page | Yes, on migrated programme notes | **Converted source links:** Bachelor, Master AI, Master DSDM, Useful Information, the Incognito site, and ICT manuals. **Newly verified link:** the current DACS page replaced the source-era DKE department URL. **Genuinely removed links:** unresolved Minutes, Event Manuals, and Function Manuals targets; redundant study/year navigation was omitted. | None | Recast the welcome copy and normalized programme terminology | | `pages/start.txt` | `src/content/docs/index.mdx` | page | Yes, on migrated programme notes | **Converted source links:** Bachelor, Master AI, Master DSDM, Useful Information, the Incognito site, and ICT manuals. **Newly verified link:** the current DACS page replaced the source-era DKE department URL. **Genuinely removed links:** unresolved Minutes, Event Manuals, and Function Manuals targets; redundant study/year navigation was omitted. | None | Recast the welcome copy and normalized programme terminology |
| `pages/study.txt` | `src/content/docs/index.mdx` | merge | Yes | **Converted source links:** Bachelor, Master AI, Master DSDM, and the YouTube embed. **Genuinely removed links:** redundant year navigation was omitted. | None | Condensed the source-era university and programme overview, including omission of its dated growth/ranking sentence; corrected punctuation, capitalization, and bachelor/master wording | | `pages/study.txt` | `src/content/docs/index.mdx` | merge | Yes | **Converted source links:** Bachelor, Master AI, Master DSDM, and the YouTube embed. **Genuinely removed links:** redundant year navigation was omitted. | None | Condensed the source-era university and programme overview, including omission of its dated growth/ranking sentence; corrected punctuation, capitalization, and bachelor/master wording |
| `pages/study/msv_incognito.txt` | `src/content/docs/about-incognito.md` | page | No | **Converted source link:** the MSV Incognito website. No links were repaired or removed. | None | Combined the source website link with the association-description sentence from `pages/start.txt`; the present-day introduction uses the current DACS name | | `pages/study/msv_incognito.txt` | `src/content/docs/about-incognito.md` | page | No | **Converted source link:** the MSV Incognito website. No links were repaired or removed. | None | Combined the source website link with the association-description sentence from `pages/start.txt`; the present-day introduction uses the current DACS name |
| `pages/study/bachelor.txt` | `src/content/docs/bachelor/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Corrected bachelor capitalization and sentence structure; disclosed that promised summaries and old exams were absent from the export | | `pages/study/bachelor.txt` | `src/content/docs/data-science-and-ai/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Corrected bachelor capitalization and sentence structure; disclosed that promised summaries and old exams were absent from the export |
| `pages/study/bachelor/year_1.txt` | `src/content/docs/bachelor/year-1/index.md` | page | Yes | **Newly verified links:** the two migrated project pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Standardized Year 1/block capitalization | | `pages/study/bachelor/year_1.txt` | `src/content/docs/data-science-and-ai/year-1/index.md` | page | Yes | **Newly verified links:** the two migrated project pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Standardized Year 1/block capitalization |
| `pages/study/bachelor/year_1/project_1-1.txt` | `src/content/docs/bachelor/year-1/project-1-1.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and clarified prerequisite prose without changing requirements | | `pages/study/bachelor/year_1/project_1-1.txt` | `src/content/docs/data-science-and-ai/year-1/project-1-1.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and clarified prerequisite prose without changing requirements |
| `pages/study/bachelor/year_1/project_1-2.txt` | `src/content/docs/bachelor/year-1/project-1-2.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected grammar, spacing, and capitalization | | `pages/study/bachelor/year_1/project_1-2.txt` | `src/content/docs/data-science-and-ai/year-1/project-1-2.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected grammar, spacing, and capitalization |
| `pages/study/bachelor/year_2.txt` | `src/content/docs/bachelor/year-2/index.md` | page | Yes | **Newly verified links:** the two migrated project pages and honours placeholder. **Genuinely removed link:** the malformed DokuWiki media-manager/upload URL. | None | Standardized Year 2/block capitalization | | `pages/study/bachelor/year_2.txt` | `src/content/docs/data-science-and-ai/year-2/index.md` | page | Yes | **Newly verified links:** the two migrated project pages and honours placeholder. **Genuinely removed link:** the malformed DokuWiki media-manager/upload URL. | None | Standardized Year 2/block capitalization |
| `pages/study/bachelor/year_2/honours_programme.txt` | `src/content/docs/bachelor/year-2/honours-programme.md` | page | Yes | **None.** The source and destination contain no content links. | None | Replaced the source's “Under Construction” text with the standard awaiting-content placeholder | | `pages/study/bachelor/year_2/honours_programme.txt` | `src/content/docs/data-science-and-ai/year-2/honours-programme.md` | page | Yes | **None.** The source and destination contain no content links. | None | Replaced the source's “Under Construction” text with the standard awaiting-content placeholder |
| `pages/study/bachelor/year_2/project_2-1.txt` | `src/content/docs/bachelor/year-2/project-2-1.md` | page | Yes | **Converted plain-text reference:** Project 1-1 became a verified local link. The Project 3-1 reference remains text. No source link was repaired. | None | Corrected grammar and standardized project naming | | `pages/study/bachelor/year_2/project_2-1.txt` | `src/content/docs/data-science-and-ai/year-2/project-2-1.md` | page | Yes | **Converted plain-text reference:** Project 1-1 became a verified local link. The Project 3-1 reference remains text. No source link was repaired. | None | Corrected grammar and standardized project naming |
| `pages/study/bachelor/year_2/project_2-2.txt` | `src/content/docs/bachelor/year-2/project-2-2.md` | page | Yes | **Converted plain-text reference:** Project 1-2 became a verified local link. No source link was repaired. | None | Corrected punctuation and standardized project naming | | `pages/study/bachelor/year_2/project_2-2.txt` | `src/content/docs/data-science-and-ai/year-2/project-2-2.md` | page | Yes | **Converted plain-text reference:** Project 1-2 became a verified local link. No source link was repaired. | None | Corrected punctuation and standardized project naming |
| `pages/study/bachelor/year_3.txt` | `src/content/docs/bachelor/year-3/index.md` | page | Yes | **Newly verified links:** the four migrated child pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Corrected block references and the schedule explanation | | `pages/study/bachelor/year_3.txt` | `src/content/docs/data-science-and-ai/year-3/index.md` | page | Yes | **Newly verified links:** the four migrated child pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Corrected block references and the schedule explanation |
| `pages/study/bachelor/year_3/bachelors_thesis.txt` | `src/content/docs/bachelor/year-3/bachelors-thesis.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected possessive bachelors wording, punctuation, and sentence flow | | `pages/study/bachelor/year_3/bachelors_thesis.txt` | `src/content/docs/data-science-and-ai/year-3/bachelors-thesis.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected possessive bachelors wording, punctuation, and sentence flow |
| `pages/study/bachelor/year_3/honours_programme.txt` | `src/content/docs/bachelor/year-3/honours-programme.md` | page | Yes | **None.** The source and destination contain no content links. | None | Preserved the source title and made its under-construction state an explicit awaiting-content placeholder | | `pages/study/bachelor/year_3/honours_programme.txt` | `src/content/docs/data-science-and-ai/year-3/honours-programme.md` | page | Yes | **None.** The source and destination contain no content links. | None | Preserved the source title and made its under-construction state an explicit awaiting-content placeholder |
| `pages/study/bachelor/year_3/project_3-1.txt` | `src/content/docs/bachelor/year-3/project-3-1.md` | page | Yes | **Converted plain-text reference:** Project 2-1 became a verified local link. No source link was repaired. | None | Corrected grammar while retaining the stated prerequisites and reading | | `pages/study/bachelor/year_3/project_3-1.txt` | `src/content/docs/data-science-and-ai/year-3/project-3-1.md` | page | Yes | **Converted plain-text reference:** Project 2-1 became a verified local link. No source link was repaired. | None | Corrected grammar while retaining the stated prerequisites and reading |
| `pages/study/bachelor/year_3/study_abroad.txt` | `src/content/docs/bachelor/year-3/study-abroad.md` | page | Yes, with a 2022-specific warning | **Newly verified link:** the official UM study-abroad page. No source links existed. | None | Corrected grammar; did not silently modernize the dated eligibility claims | | `pages/study/bachelor/year_3/study_abroad.txt` | `src/content/docs/data-science-and-ai/year-3/study-abroad.md` | page | Yes, with a 2022-specific warning | **Newly verified link:** the official UM study-abroad page. No source links existed. | None | Corrected grammar; did not silently modernize the dated eligibility claims |
| `pages/study/master_ai.txt` | `src/content/docs/master-ai/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Standardized masters-programme capitalization and punctuation; disclosed that promised summaries and old exams were absent from the export | | `pages/study/master_ai.txt` | `src/content/docs/master-ai/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Standardized masters-programme capitalization and punctuation; disclosed that promised summaries and old exams were absent from the export |
| `pages/study/master_ai/year_1.txt` | `src/content/docs/master-ai/year-1/index.md` | page | Yes | **Newly verified links:** the two migrated research-project pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Corrected sentence structure and project naming | | `pages/study/master_ai/year_1.txt` | `src/content/docs/master-ai/year-1/index.md` | page | Yes | **Newly verified links:** the two migrated research-project pages, derived from the exported child-page inventory. No source content links required repair or removal. | None | Corrected sentence structure and project naming |
| `pages/study/master_ai/year_1/project_mai_1.txt` | `src/content/docs/master-ai/year-1/research-project-1.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and rendered the project name consistently | | `pages/study/master_ai/year_1/project_mai_1.txt` | `src/content/docs/master-ai/year-1/research-project-1.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and rendered the project name consistently |
@ -32,11 +32,11 @@ This report accounts for the 29 selected DokuWiki source pages and their 28 uniq
| `pages/study/master_dsdm.txt` | `src/content/docs/master-dsdm/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Standardized DSDM and masters-programme capitalization; disclosed that promised summaries and old exams were absent from the export | | `pages/study/master_dsdm.txt` | `src/content/docs/master-dsdm/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Removed source links:** redundant DokuWiki year-navigation links, now covered by site navigation. | None | Standardized DSDM and masters-programme capitalization; disclosed that promised summaries and old exams were absent from the export |
| `pages/study/master_dsdm/year_1.txt` | `src/content/docs/master-dsdm/year-1/index.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and retained the source-era structure | | `pages/study/master_dsdm/year_1.txt` | `src/content/docs/master-dsdm/year-1/index.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and retained the source-era structure |
| `pages/study/master_dsdm/year_2.txt` | `src/content/docs/master-dsdm/year-2/index.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and retained the source-era structure | | `pages/study/master_dsdm/year_2.txt` | `src/content/docs/master-dsdm/year-2/index.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected punctuation and retained the source-era structure |
| `pages/study/useful_information.txt` | `src/content/docs/useful-information/index.md` | page | Yes | **Converted source link:** Maastricht University education website. **Newly verified links:** the four migrated child pages, derived from the exported child-page inventory. | None | Corrected heading capitalization and introductory grammar; scoped contribution guidance to repository maintainers | | `pages/study/useful_information.txt` | `src/content/docs/useful-information/index.md` | page | No | **Converted source link:** Maastricht University education website. **Newly verified links:** the four migrated child pages plus the three reviewed practical guides. | None | Reframed the landing page around practical guides and legacy reference pages; scoped contribution guidance to repository maintainers |
| `pages/study/useful_information/pages/dke_locations.txt` | `src/content/docs/useful-information/dke-locations.md` | page | Yes | **Converted source links:** the three Google Maps URLs. No links were repaired or removed. | None | Corrected sentence fragments, comma use, and room-list phrasing | | `pages/study/useful_information/pages/dke_locations.txt` | `src/content/docs/useful-information/dke-locations.md` | page | Yes | **Converted source links:** the three Google Maps URLs. No links were repaired or removed. | None | Corrected sentence fragments, comma use, and room-list phrasing |
| `pages/study/useful_information/pages/handy_locations.txt` | `src/content/docs/useful-information/handy-locations.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected capitalization; marked the two empty sections as awaiting content | | `pages/study/useful_information/pages/handy_locations.txt` | `src/content/docs/useful-information/handy-locations.md` | page | Yes | **None.** The source and destination contain no content links. | None | Corrected capitalization; marked the two empty sections as awaiting content |
| `pages/study/useful_information/pages/it_services.txt` | `src/content/docs/useful-information/it-services.md` | page | Yes, prominently historical | **Converted source links:** ICT manuals, Inner City map, Student Portal, Student Desktop Anywhere, and MyPrint. **Removed hyperlinking:** legacy EleUM and VPN URLs were retained as non-clickable historical code. No successor URL was added. | None | Corrected errors such as “Eletctronic” and “contact he,” plus punctuation and service-name casing | | `pages/study/useful_information/pages/it_services.txt` | `src/content/docs/useful-information/it-services.md` | page | Yes, prominently historical | **Converted source links:** ICT manuals, Inner City map, Student Portal, Student Desktop Anywhere, and MyPrint. **Removed hyperlinking:** legacy EleUM and VPN URLs were retained as non-clickable historical code. No successor URL was added. | None | Corrected errors such as “Eletctronic” and “contact he,” plus punctuation and service-name casing |
| `pages/study/useful_information/pages/laptop_buy_advice.txt` | `src/content/docs/useful-information/laptop-buying-advice.md` | page | Yes, prominently historical | **None.** The source and destination contain no content links. | None | Corrected spacing in capacities, punctuation, and awkward phrasing while retaining old price/specification facts | | `pages/study/useful_information/pages/laptop_buy_advice.txt` | `src/content/docs/useful-information/laptop-buying-advice.md` | page | No | **None.** The source and destination contain no content links. | None | Incorporated the newer 16 GB RAM, 512 GB SSD, mid-range processor, M-series Mac, and Nvidia/CUDA guidance; replaced obsolete price tiers with budget-neutral advice |
## Removed unresolved home-page targets ## Removed unresolved home-page targets
@ -68,10 +68,10 @@ The Study Abroad source makes time-sensitive claims tied to 2022, including elig
The IT page names source-era services, URLs, and instructions, including MyUM, EleUM, Student Desktop Anywhere, VPN steps, printing, and historical timetable navigation. These are not presented as current instructions. The destination uses a prominent historical warning, converts useful source links, and retains the legacy EleUM and VPN URLs only as non-clickable historical code. No successor URL was added. The IT page names source-era services, URLs, and instructions, including MyUM, EleUM, Student Desktop Anywhere, VPN steps, printing, and historical timetable navigation. These are not presented as current instructions. The destination uses a prominent historical warning, converts useful source links, and retains the legacy EleUM and VPN URLs only as non-clickable historical code. No successor URL was added.
## Historical laptop advice ## Laptop buying advice
Laptop prices, processor generations, RAM and storage capacities, operating-system assumptions, and the listed performance tiers reflect the source period. They were preserved rather than silently replaced with current purchasing advice. The page prominently labels the entire section historical and tells readers to verify current programme requirements, prices, and hardware support. The newer live-wiki source added a practical baseline of 16 GB RAM, a 512 GB SSD, a mid-range processor, M-series Mac compatibility guidance, and an Nvidia GPU recommendation for CUDA workloads. Those recommendations are now incorporated into the existing Laptop Buying Advice destination. Obsolete price tiers and low-capacity configurations were generalized instead of being presented as current purchasing targets.
## Publication boundary ## Publication boundary
Raw research captures remain stored only under `to-be-studied/` and are not served by the site. The 93-page newer-wiki snapshot is retained for research, while the 115-page legacy crawl was reviewed to recover missing course information. That review produced 54 supplemental course pages in `src/content/docs/`; empty placeholders, excluded utility pages, and raw captures remain unpublished. Raw research captures remain stored only under `to-be-studied/` and are not served by the site. The 93-page newer-wiki snapshot is retained for research, while reviewed derivatives of its Housing Guide, Linux Tricks, and Surviving DACS pages are registered and published as supplemental content. Its newer Laptop Buying Advice recommendations were merged into the destination already covered by the original migration manifest. The 115-page legacy crawl produced 54 supplemental course pages in `src/content/docs/`; empty placeholders, excluded utility pages, and all remaining raw captures remain unpublished.

View file

@ -0,0 +1,412 @@
# Useful Guides Publication 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:** Publish Housing Guide, Linux Tricks, and Surviving DACS in the Starlight wiki and update Laptop Buying Advice with the newer hardware recommendations.
**Architecture:** Treat the three new pages as reviewed supplemental content sourced from the retained `wiki.msvincognito.nl` snapshot, while updating the manifest-backed laptop destination in place. Use native Markdown, explicit supplemental registry entries, the existing manual sidebar, and focused Node tests to enforce publication, discovery, content-safety, and laptop-specification requirements.
**Tech Stack:** Astro 7, Starlight 0.41, Markdown, JavaScript ES modules, Node.js test runner.
## Global Constraints
- Publish Laptop Buying Advice, Housing Guide, Linux Tricks, and Surviving DACS.
- Do not add historical-information or dated-warning callouts to these four guides.
- Preserve practical substance and contributor voice while correcting obvious spelling, grammar, and Markdown defects.
- Do not invent current facts or state that time-sensitive prices, services, housing practices, or university systems were reverified.
- Remove dead links, obsolete credential-handling instructions, and recommendations that create avoidable legal or safety risk.
- Use `useful-guide-recovery` for each new supplemental registry entry.
- Keep all routes under `/useful-information/`; do not introduce redirects or a new top-level navigation group.
## File map
- `src/content/docs/useful-information/laptop-buying-advice.md`: existing guide, updated with the newer buying baseline.
- `src/content/docs/useful-information/housing-guide.md`: new housing-search and scam-avoidance guide.
- `src/content/docs/useful-information/linux-tricks.md`: new Linux-oriented UM services guide with credential-safe examples.
- `src/content/docs/useful-information/surviving-dacs.md`: new academic and student-life guide.
- `src/content/docs/useful-information/index.md`: section introduction and discovery links.
- `src/config/sidebar.mjs`: manual navigation entries for all published guides.
- `docs/supplemental-content.json`: source provenance for the three new pages.
- `docs/migration-report.md`: publication-boundary record for the reviewed snapshot material.
- `to-be-studied/README.md`: research archive note acknowledging the reviewed derivatives.
- `tests/useful-guides.test.mjs`: focused guide content, provenance, navigation, and safety contract.
- `tests/content-audit.test.mjs`: updated total counts and removal of the old laptop-warning requirement.
---
### Task 1: Modernize Laptop Buying Advice
**Files:**
- Create: `tests/useful-guides.test.mjs`
- Modify: `tests/content-audit.test.mjs`
- Modify: `src/content/docs/useful-information/laptop-buying-advice.md`
**Interfaces:**
- Consumes: the existing manifest-backed laptop route at `useful-information/laptop-buying-advice.md`.
- Produces: a published laptop page whose frontmatter remains `title: Laptop Buying Advice` and whose body contains the new minimum specification and compatibility guidance without a historical caution directive.
- [ ] **Step 1: Write the failing laptop contract**
Create `tests/useful-guides.test.mjs`:
```js
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
const docsRoot = 'src/content/docs/useful-information';
test('Laptop Buying Advice publishes the newer hardware baseline without a dated warning', async () => {
const content = await readFile(`${docsRoot}/laptop-buying-advice.md`, 'utf8');
assert.match(content, /16 GB/);
assert.match(content, /512 GB/);
assert.match(content, /Core i5-class or equivalent/i);
assert.match(content, /M-series Mac/i);
assert.match(content, /Nvidia[\s\S]*CUDA/i);
assert.doesNotMatch(content, /:::caution\[Historical information\]/);
});
```
In `tests/content-audit.test.mjs`, keep the clean-Markdown loop's frontmatter and forbidden-token assertions. Replace its unconditional historical-notice assertion with this exception so all other migrated pages retain the existing invariant:
```js
if (file !== 'src/content/docs/useful-information/laptop-buying-advice.md') {
assert.match(content, /This information originated in the previous wiki and may be outdated\./);
}
```
Change the `prominently identifies historical guidance` file list to contain only `src/content/docs/useful-information/it-services.md`.
- [ ] **Step 2: Run the focused tests and verify failure**
Run: `node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs`
Expected: FAIL because the laptop page lacks the new baseline and still contains `:::caution[Historical information]`.
- [ ] **Step 3: Rewrite the laptop page around the current source update**
Retain this frontmatter:
```md
---
title: Laptop Buying Advice
description: Practical laptop-buying guidance for DACS students.
---
```
Use `## Recommended baseline for a new laptop` as the first body section and state all five required points explicitly:
```md
- **Memory:** At least 16 GB RAM.
- **Storage:** At least a 512 GB SSD.
- **Processor:** A mid-range Intel Core i5-class, AMD Ryzen 5-class, or equivalent processor.
- **Operating system:** Windows, Linux, and M-series Macs can all be suitable; confirm course-specific software compatibility before purchasing.
- **Data Science and AI workloads:** An Nvidia GPU is useful when coursework or personal projects use CUDA, but it is not required for every course.
```
Preserve and clean the source guidance about needing a laptop for labs and projects, screen size, keyboard layouts, battery/build quality, and dedicated graphics. Replace the obsolete €200/€400/€600/€800 configurations with budget-neutral advice headed `## Choosing within your budget`; do not present 28 GB RAM, 16256 GB storage, Windows 10, or 2021 price bands as current recommendations.
- [ ] **Step 4: Run the focused tests and verify success**
Run: `node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs`
Expected: PASS with the existing content totals unchanged at 83 pages and 55 supplemental entries.
- [ ] **Step 5: Commit the laptop update**
```bash
git add tests/useful-guides.test.mjs tests/content-audit.test.mjs src/content/docs/useful-information/laptop-buying-advice.md
git commit -m "content: update laptop buying guidance"
```
---
### Task 2: Publish the Three Additional Guides
**Files:**
- Create: `src/content/docs/useful-information/housing-guide.md`
- Create: `src/content/docs/useful-information/linux-tricks.md`
- Create: `src/content/docs/useful-information/surviving-dacs.md`
- Modify: `docs/supplemental-content.json`
- Modify: `tests/useful-guides.test.mjs`
- Modify: `tests/content-audit.test.mjs`
**Interfaces:**
- Consumes: the archived Markdown under `to-be-studied/live-wiki/2026-08-02/useful-guides/` and `auditContent({ docsRoot, manifest, supplemental })` from `scripts/audit-content.mjs`.
- Produces: three native Starlight Markdown pages plus three unique supplemental registry entries; the audit result becomes `pageCount: 86` and `supplementalCount: 58`.
- [ ] **Step 1: Extend the focused tests for publication and provenance**
Append to `tests/useful-guides.test.mjs`:
```js
const recoveredGuides = [
{
slug: 'housing-guide',
title: 'Housing Guide',
source: 'https://wiki.msvincognito.nl/useful-guides/housing-guide',
},
{
slug: 'linux-tricks',
title: 'Linux Tricks',
source: 'https://wiki.msvincognito.nl/useful-guides/linux-tricks',
},
{
slug: 'surviving-dacs',
title: 'Surviving DACS',
source: 'https://wiki.msvincognito.nl/useful-guides/survivingdacs',
},
];
test('the three recovered guides are publishable native Markdown', async () => {
for (const { slug, title } of recoveredGuides) {
const content = await readFile(`${docsRoot}/${slug}.md`, 'utf8');
assert.match(content, new RegExp(`^---\\n[\\s\\S]*title: ${title}`));
assert.doesNotMatch(content, /^#\s+/m);
assert.doesNotMatch(content, /:::caution\[Historical information\]/);
assert.doesNotMatch(content, /\[\[|\{\{|NEWPAGE>|indexmenu>|~~NOCACHE~~/);
}
});
test('the recovered guide registry records source provenance', async () => {
const supplemental = JSON.parse(await readFile('docs/supplemental-content.json', 'utf8'));
for (const { slug, source } of recoveredGuides) {
assert.deepEqual(
supplemental.find(({ destination }) => destination === `${docsRoot}/${slug}.md`),
{
destination: `${docsRoot}/${slug}.md`,
source,
category: 'useful-guide-recovery',
},
);
}
});
test('published guides omit unsafe or legally questionable source recommendations', async () => {
const linux = await readFile(`${docsRoot}/linux-tricks.md`, 'utf8');
const survival = await readFile(`${docsRoot}/surviving-dacs.md`, 'utf8');
assert.doesNotMatch(linux, /password=["']?<password>/i);
assert.doesNotMatch(survival, /Sci-Hub|Library Genesis|Library\.nu/i);
});
```
Update both audit-count assertions in `tests/content-audit.test.mjs` from 83/55 to 86/58.
- [ ] **Step 2: Run the tests and verify failure**
Run: `node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs`
Expected: FAIL with `ENOENT` for the three unpublished guide files and missing supplemental records.
- [ ] **Step 3: Create Housing Guide**
Create `src/content/docs/useful-information/housing-guide.md` with:
```md
---
title: Housing Guide
description: Practical guidance for finding student housing in Maastricht and recognizing scams.
---
```
Migrate the source into these native headings: `## Where to look`, `### Facebook groups and private listings`, `### Maastricht Housing`, `### Housing agencies and student residences`, `## Avoiding scams`, `### Checking private listings`, `### Checking agencies`, and `## Practical tips after moving`. Preserve the viewing, identity-document, address/landlord verification, reverse-image checking, included-cost, contract-before-payment, and Dutch Chamber of Commerce checks. Rephrase absolute claims based solely on nationality, country code, or phone number as signals to investigate rather than proof of fraud. Omit the unverified Kamernet accusation, exact platform fee, nationality-priority description, and claims about named residences that have not been reverified.
- [ ] **Step 4: Create Linux Tricks**
Create `src/content/docs/useful-information/linux-tricks.md` with:
```md
---
title: Linux Tricks
description: Linux-oriented tips for connecting to Maastricht University services.
---
```
Migrate the source into `## Eduroam`, `## VPN and library access`, `## University file services`, `## Remote desktop`, and `## Useful resources`. Preserve distro-neutral explanations and links to authoritative ArchWiki or vendor documentation. Code fences must use `sh` or an appropriate configuration language. Do not include a plaintext `password="<password>"` line, obsolete `unimaas.nl` identity examples, or a claim that an old UM VPN/file-service hostname is currently operational. Direct readers to current UM service instructions where the captured command depends on an institutional hostname.
- [ ] **Step 5: Create Surviving DACS**
Create `src/content/docs/useful-information/surviving-dacs.md` with:
```md
---
title: Surviving DACS
description: Student-contributed advice for studying and living as a DACS student.
---
```
Preserve the contributor disclaimer as ordinary introductory prose, not a warning directive. Migrate the source into the following section structure: `## University`, with Projects, Books and articles, Studying, Exams, Programming, and Mathematics; `## Thesis`; `## Grades`; `## Living`, with Sustainability, Housing, Food, Cooking, Essentials, and Transportation; `## Social life`; `## Gaining experience`, with Honours programmes, Internships, Mentorships, and Jobs; and `## Healthy and productive habits`, with Sleep, Ventilation, Ergonomics, Task management, and Audiobooks. Remove Sci-Hub, Library Genesis, and Library.nu references. Remove or generalize stale restaurant tables, exact rent/food/gym/wage prices, dead social-group links, and claims tied to a specific building or course schedule. Preserve actionable study habits, project/version-control advice, exam strategy, cooking and transport suggestions, career-development ideas, sleep, ventilation, and ergonomics.
- [ ] **Step 6: Register the new destinations**
Add these objects to `docs/supplemental-content.json`, maintaining destination sort order:
```json
{
"destination": "src/content/docs/useful-information/housing-guide.md",
"source": "https://wiki.msvincognito.nl/useful-guides/housing-guide",
"category": "useful-guide-recovery"
},
{
"destination": "src/content/docs/useful-information/linux-tricks.md",
"source": "https://wiki.msvincognito.nl/useful-guides/linux-tricks",
"category": "useful-guide-recovery"
},
{
"destination": "src/content/docs/useful-information/surviving-dacs.md",
"source": "https://wiki.msvincognito.nl/useful-guides/survivingdacs",
"category": "useful-guide-recovery"
}
```
- [ ] **Step 7: Run the focused tests and content audit**
Run: `node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs && npm run audit:content`
Expected: PASS; audit reports 29 sources, 28 unique manifest destinations, and 86 published pages.
- [ ] **Step 8: Commit the published guide content**
```bash
git add src/content/docs/useful-information/housing-guide.md src/content/docs/useful-information/linux-tricks.md src/content/docs/useful-information/surviving-dacs.md docs/supplemental-content.json tests/useful-guides.test.mjs tests/content-audit.test.mjs
git commit -m "content: publish practical student guides"
```
---
### Task 3: Add Guide Discovery and Navigation
**Files:**
- Modify: `tests/useful-guides.test.mjs`
- Modify: `src/content/docs/useful-information/index.md`
- Modify: `src/config/sidebar.mjs`
**Interfaces:**
- Consumes: the four guide slugs under `src/content/docs/useful-information/`.
- Produces: landing-page relative links and sidebar slug entries for every practical guide.
- [ ] **Step 1: Write the failing discovery test**
Append to `tests/useful-guides.test.mjs`:
```js
test('Useful Information navigation exposes every practical guide', async () => {
const landing = await readFile(`${docsRoot}/index.md`, 'utf8');
const sidebar = await readFile('src/config/sidebar.mjs', 'utf8');
for (const slug of ['housing-guide', 'laptop-buying-advice', 'linux-tricks', 'surviving-dacs']) {
assert.match(landing, new RegExp(`\\]\(\\./${slug}/\\)`));
assert.match(sidebar, new RegExp(`slug: 'useful-information/${slug}'`));
}
});
```
- [ ] **Step 2: Run the discovery test and verify failure**
Run: `node --test tests/useful-guides.test.mjs`
Expected: FAIL because the landing page and sidebar do not yet link the three new routes.
- [ ] **Step 3: Update the Useful Information landing page**
Retain the existing frontmatter and official Maastricht University education link. Add a `## Practical guides` section with these exact relative links:
```md
- [Housing Guide](./housing-guide/)
- [Laptop Buying Advice](./laptop-buying-advice/)
- [Linux Tricks](./linux-tricks/)
- [Surviving DACS](./surviving-dacs/)
```
Keep DKE Locations, Handy Locations, and IT Services under a separate `## Other useful information` heading. Remove the page-level historical caution because the landing page now introduces both current practical guides and legacy reference pages; individual legacy destinations retain their own treatment.
- [ ] **Step 4: Update the manual sidebar**
Within the `Useful Information` item list in `src/config/sidebar.mjs`, use this order:
```js
{ slug: 'useful-information' },
{ slug: 'useful-information/housing-guide' },
{ slug: 'useful-information/laptop-buying-advice' },
{ slug: 'useful-information/linux-tricks' },
{ slug: 'useful-information/surviving-dacs' },
{ slug: 'useful-information/dke-locations' },
{ slug: 'useful-information/handy-locations' },
{ slug: 'useful-information/it-services' },
```
- [ ] **Step 5: Run focused navigation and content tests**
Run: `node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs`
Expected: PASS.
- [ ] **Step 6: Commit navigation changes**
```bash
git add src/content/docs/useful-information/index.md src/config/sidebar.mjs tests/useful-guides.test.mjs
git commit -m "content: expose useful guides in navigation"
```
---
### Task 4: Record Publication and Verify the Site
**Files:**
- Modify: `docs/migration-report.md`
- Modify: `to-be-studied/README.md`
- Test: complete repository workflow
**Interfaces:**
- Consumes: the completed guide pages, registry, and navigation.
- Produces: accurate maintainer documentation and a fully verified production build.
- [ ] **Step 1: Update the publication records**
In `docs/migration-report.md`, replace the blanket statement that all newer-wiki captures remain unpublished with an explicit record: three reviewed practical guides were published as supplemental content, and the existing laptop destination received the newer source recommendations. State that all remaining raw captures stay research-only. In the Laptop Buying Advice inventory row, change `Outdated` from `Yes, prominently historical` to `No`, and describe the incorporated 16 GB RAM, 512 GB SSD, processor, M-series Mac, and Nvidia/CUDA update. Replace the `Historical laptop advice` narrative with `Laptop buying advice`, explaining that obsolete price tiers were generalized instead of being presented as current recommendations.
In `to-be-studied/README.md`, retain the rule that raw capture files are never served directly. Add that Housing Guide, Linux Tricks, and Surviving DACS were editorially reviewed and published on 2026-08-11 through separately registered Markdown destinations, while Laptop Buying Advice was merged into its existing destination.
- [ ] **Step 2: Run formatting and targeted checks**
Run: `git diff --check && node --test tests/useful-guides.test.mjs tests/content-audit.test.mjs`
Expected: PASS with no whitespace errors.
- [ ] **Step 3: Run the complete verification workflow**
Run: `npm run verify`
Expected: Astro validation, all Node tests, content audit, production build, rendered-output check, and internal-link check all PASS.
- [ ] **Step 4: Review the built routes**
Confirm these files exist and contain rendered `<main>` content:
```text
dist/useful-information/housing-guide/index.html
dist/useful-information/laptop-buying-advice/index.html
dist/useful-information/linux-tricks/index.html
dist/useful-information/surviving-dacs/index.html
```
Run:
```bash
rg -l '<main' dist/useful-information/{housing-guide,laptop-buying-advice,linux-tricks,surviving-dacs}/index.html
```
Expected: all four files are listed.
- [ ] **Step 5: Commit documentation and any final verification fixes**
```bash
git add docs/migration-report.md to-be-studied/README.md
git commit -m "docs: record useful guides publication"
```
- [ ] **Step 6: Confirm the final worktree state**
Run: `git status --short && git log -4 --oneline`
Expected: no uncommitted changes; the four implementation commits appear above the design and plan history.

View file

@ -0,0 +1,672 @@
# GDPR-Gated Matomo Tracking 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:** Add consent-gated, cookieless Matomo analytics to every Starlight page without contacting the analytics origin before an affirmative visitor choice.
**Architecture:** A dependency-free classic browser script in `public/` owns consent persistence, the accessible consent UI, and conditional Matomo loading. Starlight's supported global `head` option loads only that local controller, with an Astro-base-aware URL; the existing custom stylesheet owns the UI presentation.
**Tech Stack:** Astro 7.1.6, Starlight 0.41.6, browser DOM APIs, Matomo JavaScript tracker API, Node's built-in test runner, `linkedom` 0.18.13.
## Global Constraints
- Do not request `https://analytics.msvincognito.nl/matomo.js` or create `window._paq` before affirmative consent.
- Use tracker endpoint `https://analytics.msvincognito.nl/matomo.php` and site ID `1`.
- Call `requireConsent`, `disableCookies`, and `setConsentGiven` before `trackPageView`.
- Declining analytics must not reduce wiki functionality or be visually harder than accepting.
- Keep a persistent **Privacy settings** control so consent can be changed or withdrawn.
- Link consent information to `https://msvincognito.nl/privacy-policy`.
- Add no third-party consent-management or analytics package.
- Preserve deployments at `/` and at a configured `BASE` subpath.
- Treat this frontend as one part of compliance; document the required Matomo server and privacy-notice follow-up.
## Execution refinement
During inline execution, Task 2's planned source-text assertions in `tests/project-structure.test.mjs` were replaced by `tests/matomo-build.test.mjs`. The replacement runs a real Astro production build with `BASE=/Incognito-Wiki` and asserts on the generated HTML, copied local controller, and emitted CSS. This follows the test-quality requirement to verify observable behavior rather than grep implementation text; `tests/project-structure.test.mjs` therefore remains unchanged.
---
## File map
- Create `public/matomo-consent.js`: dependency-free browser controller for consent storage, accessible UI, Matomo initialization, duplicate guards, and withdrawal.
- Create `tests/matomo-consent.test.mjs`: behavioral tests that execute the real controller in a lightweight DOM.
- Modify `astro.config.mjs`: register the local controller globally using Starlight `head` and a normalized Astro base path.
- Modify `src/styles/incognito.css`: style the consent banner, equal-choice actions, and persistent settings control.
- Modify `tests/project-structure.test.mjs`: lock down global registration, base-path handling, privacy link, and styling hooks.
- Modify `README.md`: document analytics behavior and the server-side deployment checklist.
---
### Task 1: Consent controller behavior
**Files:**
- Create: `public/matomo-consent.js`
- Create: `tests/matomo-consent.test.mjs`
**Interfaces:**
- Consumes: browser `window`, `document`, and best-effort `window.localStorage`.
- Produces: storage key `incognito.analytics-consent.v1` with values `accepted` or `declined`; DOM IDs `incognito-analytics-consent`, `incognito-analytics-accept`, `incognito-analytics-decline`, and `incognito-privacy-settings`; guarded globals `window.__incognitoMatomoConsentController` and `window.__incognitoMatomoInitialized`.
- Loads: `https://analytics.msvincognito.nl/matomo.js` with element ID `incognito-matomo-script` only after consent.
- [ ] **Step 1: Write the failing first-visit and acceptance tests**
Create `tests/matomo-consent.test.mjs` with a harness that runs the real classic script in `linkedom`:
```js
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
import vm from 'node:vm';
import { parseHTML } from 'linkedom';
const controllerSource = await readFile('public/matomo-consent.js', 'utf8');
const storageKey = 'incognito.analytics-consent.v1';
function runController({ decision, storageError = false } = {}) {
const { window } = parseHTML('<!doctype html><html><head></head><body></body></html>');
const values = new Map(decision ? [[storageKey, decision]] : []);
const localStorage = {
getItem(key) {
if (storageError) throw new Error('storage unavailable');
return values.get(key) ?? null;
},
setItem(key, value) {
if (storageError) throw new Error('storage unavailable');
values.set(key, value);
},
};
Object.defineProperty(window, 'localStorage', { value: localStorage });
vm.runInNewContext(controllerSource, { window, document: window.document });
return { window, document: window.document, values };
}
test('first visit offers equal choices without initializing Matomo', () => {
const { window, document } = runController();
assert.equal(window._paq, undefined);
assert.equal(document.querySelector('script[src*="analytics.msvincognito.nl"]'), null);
assert.equal(document.getElementById('incognito-analytics-consent').hidden, false);
assert.equal(document.getElementById('incognito-analytics-accept').className,
document.getElementById('incognito-analytics-decline').className);
assert.equal(
document.querySelector('#incognito-analytics-consent a').href,
'https://msvincognito.nl/privacy-policy',
);
});
test('accepting persists consent and initializes cookieless Matomo once', () => {
const { window, document, values } = runController();
document.getElementById('incognito-analytics-accept').click();
document.getElementById('incognito-analytics-accept').click();
assert.equal(values.get(storageKey), 'accepted');
assert.deepEqual(Array.from(window._paq), [
['requireConsent'],
['disableCookies'],
['setConsentGiven'],
['setTrackerUrl', 'https://analytics.msvincognito.nl/matomo.php'],
['setSiteId', '1'],
['trackPageView'],
['enableLinkTracking'],
]);
const scripts = document.querySelectorAll('#incognito-matomo-script');
assert.equal(scripts.length, 1);
assert.equal(scripts[0].src, 'https://analytics.msvincognito.nl/matomo.js');
assert.equal(scripts[0].async, true);
});
```
- [ ] **Step 2: Run the new test to verify RED**
Run:
```bash
node --test tests/matomo-consent.test.mjs
```
Expected: FAIL because `public/matomo-consent.js` does not exist.
- [ ] **Step 3: Add the minimal controller that passes first-visit and acceptance behavior**
Create `public/matomo-consent.js` as a classic script. Keep all implementation inside an IIFE so only the two duplicate-guard flags and Matomo's `_paq` are global:
```js
(function initializeAnalyticsConsent(window, document) {
'use strict';
var storageKey = 'incognito.analytics-consent.v1';
var accepted = 'accepted';
var declined = 'declined';
var trackerOrigin = 'https://analytics.msvincognito.nl/';
if (window.__incognitoMatomoConsentController) return;
window.__incognitoMatomoConsentController = true;
function readDecision() {
try {
var value = window.localStorage.getItem(storageKey);
return value === accepted || value === declined ? value : null;
} catch (_error) {
return null;
}
}
function writeDecision(value) {
try {
window.localStorage.setItem(storageKey, value);
return true;
} catch (_error) {
return false;
}
}
function initializeMatomo() {
var queue = window._paq = window._paq || [];
if (window.__incognitoMatomoInitialized) return queue;
window.__incognitoMatomoInitialized = true;
queue.push(['requireConsent']);
queue.push(['disableCookies']);
queue.push(['setConsentGiven']);
queue.push(['setTrackerUrl', trackerOrigin + 'matomo.php']);
queue.push(['setSiteId', '1']);
queue.push(['trackPageView']);
queue.push(['enableLinkTracking']);
var tracker = document.createElement('script');
tracker.id = 'incognito-matomo-script';
tracker.async = true;
tracker.src = trackerOrigin + 'matomo.js';
document.head.appendChild(tracker);
return queue;
}
function createButton(id, label) {
var button = document.createElement('button');
button.id = id;
button.className = 'incognito-consent-action';
button.type = 'button';
button.textContent = label;
return button;
}
var banner = document.createElement('section');
banner.id = 'incognito-analytics-consent';
banner.setAttribute('role', 'dialog');
banner.setAttribute('aria-labelledby', 'incognito-analytics-consent-title');
banner.innerHTML =
'<div class="incognito-consent-copy">' +
'<h2 id="incognito-analytics-consent-title">Privacy-friendly analytics</h2>' +
'<p>MSV Incognito would like to use cookieless Matomo analytics to understand how the wiki is used. Nothing is sent to Matomo unless you accept. You can change your choice at any time.</p>' +
'<a href="https://msvincognito.nl/privacy-policy">Read our privacy policy</a>' +
'</div>';
var actions = document.createElement('div');
actions.className = 'incognito-consent-actions';
var acceptButton = createButton('incognito-analytics-accept', 'Accept analytics');
var declineButton = createButton('incognito-analytics-decline', 'Decline');
actions.append(acceptButton, declineButton);
banner.appendChild(actions);
var settingsButton = document.createElement('button');
settingsButton.id = 'incognito-privacy-settings';
settingsButton.type = 'button';
settingsButton.textContent = 'Privacy settings';
function showBanner() {
banner.hidden = false;
settingsButton.hidden = true;
acceptButton.focus();
}
function hideBanner() {
banner.hidden = true;
settingsButton.hidden = false;
settingsButton.focus();
}
acceptButton.addEventListener('click', function acceptAnalytics() {
writeDecision(accepted);
if (window.__incognitoMatomoInitialized) {
window._paq.push(['setConsentGiven']);
window._paq.push(['trackPageView']);
} else {
initializeMatomo();
}
hideBanner();
});
declineButton.addEventListener('click', function declineAnalytics() {
writeDecision(declined);
if (window._paq) {
window._paq.push(['forgetConsentGiven']);
window._paq.push(['deleteCookies']);
}
hideBanner();
});
settingsButton.addEventListener('click', showBanner);
document.body.append(banner, settingsButton);
if (readDecision() === accepted) {
banner.hidden = true;
settingsButton.hidden = false;
initializeMatomo();
} else if (readDecision() === declined) {
banner.hidden = true;
settingsButton.hidden = false;
} else {
showBanner();
}
})(window, document);
```
During implementation, avoid calling `readDecision()` twice: store it in a local variable before the final branch. The duplicate click in the test must not generate a second page view; make `hideBanner()` or the accept handler ignore clicks while the banner is hidden.
- [ ] **Step 4: Run the focused test and make the minimal corrections required for GREEN**
Run:
```bash
node --test tests/matomo-consent.test.mjs
```
Expected: PASS with 2 tests and no warnings.
- [ ] **Step 5: Add decline, storage-failure, remembered-consent, and withdrawal tests**
Append these behaviors to `tests/matomo-consent.test.mjs`:
```js
test('declining persists the choice without creating Matomo state', () => {
const { window, document, values } = runController();
document.getElementById('incognito-analytics-decline').click();
assert.equal(values.get(storageKey), 'declined');
assert.equal(window._paq, undefined);
assert.equal(document.getElementById('incognito-matomo-script'), null);
assert.equal(document.getElementById('incognito-privacy-settings').hidden, false);
});
test('remembered acceptance tracks on a later page load', () => {
const { window, document } = runController({ decision: 'accepted' });
assert.ok(window._paq.some(([method]) => method === 'trackPageView'));
assert.equal(document.querySelectorAll('#incognito-matomo-script').length, 1);
assert.equal(document.getElementById('incognito-analytics-consent').hidden, true);
});
test('remembered decline never initializes Matomo', () => {
const { window, document } = runController({ decision: 'declined' });
assert.equal(window._paq, undefined);
assert.equal(document.getElementById('incognito-matomo-script'), null);
assert.equal(document.getElementById('incognito-privacy-settings').hidden, false);
});
test('storage failure defaults to no tracking and keeps consent available', () => {
const { window, document } = runController({ storageError: true });
assert.equal(window._paq, undefined);
assert.equal(document.getElementById('incognito-analytics-consent').hidden, false);
});
test('privacy settings allow an accepted visitor to withdraw consent', () => {
const { window, document, values } = runController({ decision: 'accepted' });
document.getElementById('incognito-privacy-settings').click();
document.getElementById('incognito-analytics-decline').click();
assert.equal(values.get(storageKey), 'declined');
assert.deepEqual(Array.from(window._paq).slice(-2), [
['forgetConsentGiven'],
['deleteCookies'],
]);
const later = runController({ decision: values.get(storageKey) });
assert.equal(later.window._paq, undefined);
assert.equal(later.document.getElementById('incognito-matomo-script'), null);
});
test('running the local controller twice does not duplicate UI or tracking', () => {
const state = runController({ decision: 'accepted' });
vm.runInNewContext(controllerSource, { window: state.window, document: state.document });
assert.equal(state.document.querySelectorAll('#incognito-analytics-consent').length, 1);
assert.equal(state.document.querySelectorAll('#incognito-matomo-script').length, 1);
assert.equal(state.window._paq.filter(([method]) => method === 'trackPageView').length, 1);
});
```
- [ ] **Step 6: Run the expanded focused test to verify RED**
Run:
```bash
node --test tests/matomo-consent.test.mjs
```
Expected: at least the withdrawal or duplicate-initialization assertion FAILS until the controller fully guards state transitions.
- [ ] **Step 7: Refine the controller minimally to make every state transition pass**
Make the following exact corrections in `public/matomo-consent.js`:
```js
var decision = readDecision();
acceptButton.addEventListener('click', function acceptAnalytics() {
if (banner.hidden) return;
writeDecision(accepted);
if (window.__incognitoMatomoInitialized) {
window._paq.push(['setConsentGiven']);
window._paq.push(['trackPageView']);
} else {
initializeMatomo();
}
hideBanner();
});
declineButton.addEventListener('click', function declineAnalytics() {
if (banner.hidden) return;
writeDecision(declined);
if (window._paq) {
window._paq.push(['forgetConsentGiven']);
window._paq.push(['deleteCookies']);
}
hideBanner();
});
if (decision === accepted) {
banner.hidden = true;
settingsButton.hidden = false;
initializeMatomo();
} else if (decision === declined) {
banner.hidden = true;
settingsButton.hidden = false;
} else {
showBanner();
}
```
Keep the identical `incognito-consent-action` class on both consent buttons. Do not add an automatic timeout, scroll consent, geolocation exception, or remote request in the no-decision/declined branches.
- [ ] **Step 8: Run the focused controller tests to verify GREEN**
Run:
```bash
node --test tests/matomo-consent.test.mjs
```
Expected: PASS with 8 tests and no warnings.
- [ ] **Step 9: Commit the independently working controller**
```bash
git add public/matomo-consent.js tests/matomo-consent.test.mjs
git commit -m "feat: add consent-gated Matomo controller"
```
---
### Task 2: Starlight integration, presentation, and operations
**Files:**
- Modify: `astro.config.mjs`
- Modify: `src/styles/incognito.css`
- Modify: `tests/project-structure.test.mjs`
- Modify: `README.md`
**Interfaces:**
- Consumes: `public/matomo-consent.js` and its DOM IDs/classes from Task 1.
- Produces: one deferred `<script src="{BASE}/matomo-consent.js">` on every generated Starlight page and responsive consent UI styles.
- [ ] **Step 1: Write failing structure tests for global, base-aware integration and privacy UI assets**
Append to `tests/project-structure.test.mjs`:
```js
test('Matomo consent controller is registered globally with base-path support', async () => {
const controller = await stat('public/matomo-consent.js');
assert.ok(controller.size > 0, 'public/matomo-consent.js should not be empty');
const config = await readFile('astro.config.mjs', 'utf8');
assert.match(config, /const normalizedBase/);
assert.match(config, /head:\s*\[/);
assert.match(config, /matomo-consent\.js/);
assert.match(config, /defer:\s*true/);
});
test('Matomo consent controls have shared actions and persistent settings styles', async () => {
const css = await readFile('src/styles/incognito.css', 'utf8');
assert.match(css, /#incognito-analytics-consent/);
assert.match(css, /\.incognito-consent-action/);
assert.match(css, /#incognito-privacy-settings/);
const controller = await readFile('public/matomo-consent.js', 'utf8');
assert.match(controller, /https:\/\/msvincognito\.nl\/privacy-policy/);
assert.doesNotMatch(controller, /['"]\/\/analytics\.msvincognito\.nl/);
});
test('analytics operations document server-side privacy requirements', async () => {
const readme = await readFile('README.md', 'utf8');
assert.match(readme, /Matomo analytics/i);
assert.match(readme, /IP anonymization/i);
assert.match(readme, /retention/i);
assert.match(readme, /privacy policy/i);
});
```
- [ ] **Step 2: Run the structure tests to verify RED**
Run:
```bash
node --test tests/project-structure.test.mjs
```
Expected: FAIL because `astro.config.mjs`, the stylesheet, and README do not yet contain the required integration and documentation.
- [ ] **Step 3: Register the local controller in Starlight's global head**
Modify the configuration prelude in `astro.config.mjs`:
```js
const site = process.env.SITE || 'http://localhost:4321';
const base = process.env.BASE || '/';
const normalizedBase = base === '/'
? ''
: `/${base.replace(/^\/+|\/+$/g, '')}`;
const matomoConsentScript = `${normalizedBase}/matomo-consent.js`;
```
Add this option inside the existing `starlight({ ... })` call, next to `customCss`:
```js
head: [
{
tag: 'script',
attrs: {
src: matomoConsentScript,
defer: true,
},
},
],
```
Do not add the remote Matomo origin to `astro.config.mjs`; the local controller remains the only global script.
- [ ] **Step 4: Add responsive, accessible consent presentation**
Append to `src/styles/incognito.css`:
```css
#incognito-analytics-consent {
position: fixed;
z-index: 1000;
inset-inline: max(1rem, env(safe-area-inset-left)) max(1rem, env(safe-area-inset-right));
inset-block-end: max(1rem, env(safe-area-inset-bottom));
display: grid;
gap: 1rem;
width: min(44rem, calc(100% - 2rem));
margin-inline: auto;
padding: 1.25rem;
color: var(--sl-color-white);
background: var(--incognito-navy);
border: 1px solid color-mix(in srgb, var(--incognito-bright) 45%, transparent);
border-radius: 0.75rem;
box-shadow: 0 1rem 3rem rgb(0 0 0 / 30%);
}
#incognito-analytics-consent[hidden],
#incognito-privacy-settings[hidden] {
display: none;
}
.incognito-consent-copy h2 {
margin: 0 0 0.5rem;
color: inherit;
font-size: 1.125rem;
}
.incognito-consent-copy p {
margin: 0 0 0.5rem;
line-height: 1.55;
}
.incognito-consent-copy a {
color: var(--incognito-light);
}
.incognito-consent-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem;
}
.incognito-consent-action,
#incognito-privacy-settings {
min-height: 2.75rem;
border: 2px solid var(--incognito-light);
border-radius: 0.5rem;
color: var(--incognito-navy);
background: var(--incognito-light);
font: inherit;
font-weight: 700;
cursor: pointer;
}
.incognito-consent-action:focus-visible,
#incognito-privacy-settings:focus-visible {
outline: 3px solid var(--incognito-pink);
outline-offset: 3px;
}
#incognito-privacy-settings {
position: fixed;
z-index: 999;
inset-inline-end: max(1rem, env(safe-area-inset-right));
inset-block-end: max(1rem, env(safe-area-inset-bottom));
min-height: 2.25rem;
padding-inline: 0.75rem;
border-width: 1px;
font-size: 0.8125rem;
}
@media (min-width: 50rem) {
#incognito-analytics-consent {
grid-template-columns: minmax(0, 1fr) 18rem;
align-items: end;
}
}
@media (max-width: 30rem) {
.incognito-consent-actions {
grid-template-columns: 1fr;
}
}
```
The two decision buttons intentionally share one class and identical presentation. Do not introduce color, order, or size differences that nudge acceptance.
- [ ] **Step 5: Document operational privacy requirements**
Add this section to `README.md` after Deployment:
```markdown
## Matomo analytics
The wiki uses self-hosted Matomo only after a visitor explicitly accepts analytics. The client is loaded from `https://analytics.msvincognito.nl/`, uses site ID `1`, and disables analytics cookies. Visitors can decline without losing functionality and can reopen **Privacy settings** to withdraw consent.
Before deploying analytics changes, the Matomo administrator must verify:
- IP anonymization is enabled;
- raw logs and analytics reports use documented, proportionate retention periods;
- administrator access is restricted and reviewed;
- analytics data is not reused for advertising or cross-site profiling; and
- the [MSV Incognito privacy policy](https://msvincognito.nl/privacy-policy) accurately states the controller, purpose, data categories, retention, withdrawal process, and data-subject rights.
The frontend consent gate is only one part of GDPR and ePrivacy compliance. Revisit the legal and server configuration when Matomo features or processing purposes change.
```
- [ ] **Step 6: Run focused tests to verify GREEN**
Run:
```bash
node --test tests/matomo-consent.test.mjs tests/project-structure.test.mjs
```
Expected: PASS with all controller and structure tests and no warnings.
- [ ] **Step 7: Build at the root and inspect rendered integration**
Run:
```bash
npm run build
rg -n 'src="/matomo-consent\.js"' dist/index.html
rg -n 'incognito-analytics-consent|incognito-privacy-settings' dist/_astro/*.css
```
Expected: build exits 0; `dist/index.html` contains exactly one deferred local controller URL; generated CSS contains both consent UI selectors.
- [ ] **Step 8: Build with a subpath and verify the controller URL is base-aware**
Run:
```bash
SITE=https://example.github.io BASE=/Incognito-Wiki npm run build
rg -n 'src="/Incognito-Wiki/matomo-consent\.js"' dist/index.html
```
Expected: build exits 0 and the generated script URL begins with `/Incognito-Wiki/`.
- [ ] **Step 9: Run the complete project verification from a fresh root build**
Run:
```bash
npm run verify
```
Expected: `astro check`, every Node test, content audit, production build, rendered-output check, and internal-link check all exit 0.
- [ ] **Step 10: Inspect the final diff and commit the integration**
Run:
```bash
git diff --check
git status --short
git diff -- astro.config.mjs src/styles/incognito.css tests/project-structure.test.mjs README.md
```
Confirm that the diff contains no remote Matomo `<script>` in Astro configuration, no tracking-before-consent path, and no unrelated changes. Then commit:
```bash
git add astro.config.mjs src/styles/incognito.css tests/project-structure.test.mjs README.md
git commit -m "feat: integrate privacy-first Matomo analytics"
```

View file

@ -0,0 +1,607 @@
# Programme Sidebar and Course Integration Implementation Plan
> **Execution note:** Follow this plan task by task with test-driven development. Keep `main` green at every commit and do not merge `origin/isaacs-changes`; copy only the five reviewed substantive pages from commit `d5d6730`.
**Goal:** Publish the useful Computer Science material, give Computer Science and Data Science & AI stable programme-specific routes, remove duplicated shared course bodies, and show a route-aware programme switch without hiding global wiki navigation.
**Architecture:** The docs collection owns public pages. Seven shared course bodies live as unpublished MDX partials and are imported by thin programme-specific wrappers. Pure navigation helpers select the programme, paired switch target, and sidebar tree from the pathname; Astro middleware applies that tree to Starlight route data, while a `Sidebar` component override renders the accessible switch and delegates the actual navigation to Starlight's default component. Astro redirects preserve every legacy `/bachelor/...` route.
**Stack:** Astro 7, Starlight 0.41, MD/MDX content collections, Astro middleware and component overrides, Node's built-in test runner, `linkedom`, existing migration/audit scripts.
---
## Non-negotiable content decisions
- Treat all current `src/content/docs/bachelor/**` pages as Data Science & AI and move them to `src/content/docs/data-science-and-ai/**`.
- Preserve every old `/bachelor/...` URL with an explicit permanent redirect to the exact new route.
- Import only these substantive Computer Science pages from `d5d6730`:
- `computer-science/year-1/period-1/introduction-to-computer-science.md`
- `computer-science/year-1/period-4/computer-architecture.md`
- `computer-science/year-1/period-5/algorithmic-design.md`
- `computer-science/year-1/period-5/databases.md`
- `computer-science/year-1/period-5/statistics.md`
- Do not import the malformed `computer-science/course-description.md`, any `Empty Page` placeholder, or the branch deletion of `previous-exams-and-documents.md`.
- Store the body of each byte-identical shared course once, outside `src/content/docs`, and expose it through two programme wrappers:
| Shared body | Data Science & AI route | Computer Science route |
| --- | --- | --- |
| Discrete Mathematics | `year-1/block-1/discrete-mathematics` | `year-1/period-1/discrete-mathematics` |
| Procedural Programming | `year-1/block-1/procedural-programming` | `year-1/period-1/procedural-programming` |
| Calculus | `year-1/block-2/calculus` | `year-1/period-2/calculus` |
| Logic | `year-1/block-2/logic` | `year-1/period-2/logic` |
| Objects in Programming | `year-1/block-2/objects-in-programming` | `year-1/period-2/objects-in-programming` |
| Data Structures and Algorithms | `year-1/block-4/data-structures-and-algorithms` | `year-1/period-4/data-structures-and-algorithms` |
| Linear Algebra | `year-1/block-4/linear-algebra` | `year-1/period-4/linear-algebra` |
## Task 1: Add migration-contract tests for the canonical DSAI route move
**Files:**
- Create: `tests/programme-content.test.mjs`
- Modify: `tests/content-audit.test.mjs`
- Modify: `tests/project-structure.test.mjs`
- Modify: `tests/pdf-archive.test.mjs`
- Modify: `scripts/audit-content.mjs`
- Modify: `scripts/lib/migration.mjs`
- Modify: `docs/migration-manifest.json`
- Modify: `docs/migration-report.md`
- Modify: `docs/supplemental-content.json`
- Modify: `src/content/docs/index.mdx`
- Rename: `src/content/docs/bachelor/` to `src/content/docs/data-science-and-ai/`
### Step 1: Write the failing route/provenance assertions
In `tests/programme-content.test.mjs`, add assertions that:
```js
const legacyRoot = 'src/content/docs/bachelor';
const dsaiRoot = 'src/content/docs/data-science-and-ai';
await assert.rejects(stat(legacyRoot), { code: 'ENOENT' });
assert.ok((await stat(dsaiRoot)).isDirectory());
```
Also recursively inspect the manifest, supplemental registry, migration report, landing page, audit exceptions, and migration canonical map so no destination or internal link contains `src/content/docs/bachelor/` or `href="./bachelor/"`, while the historical DokuWiki source identifiers under `pages/study/bachelor` remain unchanged.
Update existing tests to expect `data-science-and-ai` destinations and landing-page links. Keep the expected source count `29`, unique migration destinations `28`, and migration-report row count `29`; only destination paths change in this task.
### Step 2: Run the focused tests and confirm red
Run:
```sh
node --test tests/programme-content.test.mjs tests/content-audit.test.mjs tests/project-structure.test.mjs tests/pdf-archive.test.mjs
```
Expected: failure because the DSAI tree and provenance records still use `bachelor`.
### Step 3: Move the tree and update provenance atomically
Rename the directory once so Git can retain rename history:
```sh
mv src/content/docs/bachelor src/content/docs/data-science-and-ai
```
Update all destination paths in the manifest, migration report, supplemental registry, audit exception map, and canonical source map from `src/content/docs/bachelor/` to `src/content/docs/data-science-and-ai/`. Update internal public links from `/bachelor/` to `/data-science-and-ai/`, including the landing-page card. Do not change the original source paths or DokuWiki IDs because they document provenance.
Update the programme overview/year wording from generic “Bachelor” to “Data Science & AI” only where the page is describing the current published destination; preserve quoted or historical source terminology when necessary for accuracy.
### Step 4: Run the focused tests and content audit
Run:
```sh
node --test tests/programme-content.test.mjs tests/content-audit.test.mjs tests/project-structure.test.mjs tests/pdf-archive.test.mjs
npm run audit:content
```
Expected: all pass, with the same `29` sources, `28` unique manifest destinations, `86` published pages, and `58` supplemental pages.
### Step 5: Commit
```sh
git add src/content/docs/data-science-and-ai src/content/docs/index.mdx docs/migration-manifest.json docs/migration-report.md docs/supplemental-content.json scripts/audit-content.mjs scripts/lib/migration.mjs tests/programme-content.test.mjs tests/content-audit.test.mjs tests/project-structure.test.mjs tests/pdf-archive.test.mjs
git commit -m "refactor: give data science canonical programme routes"
```
## Task 2: Preserve every legacy bachelor URL with exact redirects
**Files:**
- Create: `src/config/legacy-bachelor-redirects.mjs`
- Create: `tests/programme-redirects.test.mjs`
- Modify: `astro.config.mjs`
### Step 1: Write a failing redirect coverage test
Build expected redirects by recursively listing all `.md` and `.mdx` pages under `src/content/docs/data-science-and-ai`. Convert `index` files to directory routes and all other filenames to extensionless trailing-slash routes. Assert that `legacyBachelorRedirects` has exactly one key for every DSAI page after replacing the `/data-science-and-ai/` prefix with `/bachelor/`, and that every value is the exact canonical DSAI route.
Add explicit examples:
```js
assert.equal(legacyBachelorRedirects['/bachelor/'], '/data-science-and-ai/');
assert.equal(
legacyBachelorRedirects['/bachelor/year-1/block-2/calculus/'],
'/data-science-and-ai/year-1/block-2/calculus/',
);
assert.equal(
legacyBachelorRedirects['/bachelor/year-3/study-abroad/'],
'/data-science-and-ai/year-3/study-abroad/',
);
```
Also assert `astro.config.mjs` passes the map through the top-level `redirects` property.
### Step 2: Run the test and confirm red
```sh
node --test tests/programme-redirects.test.mjs
```
Expected: module-not-found or missing redirects.
### Step 3: Implement the explicit redirect map
Create `legacy-bachelor-redirects.mjs` as a checked-in plain object. Do not derive redirects at runtime from the filesystem. Include overview, all three year indexes, projects, honours, thesis, study abroad, and every currently published DSAI course page. Export a frozen object:
```js
export const legacyBachelorRedirects = Object.freeze({
'/bachelor/': '/data-science-and-ai/',
// every exact published descendant follows
});
```
Import it into `astro.config.mjs` and set:
```js
redirects: legacyBachelorRedirects,
```
### Step 4: Prove generated redirects work at root and under BASE
Run:
```sh
node --test tests/programme-redirects.test.mjs
npm run build
BASE=/wiki npm run build
```
Inspect representative generated redirect HTML and assert its destination includes the configured base exactly once.
### Step 5: Commit
```sh
git add astro.config.mjs src/config/legacy-bachelor-redirects.mjs tests/programme-redirects.test.mjs
git commit -m "feat: redirect legacy bachelor routes to data science"
```
## Task 3: Replace seven duplicated course bodies with shared MDX partials
**Files:**
- Create: `src/content/shared-courses/year-1/*.mdx` (7 files)
- Rename: the 7 DSAI shared pages from `.md` to `.mdx`
- Create: the 7 corresponding `src/content/docs/computer-science/year-1/period-*/...mdx` wrappers
- Create: `tests/shared-programme-courses.test.mjs`
- Modify: `astro.config.mjs`
- Modify: `docs/supplemental-content.json`
### Step 1: Write failing shared-source tests
Define the seven route pairs in the test. For each pair, assert:
- both published wrappers exist and contain valid `title` and `description` frontmatter;
- both import the same file under `src/content/shared-courses/year-1/`;
- neither wrapper contains the course sections duplicated from the partial;
- the partial is outside the docs collection and contains the substantive body;
- each wrapper contains the standard historical caution;
- no shared course appears as a second body file anywhere else.
Also assert Starlight config includes:
```js
markdown: {
processedDirs: ['./src/content/shared-courses/'],
},
```
Add the seven Computer Science wrappers to the supplemental registry with a provenance kind such as `shared-course-wrapper` and retain the DSAI registry entries with their migrated destinations.
### Step 2: Run the test and confirm red
```sh
node --test tests/shared-programme-courses.test.mjs
```
Expected: missing partials and Computer Science wrappers.
### Step 3: Extract each DSAI body exactly once
For each shared course, retain the existing frontmatter values and historical caution in its DSAI wrapper. Move everything after the caution into the matching partial. The wrapper structure must be:
```mdx
---
title: Course title
description: Existing verified description
---
import SharedCourse from '../../../../shared-courses/year-1/course-slug.mdx';
:::caution[Historical information]
This information originated in the previous wiki and may be outdated.
:::
<SharedCourse />
```
Create the Computer Science wrapper with the same shared import and warning, but programme-specific frontmatter. Add an empty, documented programme-notes seam only when content actually diverges later; do not add speculative notes now.
### Step 4: Verify preservation and rendering
Run:
```sh
node --test tests/shared-programme-courses.test.mjs tests/content-audit.test.mjs
npm run audit:content
npm run build
```
Expected: both routes render the same substantive headings and links, exactly one `<h1>` per page, and the shared partials do not become public routes.
Update the audit count assertions to the actual new published-page total: `86 + 7 = 93`; supplemental total becomes `58 + 7 = 65`. The seven partials are excluded from both published counts.
### Step 5: Commit
```sh
git add astro.config.mjs src/content/shared-courses src/content/docs/data-science-and-ai/year-1 src/content/docs/computer-science docs/supplemental-content.json tests/shared-programme-courses.test.mjs tests/content-audit.test.mjs
git commit -m "refactor: share common bachelor course content"
```
## Task 4: Import the five substantive Computer Science pages and clean overviews
**Files:**
- Create: `src/content/docs/computer-science/index.md`
- Create: `src/content/docs/computer-science/year-1/index.md`
- Create: five reviewed Computer Science course pages under `year-1/period-*`
- Modify: `docs/supplemental-content.json`
- Modify: `tests/programme-content.test.mjs`
- Modify: `tests/content-audit.test.mjs`
### Step 1: Write failing content-selection tests
Assert the exact five unique course destinations exist, have valid frontmatter, contain the standard historical caution, and contain substantive headings or text from `d5d6730`.
Assert forbidden imports are absent:
```js
assert.doesNotMatch(allComputerScienceContent, /^#Hello$/m);
assert.doesNotMatch(allComputerScienceContent, /Empty Page/);
assert.doesNotMatch(allComputerScienceContent, /course-description/i);
```
Assert there are exactly `14` Computer Science published pages at this point: overview, Year 1 overview, five unique courses, and seven shared wrappers. Assert `previous-exams-and-documents.md` still exists.
### Step 2: Run the test and confirm red
```sh
node --test tests/programme-content.test.mjs
```
Expected: five unique pages and two overview pages are missing.
### Step 3: Copy only reviewed branch blobs through explicit patches
Use read-only `git show d5d6730:<path>` to inspect each source, normalize frontmatter and the standard warning, remove any body H1, and add only the five approved pages. Do not perform a merge or checkout from the branch.
Write a concise Computer Science overview and Year 1 overview based only on the published course set. State that the material is recovered historical wiki content and may not represent the current curriculum. Link to official Maastricht University curriculum information and to Previous Exams without inventing year 2 or year 3 content.
Register all seven new pages (five courses plus two overviews) in `docs/supplemental-content.json` with source commit `d5d6730`, original branch path where applicable, and a note that malformed/placeholder pages were excluded.
### Step 4: Audit and build
```sh
node --test tests/programme-content.test.mjs tests/content-audit.test.mjs
npm run audit:content
npm run build
```
Expected final published count for this content phase: `100` pages, with `72` supplemental destinations (`65 + 7`). Verify actual counts from the audit and encode them in the tests only after confirming the registry matches the filesystem.
### Step 5: Commit
```sh
git add src/content/docs/computer-science docs/supplemental-content.json tests/programme-content.test.mjs tests/content-audit.test.mjs
git commit -m "feat: publish reviewed computer science courses"
```
## Task 5: Build pure programme navigation data and route selection
**Files:**
- Create: `src/config/programme-navigation.mjs`
- Create: `tests/programme-navigation.test.mjs`
- Modify: `src/config/recovered-course-sidebar.mjs`
- Modify: `src/config/sidebar.mjs`
### Step 1: Write failing pure-function tests
Test `programmeForPathname()`, `programmeSwitchTargets()`, and `sidebarForPathname()` without Astro. Cover:
```js
programmeForPathname('/computer-science/year-1/period-2/calculus/') === 'computer-science'
programmeForPathname('/data-science-and-ai/year-3/') === 'data-science-and-ai'
programmeForPathname('/bachelor/year-1/') === 'data-science-and-ai'
programmeForPathname('/useful-information/') === null
```
Switch mapping cases:
- shared CS course -> paired DSAI course;
- shared DSAI course -> paired CS course;
- unique CS course -> DSAI Year 1;
- unique DSAI Year 2/3 course -> Computer Science overview;
- programme overview/year page -> corresponding overview/year fallback where it exists;
- global page -> both programme overviews with neither active.
Sidebar assertions must prove:
- exactly one detailed undergraduate programme tree is visible on programme pages;
- global pages show compact links to both programme overviews;
- Home, About, Previous exams, Master AI, Master DSDM, and Useful Information remain present for every route;
- Useful Information retains all guide children.
### Step 2: Run and confirm red
```sh
node --test tests/programme-navigation.test.mjs
```
Expected: navigation module is missing.
### Step 3: Implement immutable navigation structures
Export:
```js
export const sharedCoursePairs = Object.freeze({ /* 7 bidirectional route pairs */ });
export const computerScienceSidebar = [/* overview, Year 1, periods and 12 courses */];
export const dataScienceSidebar = [/* migrated overview, Years 13 and all existing pages */];
export const globalSidebar = [/* Home, About, exams, masters, Useful Information */];
export function programmeForPathname(pathname) { /* prefix match incl. /bachelor */ }
export function programmeSwitchTargets(pathname) { /* pair/fallback policy */ }
export function sidebarForPathname(pathname) { /* compose programme/global tree */ }
```
Normalize the recovered-course keys/slugs from `bachelor/...` to `data-science-and-ai/...`. Keep course ordering by period/block, not alphabetically across the whole year. The Computer Science tree contains only Year 1 and only the twelve published courses.
Make `src/config/sidebar.mjs` export a default global/compact sidebar for Starlight's initial configuration and re-export the selector data needed by middleware. Avoid duplicating masters or Useful Information in two source files.
### Step 4: Run tests
```sh
node --test tests/programme-navigation.test.mjs tests/useful-guides.test.mjs tests/pdf-archive.test.mjs
```
Expected: all pass.
### Step 5: Commit
```sh
git add src/config/programme-navigation.mjs src/config/recovered-course-sidebar.mjs src/config/sidebar.mjs tests/programme-navigation.test.mjs tests/useful-guides.test.mjs tests/pdf-archive.test.mjs
git commit -m "feat: add route-aware programme navigation data"
```
## Task 6: Apply route-aware sidebar data through Astro middleware
**Files:**
- Create: `src/middleware.ts`
- Create: `tests/programme-middleware.test.mjs`
### Step 1: Write a failing middleware behavior test
Export a small handler factory or pure mutation helper so the Node test can provide a fake context with `url.pathname` and `locals.starlightRoute.sidebar`. Assert that it replaces only the sidebar and preserves the rest of route data.
Test Computer Science, DSAI, legacy `/bachelor`, master, Useful Information, and home paths. Include a no-Starlight-locals guard so non-Starlight routes/assets are unaffected.
### Step 2: Run and confirm red
```sh
node --test tests/programme-middleware.test.mjs
```
Expected: middleware module is missing.
### Step 3: Implement middleware using Starlight's supported route data
Use Astro's `defineMiddleware` and the pure selector:
```ts
import { defineMiddleware } from 'astro:middleware';
import { sidebarForPathname } from './config/programme-navigation.mjs';
export const onRequest = defineMiddleware(async (context, next) => {
const response = await next();
if (context.locals.starlightRoute) {
context.locals.starlightRoute.sidebar = sidebarForPathname(context.url.pathname);
}
return response;
});
```
Adjust ordering to the Starlight 0.41 route-data lifecycle if the focused integration build shows route data must be mutated before `next()`. Keep all selection logic in the pure module, not in middleware.
### Step 4: Run test and focused builds
```sh
node --test tests/programme-middleware.test.mjs tests/programme-navigation.test.mjs
npm run build
BASE=/wiki npm run build
```
Inspect representative built HTML to confirm the active programme tree is present and the inactive detailed tree is absent.
### Step 5: Commit
```sh
git add src/middleware.ts tests/programme-middleware.test.mjs
git commit -m "feat: select programme sidebar per route"
```
## Task 7: Add the accessible programme switch through a Sidebar override
**Files:**
- Create: `src/components/ProgrammeSwitch.astro`
- Create: `src/components/Sidebar.astro`
- Create: `tests/programme-switch.test.mjs`
- Modify: `astro.config.mjs`
- Modify: `src/styles/incognito.css`
### Step 1: Write failing structure/accessibility tests
Assert the override is registered as:
```js
components: {
Sidebar: './src/components/Sidebar.astro',
},
```
Assert `Sidebar.astro` imports and renders the custom switch followed by Starlight's default Sidebar component. Assert the switch:
- uses a `<nav aria-label="Bachelor programme">` landmark;
- renders ordinary `<a>` links for both programmes;
- applies `aria-current="page"` only to the active programme;
- consumes the pure switch-target helper;
- has no client directive and no inline script.
Add rendered-output checks using `linkedom` against representative built pages for desktop/mobile-shared markup and base-prefixed hrefs.
### Step 2: Run and confirm red
```sh
node --test tests/programme-switch.test.mjs
```
Expected: missing override and registration.
### Step 3: Implement the wrapper, not a copy of Starlight internals
`src/components/Sidebar.astro` should remain minimal:
```astro
---
import DefaultSidebar from '@astrojs/starlight/components/Sidebar.astro';
import ProgrammeSwitch from './ProgrammeSwitch.astro';
---
<ProgrammeSwitch />
<DefaultSidebar />
```
`ProgrammeSwitch.astro` reads `Astro.url.pathname`, obtains base-aware targets from the pure helper, and renders two compact links. Use Astro's base URL support so root and `/wiki/` builds do not hard-code `/`.
Style the control in `incognito.css` using existing Starlight color tokens. Keep a visible focus state, do not rely on color alone for active state, permit wrapping at narrow widths, and keep link targets at least as tall as surrounding sidebar links.
### Step 4: Verify component and output
```sh
node --test tests/programme-switch.test.mjs
npm run build
npm run check:rendered
npm run check:links
BASE=/wiki npm run build
npm run check:rendered
BASE=/wiki npm run check:links
```
Expected: accessible switch appears on all Starlight pages, correct option is active on programme pages, neither is active on global pages, and all links honor BASE.
### Step 5: Commit
```sh
git add astro.config.mjs src/components/ProgrammeSwitch.astro src/components/Sidebar.astro src/styles/incognito.css tests/programme-switch.test.mjs
git commit -m "feat: add accessible bachelor programme switch"
```
## Task 8: Final integration audit, documentation reconciliation, and push
**Files:**
- Modify as needed: `README.md`
- Modify as needed: `docs/migration-report.md`
- Modify: tests containing obsolete counts/routes
### Step 1: Search for accidental legacy or rejected content
Run:
```sh
rg -n "src/content/docs/bachelor|href=\"(?:\./|/)bachelor|slug: 'bachelor|#Hello|Empty Page" src docs tests scripts astro.config.mjs
```
Expected: no old destination/sidebar/link references and no rejected branch content. Historical DokuWiki source IDs and the explicit redirect keys are allowed and should be reviewed individually.
### Step 2: Run the complete verification suite at the root base
```sh
npm run verify
```
Expected: Astro check, all Node tests, content audit, build, H1/unpublished-output audit, and internal-link audit all pass.
### Step 3: Repeat build/output verification with a non-root base
```sh
BASE=/wiki npm run build
npm run check:rendered
BASE=/wiki npm run check:links
```
Expected: all programme switch links, content links, Matomo assets, and redirect targets contain `/wiki/` exactly once where required.
### Step 4: Review the diff against the approved exclusions
Run:
```sh
git diff --check
git status --short
git diff --stat origin/main...HEAD
git log --oneline origin/main..HEAD
```
Confirm:
- `previous-exams-and-documents.md` remains present;
- no empty year 2/year 3 Computer Science files exist;
- no malformed duplicate description exists;
- seven shared bodies have one source each;
- DSAI retained all prior pages under the new canonical prefix;
- every old bachelor route redirects;
- global sidebar groups remain visible.
### Step 5: Commit any final reconciliation
If final verification required documentation or assertion corrections:
```sh
git add README.md docs tests
git commit -m "docs: reconcile programme migration records"
```
Do not create an empty commit.
### Step 6: Push main and verify the Forgejo remote
```sh
git push origin main
git ls-remote --heads origin main
git rev-parse HEAD
```
Expected: the remote `main` hash exactly equals local `HEAD`. Report the final commit range, verification commands, published course selection, redirect coverage count, and why `origin/isaacs-changes` was not merged wholesale.

View file

@ -0,0 +1,327 @@
# Compact Programme Switch and Sidebar Filtering 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:** Replace the wide bachelor programme selector with a compact DSAI/CS control and focus programme sidebars on the selected curriculum plus Home, Previous exams and documents, and Useful Information.
**Architecture:** Keep the current URL-derived, no-JavaScript programme state and route-aware switch targets. Concentrate sidebar selection in the existing pure navigation helpers, while `ProgrammeSwitch.astro` remains a presentational link-based component and Starlight middleware consumes the filtered result.
**Tech Stack:** Astro 7, Astro Starlight 0.41, TypeScript/Astro components, CSS custom properties, Node.js 22 built-in test runner.
## Global Constraints
- Visible programme labels are exactly `DSAI` and `CS`.
- Full programme names remain available to assistive technology.
- Programme selection is derived from the URL and is not stored in cookies or local storage.
- The switch uses ordinary links and no client-side JavaScript or hydration directive.
- Computer Science and Data Science & AI routes each show only Home, Previous exams and documents, the active curriculum, and Useful Information.
- About Incognito, Master AI, Master DSDM, and the inactive bachelor curriculum are hidden on programme routes.
- Non-programme routes retain the existing global sidebar.
- Existing paired-course and fallback programme destinations remain unchanged.
- Do not change course content, curriculum structure, or the paired-course mapping.
---
### Task 1: Focus programme-specific sidebar navigation
**Files:**
- Modify: `tests/programme-navigation.test.mjs`
- Modify: `src/config/programme-navigation.mjs:48-68`
**Interfaces:**
- Consumes: `programmeForPathname(pathname: string): 'computer-science' | 'data-science-and-ai' | null` and the existing exported `sidebar` configuration.
- Produces: `sidebarForPathname(pathname: string): Array<SidebarEntry>` and `filterResolvedSidebar(entries: Array<ResolvedSidebarEntry>, pathname: string): Array<ResolvedSidebarEntry>` with identical programme-specific inclusion rules.
- [ ] **Step 1: Replace the broad sidebar-retention test with focused failing tests**
Update `tests/programme-navigation.test.mjs` so programme and global behavior are asserted independently:
```js
test('programme routes retain only the selected curriculum and shared destinations', () => {
const computerScience = JSON.stringify(sidebarForPathname('/computer-science/year-1/'));
assert.match(computerScience, /"label":"Home"/);
assert.match(computerScience, /previous-exams-and-documents/);
assert.match(computerScience, /"label":"Computer Science"/);
assert.match(computerScience, /"label":"Useful Information"/);
assert.doesNotMatch(computerScience, /"label":"Data Science & AI"/);
assert.doesNotMatch(computerScience, /"label":"About Incognito"/);
assert.doesNotMatch(computerScience, /"label":"Master AI"/);
assert.doesNotMatch(computerScience, /"label":"Master DSDM"/);
const dataScience = JSON.stringify(sidebarForPathname('/data-science-and-ai/year-2/'));
assert.match(dataScience, /"label":"Home"/);
assert.match(dataScience, /previous-exams-and-documents/);
assert.match(dataScience, /"label":"Data Science & AI"/);
assert.match(dataScience, /"label":"Useful Information"/);
assert.doesNotMatch(dataScience, /"label":"Computer Science"/);
assert.doesNotMatch(dataScience, /"label":"About Incognito"/);
assert.doesNotMatch(dataScience, /"label":"Master AI"/);
assert.doesNotMatch(dataScience, /"label":"Master DSDM"/);
});
test('global routes retain the existing global navigation', () => {
const global = JSON.stringify(sidebarForPathname('/useful-information/'));
assert.match(global, /"label":"About Incognito"/);
assert.match(global, /"label":"Master AI"/);
assert.match(global, /"label":"Master DSDM"/);
assert.match(global, /"label":"Bachelor programmes"/);
});
test('resolved programme sidebars use the same focused groups', () => {
const entries = [
{ label: 'Home' },
{ label: 'About Incognito' },
{ label: 'Previous exams and documents' },
{ label: 'Computer Science' },
{ label: 'Data Science & AI' },
{ label: 'Master AI' },
{ label: 'Master DSDM' },
{ label: 'Useful Information' },
];
assert.deepEqual(
filterResolvedSidebar(entries, '/computer-science/').map(({ label }) => label),
['Home', 'Previous exams and documents', 'Computer Science', 'Useful Information'],
);
assert.deepEqual(
filterResolvedSidebar(entries, '/data-science-and-ai/').map(({ label }) => label),
['Home', 'Previous exams and documents', 'Data Science & AI', 'Useful Information'],
);
assert.deepEqual(filterResolvedSidebar(entries, '/useful-information/'), entries);
});
```
Also add `filterResolvedSidebar` to the named import at the top of the test file.
- [ ] **Step 2: Run the navigation tests and verify the new assertions fail**
Run:
```bash
node --test tests/programme-navigation.test.mjs
```
Expected: FAIL because programme routes still include About Incognito, Master AI, and Master DSDM, and the resolved filter preserves those groups.
- [ ] **Step 3: Implement one shared inclusion policy in the navigation helpers**
In `src/config/programme-navigation.mjs`, define the resolved labels shared across programme views and update both exported functions:
```js
const programmeSharedLabels = new Set(['Home', 'Previous exams and documents', 'Useful Information']);
export function sidebarForPathname(pathname) {
const programme = programmeForPathname(pathname);
const dataScience = sidebar.find(({ label }) => label === 'Data Science & AI');
if (programme) {
const undergraduate = programme === 'computer-science' ? computerScience : dataScience;
const home = sidebar.find(({ label }) => label === 'Home');
const previousExams = sidebar.find(({ slug }) => slug === 'previous-exams-and-documents');
const usefulInformation = sidebar.find(({ label }) => label === 'Useful Information');
return [home, previousExams, undergraduate, usefulInformation];
}
const withoutProgrammes = sidebar.filter(({ label }) => label !== 'Computer Science' && label !== 'Data Science & AI');
return [...withoutProgrammes.slice(0, 3), compact, ...withoutProgrammes.slice(3)];
}
export function filterResolvedSidebar(entries, pathname) {
const programme = programmeForPathname(pathname);
if (programme) {
const activeLabel = programme === 'computer-science' ? 'Computer Science' : 'Data Science & AI';
return entries.filter(({ label }) => programmeSharedLabels.has(label) || label === activeLabel);
}
return entries.map((entry) => {
if ((entry.label === 'Computer Science' || entry.label === 'Data Science & AI') && 'entries' in entry) {
return { ...entry, entries: entry.entries.slice(0, 1) };
}
return entry;
});
}
```
Keep the helper ordering exactly as Home, Previous exams and documents, active curriculum, and Useful Information. Do not modify `shared`, `programmeForPathname`, or `programmeSwitchTargets`.
- [ ] **Step 4: Run the focused and related tests**
Run:
```bash
node --test tests/programme-navigation.test.mjs tests/programme-switch.test.mjs tests/programme-redirects.test.mjs
```
Expected: all tests PASS, including the unchanged paired-course assertion.
- [ ] **Step 5: Commit the focused navigation behavior**
```bash
git add tests/programme-navigation.test.mjs src/config/programme-navigation.mjs
git commit -m "feat: focus programme sidebar navigation"
```
---
### Task 2: Render and style the compact accessible programme switch
**Files:**
- Modify: `tests/programme-switch.test.mjs`
- Modify: `src/components/ProgrammeSwitch.astro:9-14`
- Modify: `src/styles/incognito.css:136-178`
**Interfaces:**
- Consumes: `programmeForPathname(pathname)` and `programmeSwitchTargets(pathname)` from `src/config/programme-navigation.mjs`.
- Produces: a `<nav aria-label="Bachelor programme">` with visible `DSAI` and `CS` links, full `aria-label` and `title` values, and `aria-current="page"` on the active programme.
- [ ] **Step 1: Strengthen the component contract with failing markup tests**
Extend `tests/programme-switch.test.mjs` with exact compact-label and accessible-name assertions:
```js
test('programme switch uses compact visible labels with full accessible names', async () => {
const component = await readFile('src/components/ProgrammeSwitch.astro', 'utf8');
assert.match(component, />DSAI<\/a>/);
assert.match(component, />CS<\/a>/);
assert.match(component, /aria-label="Data Science & AI"/);
assert.match(component, /aria-label="Computer Science"/);
assert.match(component, /title="Data Science & AI"/);
assert.match(component, /title="Computer Science"/);
assert.match(component, />Bachelor programme<\/span>/);
assert.doesNotMatch(component, />Course information<\/span>/);
});
```
Retain the existing no-JavaScript, `aria-current`, navigation-label, and default Starlight sidebar assertions.
- [ ] **Step 2: Run the component test and verify it fails**
Run:
```bash
node --test tests/programme-switch.test.mjs
```
Expected: FAIL because the component still exposes long visible labels and the `Course information` caption.
- [ ] **Step 3: Replace the wide link copy with compact accessible links**
Update the rendered markup in `src/components/ProgrammeSwitch.astro`:
```astro
<nav class="programme-switch" aria-label="Bachelor programme">
<span class="programme-switch__label">Bachelor programme</span>
<div class="programme-switch__links">
<a
href={withBase(targets.dataScience)}
aria-label="Data Science & AI"
title="Data Science & AI"
aria-current={active === 'data-science-and-ai' ? 'page' : undefined}
>DSAI</a>
<a
href={withBase(targets.computerScience)}
aria-label="Computer Science"
title="Computer Science"
aria-current={active === 'computer-science' ? 'page' : undefined}
>CS</a>
</div>
</nav>
```
Do not change how `active`, `targets`, or deployment-base-aware URLs are calculated.
- [ ] **Step 4: Restyle the existing component as a compact segmented control**
Replace the programme-switch rules in `src/styles/incognito.css` with compact styles based on the existing theme variables:
```css
.programme-switch {
padding: 0.625rem;
margin: 0 0.5rem 0.75rem;
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.6rem;
}
.programme-switch__label {
display: block;
margin-bottom: 0.4rem;
color: var(--sl-color-gray-2);
font-size: var(--sl-text-xs);
font-weight: 600;
}
.programme-switch__links {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.2rem;
padding: 0.2rem;
border-radius: 0.45rem;
background: var(--sl-color-gray-6);
}
.programme-switch__links a {
display: flex;
min-height: 2.25rem;
align-items: center;
justify-content: center;
padding: 0.3rem 0.5rem;
border-radius: 0.35rem;
color: var(--sl-color-gray-2);
font-size: var(--sl-text-xs);
font-weight: 600;
line-height: 1;
text-decoration: none;
white-space: nowrap;
}
.programme-switch__links a[aria-current='page'] {
color: var(--incognito-light);
background: var(--incognito-primary);
font-weight: 700;
box-shadow: var(--sl-shadow-sm);
}
.programme-switch__links a:focus-visible {
outline: 2px solid var(--sl-color-accent-high);
outline-offset: 2px;
}
```
These variables already exist in `src/styles/incognito.css` and Starlight's public theme tokens. Keep the visible focus outline independent from the active fill.
- [ ] **Step 5: Run the component and navigation tests**
Run:
```bash
node --test tests/programme-switch.test.mjs tests/programme-navigation.test.mjs tests/base-aware-markdown.test.mjs
```
Expected: all tests PASS.
- [ ] **Step 6: Build and run the complete verification suite**
Run:
```bash
npm run verify
```
Expected: Astro checks, Node tests, content audit, production build, rendered-output checks, and internal-link checks all exit successfully.
- [ ] **Step 7: Check both responsive selector states manually**
Run:
```bash
npm run dev
```
Inspect a Computer Science route and a Data Science & AI route at desktop width and at a 285-pixel sidebar/mobile-navigation width. Confirm the short labels never wrap, only the selected curriculum and three shared destinations are present, the filled active state has sufficient contrast in light and dark themes, and keyboard focus is visible on both links. Stop the development server after the review.
- [ ] **Step 8: Commit the compact selector**
```bash
git add tests/programme-switch.test.mjs src/components/ProgrammeSwitch.astro src/styles/incognito.css
git commit -m "feat: compact programme selector"
```

View file

@ -0,0 +1,72 @@
# Useful Guides Publication Design
## Goal
Publish the practical Useful Guides retained in the 2026-08-02 `wiki.msvincognito.nl` snapshot as first-class pages in the current Starlight wiki. Publish all four guide topics without adding historical-warning callouts, and include the newer laptop recommendations that are absent from the current migrated laptop page.
## Scope
The published Useful Information section will contain:
- Laptop Buying Advice
- Housing Guide
- Linux Tricks
- Surviving DACS
The existing Laptop Buying Advice destination will be updated in place. The other three guides will be added under `src/content/docs/useful-information/` and exposed through the Useful Information landing page and manual sidebar.
Course content, the original DokuWiki migration, and unrelated Useful Information pages are outside this change.
## Sources and destinations
| Archived source | Published destination |
| --- | --- |
| `to-be-studied/live-wiki/2026-08-02/useful-guides/laptop-buying-advice.md` | `src/content/docs/useful-information/laptop-buying-advice.md` |
| `to-be-studied/live-wiki/2026-08-02/useful-guides/housing-guide.md` | `src/content/docs/useful-information/housing-guide.md` |
| `to-be-studied/live-wiki/2026-08-02/useful-guides/linux-tricks.md` | `src/content/docs/useful-information/linux-tricks.md` |
| `to-be-studied/live-wiki/2026-08-02/useful-guides/survivingdacs.md` | `src/content/docs/useful-information/surviving-dacs.md` |
Each newly published destination will be declared in `docs/supplemental-content.json` with its original public source URL and a `useful-guide-recovery` category. The laptop page remains covered by the original migration manifest because its destination already exists there.
## Content treatment
Use a faithful, maintainable migration rather than a verbatim scrape or a full rewrite:
- Preserve the guides' practical substance, contributor voice, headings, lists, and useful external references.
- Convert captured HTML remnants and snapshot-specific formatting to ordinary Starlight Markdown.
- Correct obvious spelling, grammar, and formatting defects without changing meaning.
- Do not add historical-information or dated-warning callouts.
- Retain source-provided dates only where they identify authorship or the guide's own revision context; do not turn them into warnings.
- Remove dead links, instructions that expose obsolete credential-handling practices, and recommendations that create avoidable legal or safety risk. Rewrite the surrounding sentence when needed so the page remains coherent.
- Do not invent current facts or silently claim that time-sensitive prices, services, housing practices, or university systems have been reverified.
The Laptop Buying Advice page will lead with the newer source recommendations:
- at least 16 GB RAM;
- at least 512 GB SSD storage;
- a mid-range Intel Core i5-class or equivalent processor;
- M-series Mac compatibility, with possible course-specific compatibility considerations;
- an Nvidia GPU recommendation for Data Science and AI workloads that use CUDA.
The older price-tier material may remain as general budget context, but language that presents old configurations or prices as current purchasing targets will be revised or removed.
## Navigation and discovery
Update `src/content/docs/useful-information/index.md` to introduce the practical guides and link all four of them alongside the existing location and IT pages. Add the three new routes to the Useful Information group in `src/config/sidebar.mjs`, keeping related guides adjacent.
No redirects or new top-level navigation group are needed. Existing `/useful-information/` routes remain unchanged.
## Validation
Automated coverage will verify:
- all three new destination files exist and are registered as supplemental content;
- the Useful Information landing page links all four practical guides;
- the sidebar exposes the three new routes;
- the laptop page contains the newer minimum RAM and storage recommendations, M-series guidance, and Nvidia/CUDA guidance;
- the content audit accepts the additional supplemental destinations;
- the full project verification succeeds, including Astro checks, tests, build, content audit, and internal-link validation.
## Success criteria
Visitors can discover and read all four practical guides from the Useful Information section of the new wiki. The laptop guide contains the newer recommendations from the GitHub-era wiki. The pages render as native Starlight content, contain no added historical-warning callouts, and pass the repository's complete verification workflow.

View file

@ -0,0 +1,82 @@
# GDPR-Gated Matomo Tracking Design
## Goal
Add Matomo analytics to the Astro Starlight wiki without sending analytics requests or loading the remote Matomo client before a visitor explicitly consents. Visitors must be able to decline without losing site functionality and withdraw consent later.
This implementation supports privacy compliance but does not, by itself, guarantee legal compliance. The Matomo server configuration and the association's privacy documentation must match the behavior described here.
## Chosen approach
Use Starlight's global `head` configuration to load a small local consent controller on every page. The controller owns the consent interface and loads `https://analytics.msvincognito.nl/matomo.js` only after affirmative consent.
This follows Starlight's supported analytics integration point without overriding framework components. It also avoids making a request to the analytics host before the visitor chooses to participate.
## Visitor experience
- On a first visit, show an accessible consent banner that briefly explains first-party, cookieless analytics and links to `https://msvincognito.nl/privacy-policy`.
- Show equally visible **Accept analytics** and **Decline** actions. The wiki remains fully usable after either choice.
- Remember the choice locally so the banner does not reappear on each page.
- Keep a visible **Privacy settings** control available after a choice. It reopens the banner so consent can be changed or withdrawn.
- If browser storage is unavailable, fail privately: do not track automatically, and ask again on a later page load.
## Tracking behavior
The local controller uses a versioned, wiki-specific storage key with three states: no decision, accepted, or declined.
When the state is accepted, initialize `window._paq` and queue Matomo commands in privacy-first order:
1. require tracking consent as defense in depth;
2. disable analytics cookies;
3. grant consent for the current page load based on the stored choice;
4. set the HTTPS tracker URL to `https://analytics.msvincognito.nl/matomo.php`;
5. set site ID `1`;
6. track the page view and enable link tracking;
7. asynchronously load `https://analytics.msvincognito.nl/matomo.js` once.
When the state is declined, do not initialize Matomo or load any resource from the analytics origin.
When an accepted choice is withdrawn, queue Matomo's consent-revocation command if the tracker has already loaded, remove any Matomo cookies defensively, persist the declined state, and stop future tracking. The current page is not reloaded.
The current Starlight site uses normal document navigation, not Astro's `ClientRouter`, so each page load initializes at most one page view. The controller must still guard against duplicate initialization.
## Project structure
- Add one focused client script for consent state, UI behavior, and conditional Matomo loading.
- Register that script globally through `astro.config.mjs`, using the configured Astro base path so subpath deployments continue to work.
- Add the banner and settings-control styles to the existing `src/styles/incognito.css` file.
- Add focused Node tests alongside the existing tests. Tests should exercise observable behavior with a lightweight DOM rather than duplicate the implementation.
No third-party consent-management dependency is added.
## Accessibility and privacy requirements
- Use a labelled dialog/banner region with keyboard-operable native buttons.
- Move focus into the banner when privacy settings are reopened and restore a sensible focus target after a decision.
- Do not preselect consent, treat inactivity as consent, or make acceptance visually easier than refusal.
- Use clear language that identifies MSV Incognito, the analytics purpose, the cookieless configuration, and the withdrawal route.
- Do not record the consent choice in Matomo.
## Verification
Automated tests must verify that:
- no Matomo script or queue is created before consent;
- acceptance persists the choice and loads the expected HTTPS Matomo endpoint once;
- decline persists the choice without loading Matomo;
- withdrawal changes the stored choice, invokes revocation when possible, and prevents later initialization;
- the global Starlight configuration includes the local consent controller with base-path support;
- a production build contains the controller and consent UI styling.
Run the project's full `npm run verify` command after the focused red/green test cycle.
## Required operational follow-up
Before deployment, the Matomo administrator should confirm server-side IP anonymization, appropriate log and analytics retention periods, restricted administrator access, and that data is not repurposed for advertising or cross-site profiling. The privacy page must describe the analytics purpose, data categories, retention, controller identity, consent withdrawal, and data-subject rights.
## References
- [Starlight global head configuration](https://starlight.astro.build/reference/configuration/#head)
- [Matomo tracking consent API](https://developer.matomo.org/guides/tracking-consent)
- [Matomo JavaScript tracking API](https://developer.matomo.org/guides/tracking-javascript)
- [Dutch DPA cookie-banner guidance](https://autoriteitpersoonsgegevens.nl/actueel/ap-pakt-misleidende-cookiebanners-aan)

View file

@ -0,0 +1,160 @@
# Programme Sidebar and Course Integration Design
## Goal
Publish the substantive Computer Science course material from `origin/isaacs-changes` without merging that branch's broken file moves, empty placeholders, malformed description, content duplication, or archive deletion. Reorganize bachelor navigation so readers can switch between Computer Science and Data Science & AI while global wiki resources remain available.
## Source and scope
The source branch is `origin/isaacs-changes` at commit `d5d6730`. It diverged from `main` at `c590f77` and must not be merged or cherry-picked wholesale.
Publish these five substantive Computer Science pages from the branch after normalizing them to current repository conventions:
- Introduction to Computer Science
- Computer Architecture
- Algorithmic Design
- Databases
- Statistics
Also expose these seven courses in both bachelor programmes:
- Discrete Mathematics
- Procedural Programming
- Calculus
- Logic
- Objects in Programming
- Data Structures and Algorithms
- Linear Algebra
The branch's copies of those seven pages are byte-identical. Their common body content therefore has one source, while each programme receives its own public route and frontmatter wrapper.
Do not publish:
- `computer-science/course-description.md`, which lacks frontmatter and contains duplicate `#Hello` headings;
- any of the 30 Computer Science Year 2 and Year 3 `Empty Page` placeholders;
- duplicate full copies of identical shared-course bodies; or
- the branch deletion of `previous-exams-and-documents.md`.
No other `origin/isaacs-changes` change is in scope.
## Public route model
Use programme-specific routes:
- `/computer-science/`
- `/computer-science/year-1/`
- `/computer-science/year-1/period-N/<course>/`
- `/data-science-and-ai/`
- `/data-science-and-ai/year-N/`
- `/data-science-and-ai/year-N/block-N/<course>/`
The period/block naming follows each source programme's existing terminology. Do not rewrite course URLs to imply the two curricula have identical structures.
Move the current DSAI pages from `/bachelor/...` to `/data-science-and-ai/...`. Preserve every previous `/bachelor/...` URL with a permanent redirect to its exact DSAI replacement, including overview, year, project, honours, thesis, study-abroad, and course routes. Internal wiki links and the sidebar should use the new canonical URLs after migration.
The five newly recovered Computer Science pages live only under Computer Science routes. Empty later-year Computer Science routes are not created.
## Shared-course content model
Store the common body of each of the seven identical courses as a non-published MDX partial outside the `docs` collection. Create a thin MDX page at each programme-specific route with:
- programme-appropriate title and description frontmatter;
- the standard historical-information caution;
- an import of the shared body partial; and
- an optional programme-specific notes section when verified differences exist.
The shared partial contains no page-level frontmatter, H1 heading, or historical caution. This prevents duplicate rendered titles and notices.
If a shared course later diverges, keep both public routes stable. Replace or extend only that programme wrapper; the other programme continues using the shared partial. Do not encode programme identity in the shared source.
## Programme overview pages
Create a clean Computer Science overview and Year 1 overview using only verified descriptions and links. Do not reuse or repair the malformed branch course-description text by inference.
Retitle and relocate the existing bachelor overview pages as Data Science & AI. Preserve their historical warning and current external Maastricht University guidance.
## Sidebar interaction
Add a small route-aware programme switch above the course-navigation portion of Starlight's global sidebar:
- **Computer Science**
- **Data Science & AI**
The switch uses ordinary links, works without client JavaScript, is keyboard accessible, and marks the active programme with `aria-current`. It renders inside the existing Starlight sidebar on desktop and in the existing mobile navigation.
Use Starlight's supported `Sidebar` component override to prepend the switch while reusing the default `Sidebar` component. Use Starlight route-data middleware to filter the detailed bachelor course tree to the active programme.
Programme selection is derived from the current route and is never stored in cookies or local storage:
- Computer Science routes activate Computer Science navigation.
- Data Science & AI routes and redirected legacy bachelor routes activate DSAI navigation.
- Global pages show neither option as active and show compact links to both programme overviews instead of a detailed bachelor tree.
When a reader uses the switch from one of the seven shared-course routes, link to the paired route in the other programme. From a programme-specific course without an equivalent, link to the other programme's Year 1 overview. From other programme pages, link to the other programme overview.
The rest of the global navigation remains available in both programme contexts:
- Home
- About Incognito
- Previous exams and documents
- Master AI
- Master DSDM
- Useful Information and all its current child pages
The programme switch changes only the detailed bachelor-course portion. It must not hide the master programmes or Useful Information.
## Navigation data boundaries
Keep navigation definitions as focused data structures:
- one Computer Science course tree;
- one Data Science & AI course tree;
- one global navigation section;
- one explicit map of paired shared-course routes; and
- one pure function that selects the active programme and switch destinations from a route ID.
The route middleware consumes those structures and assigns the final Starlight sidebar. The component override only renders the switch and delegates the link tree to Starlight's default component; it does not duplicate sidebar filtering logic.
## Migration records and provenance
Update the migration manifest, migration report, supplemental-content registry, and sidebar tests in lockstep with the route moves and new pages.
- Existing DSAI content retains its existing source provenance while its destination changes.
- The five new Computer Science pages record their `wiki.msvincognito.nl` source URLs and `2026-08-02` capture provenance.
- Shared programme wrappers record which source page supplied the common body.
- Redirects are documented as compatibility routes, not duplicate published content.
The audit must continue to reject undeclared published Markdown/MDX files and any reintroduction of the excluded placeholder or malformed pages.
## Accessibility and responsive behavior
- Use a labelled navigation region for the programme switch.
- Expose the current programme with both visual styling and `aria-current="page"`.
- Keep touch targets at least as large as existing Starlight sidebar links.
- Preserve Starlight's default sidebar persistence, focus behavior, and mobile menu footer by wrapping rather than copying the default component.
- Ensure long programme labels wrap without horizontal scrolling at narrow widths.
## Testing and verification
Add focused automated coverage for:
- exact inclusion of the five substantive Computer Science pages;
- exclusion of the malformed description and all empty placeholders;
- one shared source body for each identical course and two programme-specific public routes;
- programme-specific content overrides remaining possible;
- complete old `/bachelor/...` to new DSAI redirect coverage;
- route-to-programme detection and paired-course switch destinations;
- route middleware retaining global navigation and showing only the active detailed bachelor tree;
- accessible programme switch markup and active state;
- sidebar visibility on Computer Science, DSAI, master, and Useful Information pages;
- updated manifest, report, and supplemental registry consistency; and
- absence of duplicate H1 headings, broken internal links, and unpublished-source leakage.
Run focused red/green tests first, then the full `npm run verify` workflow. Build once at `/` and once with a non-root `BASE` to cover canonical links, redirects, the sidebar override, and mobile-compatible output.
## References
- [Starlight Sidebar override](https://starlight.astro.build/reference/overrides/#sidebar)
- [Starlight component overrides](https://starlight.astro.build/guides/overriding-components/)
- [Starlight route-data middleware](https://starlight.astro.build/guides/route-data/#customizing-route-data)
- [Starlight sidebar configuration](https://starlight.astro.build/guides/sidebar/)

View file

@ -0,0 +1,93 @@
# Compact Programme Switch and Sidebar Filtering Design
## Goal
Replace the current two-column bachelor programme selector with a compact segmented control. Selecting a programme must also focus the sidebar on that programme's curriculum and a small set of shared destinations.
## Approved interaction
The switch appears above the Starlight navigation tree and contains two short labels:
- `DSAI` for Data Science & AI;
- `CS` for Computer Science.
Both options remain ordinary links. The current programme is shown with a filled accent state and `aria-current="page"`. Switching programmes navigates to the existing route-aware destination: a paired course where one exists, otherwise the other programme's overview.
The control is navigation, not a client-side preference. Programme state continues to come from the current URL; it is not stored in cookies, local storage, or client JavaScript.
## Visual design
Use a small bordered container labelled `Bachelor programme`. Inside it, place `DSAI` and `CS` in an equal-width, two-segment row.
The inactive segment uses the sidebar's subdued text and background colors. The active segment uses a filled blue accent with high-contrast text. Do not repeat the active programme's full name above the segments because the selected state and curriculum heading already communicate it.
The control must fit the narrow mobile sidebar without either short label wrapping. It should use the existing Starlight and Incognito color variables so light and dark themes remain consistent.
## Accessible naming and keyboard behavior
Visible labels stay short, while each link exposes its full programme name to assistive technology. A tooltip may also expose the full name to pointer users, but it must not be the only source of the accessible name.
The current link retains `aria-current="page"`. Both links remain reachable and operable by keyboard without JavaScript. Keyboard focus receives a visible outline that is distinguishable from the selected state.
## Programme-specific sidebar contents
On a Computer Science route, render:
1. Home;
2. Previous exams and documents;
3. the complete Computer Science course tree;
4. Useful Information and its existing child pages.
On a Data Science & AI route, render:
1. Home;
2. Previous exams and documents;
3. the complete Data Science & AI course tree;
4. Useful Information and its existing child pages.
Hide the inactive bachelor programme, About Incognito, Master AI, and Master DSDM in both programme-specific views.
Global pages that are not associated with either bachelor programme retain the existing global sidebar. This keeps non-programme navigation usable and avoids arbitrarily choosing a programme when the URL supplies no programme context.
## Implementation boundaries
`ProgrammeSwitch.astro` owns only the accessible switch markup and presentation hooks. It consumes the current programme and switch destinations from the existing pure navigation helpers.
The route-aware sidebar configuration remains responsible for selecting and ordering navigation entries. It must produce focused programme navigation without duplicating course definitions or switch-destination logic in the component.
The Starlight `Sidebar` override continues to compose the programme switch with Starlight's default sidebar component. No client-side component or hydration directive is introduced.
## Data flow
1. The request pathname identifies the active programme.
2. Existing navigation helpers calculate the two switch destinations.
3. The switch renders ordinary links and marks the active programme.
4. Middleware derives the resolved Starlight sidebar for the same pathname.
5. Programme routes receive the selected curriculum plus the three approved shared destinations; global routes retain the global navigation.
Unknown or malformed paths must fall back to global navigation rather than showing an incorrect programme as selected.
## Verification
Automated tests should verify:
- the visible switch labels are `DSAI` and `CS`;
- each link has a full accessible programme name;
- the active link receives `aria-current="page"`;
- the component contains no script or client hydration directive;
- Computer Science routes include only the Computer Science course tree and the three shared destinations;
- Data Science & AI routes include only the Data Science & AI course tree and the three shared destinations;
- programme routes exclude About Incognito, Master AI, Master DSDM, and the inactive bachelor programme;
- global routes retain the global navigation;
- paired-course and fallback switch destinations continue to pass their existing route tests;
- a production build renders the selector without horizontal overflow at narrow sidebar widths.
Manual review should check both programme states in desktop and mobile navigation, in light and dark themes, with keyboard focus visible.
## Out of scope
- Changing course content or curriculum structure;
- changing the paired-course mapping;
- storing programme selection as a user preference;
- adding JavaScript to animate or hydrate the switch;
- redesigning the rest of Starlight's navigation tree.

View file

@ -1,191 +1,226 @@
[ [
{ {
"destination": "src/content/docs/bachelor/year-1/block-1/discrete-mathematics.md", "destination": "src/content/docs/computer-science/index.md",
"source": "origin/isaacs-changes@d5d6730 (curated overview)",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/index.md",
"source": "origin/isaacs-changes@d5d6730 (curated overview)",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-1/introduction-to-computer-science.md",
"source": "origin/isaacs-changes@d5d6730",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-4/computer-architecture.md",
"source": "origin/isaacs-changes@d5d6730",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-5/algorithmic-design.md",
"source": "origin/isaacs-changes@d5d6730",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-5/databases.md",
"source": "origin/isaacs-changes@d5d6730",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-5/statistics.md",
"source": "origin/isaacs-changes@d5d6730",
"category": "computer-science-recovery"
},
{
"destination": "src/content/docs/data-science-and-ai/year-1/block-1/discrete-mathematics.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/discrete_mathematics", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/discrete_mathematics",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-1/introduction-to-data-science-and-artifical-intelligence.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/introduction_to_data_science_and_artifical_intelligence",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-1/procedural-programming.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-1/procedural-programming.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/procedural_programming", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_1/procedural_programming",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-2/calculus.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-2/calculus.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/calculus", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/calculus",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-2/logic.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-2/logic.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/logic", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/logic",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-2/objects-in-programming.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-2/objects-in-programming.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/objects_in_programming", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_2/objects_in_programming",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-4/data-structures-and-algorithms.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-4/data-structures-and-algorithms.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/data_structures_and_algorithms", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/data_structures_and_algorithms",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-4/linear-algebra.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-4/linear-algebra.mdx",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/linear_algebra", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/linear_algebra",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-4/principles-of-data-science.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-4/principles-of-data-science.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/principles_of_data_science", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_4/principles_of_data_science",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-5/computational-and-cognitive-neuroscience.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-5/computational-and-cognitive-neuroscience.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/computational_and_cognitive_neuroscience",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-5/numerical-methods.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-5/numerical-methods.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/numerical_methods", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/numerical_methods",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-1/block-5/software-engineering.md", "destination": "src/content/docs/data-science-and-ai/year-1/block-5/software-engineering.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/software_engineering", "source": "https://msvincognito.nl/wiki/study/bachelor/year_1/block_5/software_engineering",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-1/databases.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-1/databases.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/databases", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/databases",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-1/graph-theory.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-1/graph-theory.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/graph_theory", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/graph_theory",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-1/probability-and-statistics.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-1/probability-and-statistics.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/probability_and_statistics", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_1/probability_and_statistics",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-2/machine-learning.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-2/machine-learning.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/machine_learning", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/machine_learning",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-2/reasoning-techniques.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-2/reasoning-techniques.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/reasoning_techniques", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/reasoning_techniques",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-2/simulation-and-statisical-analysis.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-2/simulation-and-statisical-analysis.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/simulation_and_statisical_analysis", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_2/simulation_and_statisical_analysis",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-4/human-computer-interaction-and-affective-computing.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-4/human-computer-interaction-and-affective-computing.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/human_computer_interaction_and_affective_computing",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-4/mathematical-modelling.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-4/mathematical-modelling.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/mathematical_modelling", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/mathematical_modelling",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-4/natural-language-processing.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-4/natural-language-processing.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/natural_language_processing", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_4/natural_language_processing",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-5/game-theory.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-5/game-theory.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/game_theory", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/game_theory",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-5/introduction-to-image-and-video-processing.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-5/introduction-to-image-and-video-processing.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/introduction_to_image_and_video_processing",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-5/linear-programming.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-5/linear-programming.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/linear_programming", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/linear_programming",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-2/block-5/philosophy-and-artificial-intelligence.md", "destination": "src/content/docs/data-science-and-ai/year-2/block-5/philosophy-and-artificial-intelligence.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence", "source": "https://msvincognito.nl/wiki/study/bachelor/year_2/block_5/philosophy_and_artificial_intelligence",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-1/prolog.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-1/prolog.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/prolog", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/prolog",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-1/robotics-and-embedded-systems.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-1/robotics-and-embedded-systems.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/robotics_and_embedded_systems", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/robotics_and_embedded_systems",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-1/semantic-web.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-1/semantic-web.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/semantic_web", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/semantic_web",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-1/software-and-systems-verification.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-1/software-and-systems-verification.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/software_and_systems_verification", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_1/software_and_systems_verification",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/introduction-to-bio-informatics.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/introduction-to-bio-informatics.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/introduction_to_bio-informatics", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/introduction_to_bio-informatics",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/logic-for-artificial-intelligence.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/logic-for-artificial-intelligence.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/logic_for_artificial_intelligence", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/logic_for_artificial_intelligence",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/parallel-programming.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/parallel-programming.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/parallel_programming", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/parallel_programming",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/quantum-computation.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/quantum-computation.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/quantum_computation", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/quantum_computation",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/recommender-systems.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/recommender-systems.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/recommender_systems", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/recommender_systems",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-2/secure-web-applications.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-2/secure-web-applications.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/secure_web_applications", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_2/secure_web_applications",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-4/data-analysis.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-4/data-analysis.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/data_analysis", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/data_analysis",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-4/intelligent-systems.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-4/intelligent-systems.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/intelligent_systems", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/intelligent_systems",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
{ {
"destination": "src/content/docs/bachelor/year-3/block-4/operations-research-case-studies.md", "destination": "src/content/docs/data-science-and-ai/year-3/block-4/operations-research-case-studies.md",
"source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/operations_research_case_studies", "source": "https://msvincognito.nl/wiki/study/bachelor/year_3/block_4/operations_research_case_studies",
"category": "live-course-recovery" "category": "live-course-recovery"
}, },
@ -273,5 +308,55 @@
"destination": "src/content/docs/previous-exams-and-documents.md", "destination": "src/content/docs/previous-exams-and-documents.md",
"source": "docs/pdf-inventory.tsv", "source": "docs/pdf-inventory.tsv",
"category": "pdf-archive" "category": "pdf-archive"
},
{
"destination": "src/content/docs/useful-information/housing-guide.md",
"source": "https://wiki.msvincognito.nl/useful-guides/housing-guide",
"category": "useful-guide-recovery"
},
{
"destination": "src/content/docs/useful-information/linux-tricks.md",
"source": "https://wiki.msvincognito.nl/useful-guides/linux-tricks",
"category": "useful-guide-recovery"
},
{
"destination": "src/content/docs/useful-information/surviving-dacs.md",
"source": "https://wiki.msvincognito.nl/useful-guides/survivingdacs",
"category": "useful-guide-recovery"
},
{
"destination": "src/content/docs/computer-science/year-1/period-1/discrete-mathematics.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-1/procedural-programming.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-2/calculus.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-2/logic.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-2/objects-in-programming.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-4/data-structures-and-algorithms.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
},
{
"destination": "src/content/docs/computer-science/year-1/period-4/linear-algebra.mdx",
"source": "origin/isaacs-changes@d5d6730",
"category": "shared-course-wrapper"
} }
] ]

1
package-lock.json generated
View file

@ -8,6 +8,7 @@
"name": "incognito-wiki", "name": "incognito-wiki",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@astrojs/markdown-remark": "7.2.2",
"@astrojs/starlight": "0.41.6", "@astrojs/starlight": "0.41.6",
"astro": "7.1.6", "astro": "7.1.6",
"sharp": "0.35.3" "sharp": "0.35.3"

View file

@ -3,7 +3,9 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",
"engines": { "node": ">=22.12.0" }, "engines": {
"node": ">=22.12.0"
},
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"build": "astro build", "build": "astro build",
@ -18,6 +20,7 @@
"verify": "npm run check && npm run audit:content && npm run build && npm run check:rendered && npm run check:links" "verify": "npm run check && npm run audit:content && npm run build && npm run check:rendered && npm run check:links"
}, },
"dependencies": { "dependencies": {
"@astrojs/markdown-remark": "7.2.2",
"@astrojs/starlight": "0.41.6", "@astrojs/starlight": "0.41.6",
"astro": "7.1.6", "astro": "7.1.6",
"sharp": "0.35.3" "sharp": "0.35.3"

135
public/matomo-consent.js Normal file
View file

@ -0,0 +1,135 @@
(function initializeAnalyticsConsent(window, document) {
'use strict';
var storageKey = 'incognito.analytics-consent.v1';
var accepted = 'accepted';
var declined = 'declined';
var trackerOrigin = 'https://analytics.msvincognito.nl/';
if (window.__incognitoMatomoConsentController) return;
window.__incognitoMatomoConsentController = true;
function readDecision() {
try {
var value = window.localStorage.getItem(storageKey);
return value === accepted || value === declined ? value : null;
} catch (_error) {
return null;
}
}
function writeDecision(value) {
try {
window.localStorage.setItem(storageKey, value);
} catch (_error) {
// Consent applies to this page load only when browser storage is unavailable.
}
}
function initializeMatomo() {
var queue = window._paq = window._paq || [];
if (window.__incognitoMatomoInitialized) return;
window.__incognitoMatomoInitialized = true;
queue.push(['requireConsent']);
queue.push(['disableCookies']);
queue.push(['setConsentGiven']);
queue.push(['setTrackerUrl', trackerOrigin + 'matomo.php']);
queue.push(['setSiteId', '1']);
queue.push(['trackPageView']);
queue.push(['enableLinkTracking']);
var tracker = document.createElement('script');
tracker.id = 'incognito-matomo-script';
tracker.async = true;
tracker.src = trackerOrigin + 'matomo.js';
document.head.appendChild(tracker);
}
function createButton(id, label) {
var button = document.createElement('button');
button.id = id;
button.className = 'incognito-consent-action';
button.type = 'button';
button.textContent = label;
return button;
}
var banner = document.createElement('section');
banner.id = 'incognito-analytics-consent';
banner.setAttribute('role', 'dialog');
banner.setAttribute('aria-labelledby', 'incognito-analytics-consent-title');
banner.setAttribute('aria-describedby', 'incognito-analytics-consent-description');
banner.innerHTML =
'<div class="incognito-consent-copy">' +
'<h2 id="incognito-analytics-consent-title">Privacy-friendly analytics</h2>' +
'<p id="incognito-analytics-consent-description">MSV Incognito would like to use cookieless Matomo analytics to understand how the wiki is used. Nothing is sent to Matomo unless you accept. You can change your choice at any time.</p>' +
'<a href="https://msvincognito.nl/privacy-policy">Read our privacy policy</a>' +
'</div>';
var actions = document.createElement('div');
actions.className = 'incognito-consent-actions';
var acceptButton = createButton('incognito-analytics-accept', 'Accept analytics');
var declineButton = createButton('incognito-analytics-decline', 'Decline');
actions.appendChild(acceptButton);
actions.appendChild(declineButton);
banner.appendChild(actions);
var settingsButton = document.createElement('button');
settingsButton.id = 'incognito-privacy-settings';
settingsButton.type = 'button';
settingsButton.textContent = 'Privacy settings';
function showBanner(moveFocus) {
banner.hidden = false;
settingsButton.hidden = true;
if (moveFocus) acceptButton.focus();
}
function hideBanner(restoreFocus) {
banner.hidden = true;
settingsButton.hidden = false;
if (restoreFocus) settingsButton.focus();
}
acceptButton.addEventListener('click', function acceptAnalytics() {
if (banner.hidden) return;
writeDecision(accepted);
if (window.__incognitoMatomoInitialized) {
window._paq.push(['setConsentGiven']);
window._paq.push(['trackPageView']);
} else {
initializeMatomo();
}
hideBanner(true);
});
declineButton.addEventListener('click', function declineAnalytics() {
if (banner.hidden) return;
writeDecision(declined);
if (window._paq) {
window._paq.push(['forgetConsentGiven']);
window._paq.push(['deleteCookies']);
}
hideBanner(true);
});
settingsButton.addEventListener('click', function openPrivacySettings() {
showBanner(true);
});
document.body.appendChild(banner);
document.body.appendChild(settingsButton);
var decision = readDecision();
if (decision === accepted) {
banner.hidden = true;
settingsButton.hidden = false;
initializeMatomo();
} else if (decision === declined) {
banner.hidden = true;
settingsButton.hidden = false;
} else {
showBanner(false);
}
})(window, document);

View file

@ -10,8 +10,8 @@ const OUTDATED_NOTICE = 'This information originated in the previous wiki and ma
const PAGE_NOTICE = ':::note[Awaiting content]\nThis page is awaiting content.\n:::'; const PAGE_NOTICE = ':::note[Awaiting content]\nThis page is awaiting content.\n:::';
const SECTION_NOTICE = ':::note[Awaiting content]\nThis section is awaiting content.\n:::'; const SECTION_NOTICE = ':::note[Awaiting content]\nThis section is awaiting content.\n:::';
const EMPTY_PAGE_NOTICES = new Map([ const EMPTY_PAGE_NOTICES = new Map([
['bachelor/year-2/honours-programme.md', PAGE_NOTICE], ['data-science-and-ai/year-2/honours-programme.md', PAGE_NOTICE],
['bachelor/year-3/honours-programme.md', PAGE_NOTICE], ['data-science-and-ai/year-3/honours-programme.md', PAGE_NOTICE],
['useful-information/handy-locations.md', SECTION_NOTICE], ['useful-information/handy-locations.md', SECTION_NOTICE],
]); ]);

View file

@ -90,6 +90,7 @@ export async function checkInternalLinks({ distRoot, base }) {
const pageUrl = new URL(`${normalizedBase.replace(/\/$/, '')}${route}`, 'https://built.invalid'); const pageUrl = new URL(`${normalizedBase.replace(/\/$/, '')}${route}`, 'https://built.invalid');
const html = await readFile(sourcePath, 'utf8'); const html = await readFile(sourcePath, 'utf8');
const { document } = parseHTML(html); const { document } = parseHTML(html);
if (document.querySelector('meta[http-equiv="refresh"]')) continue;
for (const element of document.querySelectorAll('[href]')) { for (const element of document.querySelectorAll('[href]')) {
const href = element.getAttribute('href')?.trim() ?? ''; const href = element.getAttribute('href')?.trim() ?? '';

View file

@ -7,19 +7,19 @@ const CANONICAL_ENTRIES = new Map([
['pages/start.txt', ['src/content/docs/index.mdx', 'page']], ['pages/start.txt', ['src/content/docs/index.mdx', 'page']],
['pages/study.txt', ['src/content/docs/index.mdx', 'merge']], ['pages/study.txt', ['src/content/docs/index.mdx', 'merge']],
['pages/study/msv_incognito.txt', ['src/content/docs/about-incognito.md', 'page']], ['pages/study/msv_incognito.txt', ['src/content/docs/about-incognito.md', 'page']],
['pages/study/bachelor.txt', ['src/content/docs/bachelor/index.md', 'page']], ['pages/study/bachelor.txt', ['src/content/docs/data-science-and-ai/index.md', 'page']],
['pages/study/bachelor/year_1.txt', ['src/content/docs/bachelor/year-1/index.md', 'page']], ['pages/study/bachelor/year_1.txt', ['src/content/docs/data-science-and-ai/year-1/index.md', 'page']],
['pages/study/bachelor/year_1/project_1-1.txt', ['src/content/docs/bachelor/year-1/project-1-1.md', 'page']], ['pages/study/bachelor/year_1/project_1-1.txt', ['src/content/docs/data-science-and-ai/year-1/project-1-1.md', 'page']],
['pages/study/bachelor/year_1/project_1-2.txt', ['src/content/docs/bachelor/year-1/project-1-2.md', 'page']], ['pages/study/bachelor/year_1/project_1-2.txt', ['src/content/docs/data-science-and-ai/year-1/project-1-2.md', 'page']],
['pages/study/bachelor/year_2.txt', ['src/content/docs/bachelor/year-2/index.md', 'page']], ['pages/study/bachelor/year_2.txt', ['src/content/docs/data-science-and-ai/year-2/index.md', 'page']],
['pages/study/bachelor/year_2/honours_programme.txt', ['src/content/docs/bachelor/year-2/honours-programme.md', 'page']], ['pages/study/bachelor/year_2/honours_programme.txt', ['src/content/docs/data-science-and-ai/year-2/honours-programme.md', 'page']],
['pages/study/bachelor/year_2/project_2-1.txt', ['src/content/docs/bachelor/year-2/project-2-1.md', 'page']], ['pages/study/bachelor/year_2/project_2-1.txt', ['src/content/docs/data-science-and-ai/year-2/project-2-1.md', 'page']],
['pages/study/bachelor/year_2/project_2-2.txt', ['src/content/docs/bachelor/year-2/project-2-2.md', 'page']], ['pages/study/bachelor/year_2/project_2-2.txt', ['src/content/docs/data-science-and-ai/year-2/project-2-2.md', 'page']],
['pages/study/bachelor/year_3.txt', ['src/content/docs/bachelor/year-3/index.md', 'page']], ['pages/study/bachelor/year_3.txt', ['src/content/docs/data-science-and-ai/year-3/index.md', 'page']],
['pages/study/bachelor/year_3/bachelors_thesis.txt', ['src/content/docs/bachelor/year-3/bachelors-thesis.md', 'page']], ['pages/study/bachelor/year_3/bachelors_thesis.txt', ['src/content/docs/data-science-and-ai/year-3/bachelors-thesis.md', 'page']],
['pages/study/bachelor/year_3/honours_programme.txt', ['src/content/docs/bachelor/year-3/honours-programme.md', 'page']], ['pages/study/bachelor/year_3/honours_programme.txt', ['src/content/docs/data-science-and-ai/year-3/honours-programme.md', 'page']],
['pages/study/bachelor/year_3/project_3-1.txt', ['src/content/docs/bachelor/year-3/project-3-1.md', 'page']], ['pages/study/bachelor/year_3/project_3-1.txt', ['src/content/docs/data-science-and-ai/year-3/project-3-1.md', 'page']],
['pages/study/bachelor/year_3/study_abroad.txt', ['src/content/docs/bachelor/year-3/study-abroad.md', 'page']], ['pages/study/bachelor/year_3/study_abroad.txt', ['src/content/docs/data-science-and-ai/year-3/study-abroad.md', 'page']],
['pages/study/master_ai.txt', ['src/content/docs/master-ai/index.md', 'page']], ['pages/study/master_ai.txt', ['src/content/docs/master-ai/index.md', 'page']],
['pages/study/master_ai/year_1.txt', ['src/content/docs/master-ai/year-1/index.md', 'page']], ['pages/study/master_ai/year_1.txt', ['src/content/docs/master-ai/year-1/index.md', 'page']],
['pages/study/master_ai/year_1/project_mai_1.txt', ['src/content/docs/master-ai/year-1/research-project-1.md', 'page']], ['pages/study/master_ai/year_1/project_mai_1.txt', ['src/content/docs/master-ai/year-1/research-project-1.md', 'page']],

View file

@ -4,7 +4,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path'; import { join } from 'node:path';
const PROGRAMMES = new Map([ const PROGRAMMES = new Map([
['bachelor', 'bachelor'], ['bachelor', 'data-science-and-ai'],
['master_ai', 'master-ai'], ['master_ai', 'master-ai'],
['master_dsdm', 'master-dsdm'], ['master_dsdm', 'master-dsdm'],
]); ]);
@ -42,7 +42,11 @@ export function destinationForPageId(pageId) {
return null; return null;
} }
const routeParts = [PROGRAMMES.get(parts[1]), ...parts.slice(2).map(slug)]; const routeParts = [PROGRAMMES.get(parts[1]), ...parts.slice(2).map(slug)];
return `src/content/docs/${routeParts.join('/')}.md`; const sharedCourse = parts[1] === 'bachelor' && parts[2] === 'year_1' && new Set([
'discrete_mathematics', 'procedural_programming', 'calculus', 'logic',
'objects_in_programming', 'data_structures_and_algorithms', 'linear_algebra',
]).has(parts.at(-1));
return `src/content/docs/${routeParts.join('/')}.${sharedCourse ? 'mdx' : 'md'}`;
} }
export function classifyPreviousPage(pageId, representedPageIds = new Set(), source = '') { export function classifyPreviousPage(pageId, representedPageIds = new Set(), source = '') {
@ -61,7 +65,7 @@ function humanLabel(value) {
function markdownRouteForPageId(pageId) { function markdownRouteForPageId(pageId) {
const destination = destinationForPageId(pageId); const destination = destinationForPageId(pageId);
return destination return destination
? `/${destination.replace(/^src\/content\/docs\//, '').replace(/\.md$/, '')}/` ? `/${destination.replace(/^src\/content\/docs\//, '').replace(/\.mdx?$/, '')}/`
: `https://msvincognito.nl/wiki/${pageId.replaceAll(':', '/')}`; : `https://msvincognito.nl/wiki/${pageId.replaceAll(':', '/')}`;
} }

View file

@ -26,6 +26,7 @@ export async function checkRenderedOutput({ distRoot }) {
for (const file of htmlFiles) { for (const file of htmlFiles) {
const path = relative(absoluteDistRoot, file).split(sep).join('/'); const path = relative(absoluteDistRoot, file).split(sep).join('/');
const { document } = parseHTML(await readFile(file, 'utf8')); const { document } = parseHTML(await readFile(file, 'utf8'));
if (document.querySelector('meta[http-equiv="refresh"]')) continue;
const h1Count = document.querySelectorAll('h1').length; const h1Count = document.querySelectorAll('h1').length;
if (h1Count !== 1) headingIssues.push({ file: path, h1Count }); if (h1Count !== 1) headingIssues.push({ file: path, h1Count });
} }

View file

@ -0,0 +1,25 @@
---
import { programmeForPathname, programmeSwitchTargets } from '../config/programme-navigation.mjs';
const active = programmeForPathname(Astro.url.pathname.replace(import.meta.env.BASE_URL.replace(/\/$/, ''), ''));
const targets = programmeSwitchTargets(Astro.url.pathname);
const withBase = (path: string) => `${import.meta.env.BASE_URL.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
---
<nav class="programme-switch" aria-label="Bachelor programme">
<span class="programme-switch__label">Bachelor programme</span>
<div class="programme-switch__links">
<a
href={withBase(targets.dataScience)}
aria-label="Data Science & AI"
title="Data Science & AI"
aria-current={active === 'data-science-and-ai' ? 'page' : undefined}
>DSAI</a>
<a
href={withBase(targets.computerScience)}
aria-label="Computer Science"
title="Computer Science"
aria-current={active === 'computer-science' ? 'page' : undefined}
>CS</a>
</div>
</nav>

View file

@ -0,0 +1,7 @@
---
import DefaultSidebar from '@astrojs/starlight/components/Sidebar.astro';
import ProgrammeSwitch from './ProgrammeSwitch.astro';
---
<ProgrammeSwitch />
<DefaultSidebar />

View file

@ -0,0 +1,19 @@
export function baseAwareLinks({ base = '/' } = {}) {
const prefix = base === '/' ? '' : `/${base.replace(/^\/+|\/+$/g, '')}`;
return function transform(tree) {
if (!prefix) return tree;
const visit = (node) => {
if ((node.type === 'link' || node.type === 'image')
&& typeof node.url === 'string'
&& node.url.startsWith('/')
&& !node.url.startsWith('//')
&& node.url !== prefix
&& !node.url.startsWith(`${prefix}/`)) {
node.url = `${prefix}${node.url}`;
}
if (Array.isArray(node.children)) node.children.forEach(visit);
};
visit(tree);
return tree;
};
}

View file

@ -0,0 +1,22 @@
const routes = [
'',
'year-1/',
'year-1/project-1-1', 'year-1/project-1-2',
'year-1/block-1/discrete-mathematics', 'year-1/block-1/introduction-to-data-science-and-artifical-intelligence', 'year-1/block-1/procedural-programming',
'year-1/block-2/calculus', 'year-1/block-2/logic', 'year-1/block-2/objects-in-programming',
'year-1/block-4/data-structures-and-algorithms', 'year-1/block-4/linear-algebra', 'year-1/block-4/principles-of-data-science',
'year-1/block-5/computational-and-cognitive-neuroscience', 'year-1/block-5/numerical-methods', 'year-1/block-5/software-engineering',
'year-2/', 'year-2/project-2-1', 'year-2/project-2-2', 'year-2/honours-programme',
'year-2/block-1/databases', 'year-2/block-1/graph-theory', 'year-2/block-1/probability-and-statistics',
'year-2/block-2/machine-learning', 'year-2/block-2/reasoning-techniques', 'year-2/block-2/simulation-and-statisical-analysis',
'year-2/block-4/human-computer-interaction-and-affective-computing', 'year-2/block-4/mathematical-modelling', 'year-2/block-4/natural-language-processing',
'year-2/block-5/game-theory', 'year-2/block-5/introduction-to-image-and-video-processing', 'year-2/block-5/linear-programming', 'year-2/block-5/philosophy-and-artificial-intelligence',
'year-3/', 'year-3/bachelors-thesis', 'year-3/project-3-1', 'year-3/study-abroad', 'year-3/honours-programme',
'year-3/block-1/prolog', 'year-3/block-1/robotics-and-embedded-systems', 'year-3/block-1/semantic-web', 'year-3/block-1/software-and-systems-verification',
'year-3/block-2/introduction-to-bio-informatics', 'year-3/block-2/logic-for-artificial-intelligence', 'year-3/block-2/parallel-programming', 'year-3/block-2/quantum-computation', 'year-3/block-2/recommender-systems', 'year-3/block-2/secure-web-applications',
'year-3/block-4/data-analysis', 'year-3/block-4/intelligent-systems', 'year-3/block-4/operations-research-case-studies',
];
export const legacyBachelorRedirects = Object.freeze(Object.fromEntries(
routes.sort().map((route) => [`/bachelor/${route}`, `/data-science-and-ai/${route}`]),
));

View file

@ -0,0 +1,24 @@
const examArchivePath = /(?:^|[/\\])previous-exams-and-documents\.md$/;
export function openExamLinksInNewTab() {
return (tree, file) => {
if (!examArchivePath.test(file?.path ?? '')) return;
const visit = (node) => {
if (node.type === 'link' && /\.pdf(?:[?#]|$)/i.test(node.url ?? '')) {
node.data = {
...node.data,
hProperties: {
...node.data?.hProperties,
target: '_blank',
rel: 'noopener noreferrer',
},
};
}
for (const child of node.children ?? []) visit(child);
};
visit(tree);
};
}

View file

@ -0,0 +1,79 @@
import { sidebar } from './sidebar.mjs';
const shared = {
'period-1/discrete-mathematics': 'block-1/discrete-mathematics',
'period-1/procedural-programming': 'block-1/procedural-programming',
'period-2/calculus': 'block-2/calculus',
'period-2/logic': 'block-2/logic',
'period-2/objects-in-programming': 'block-2/objects-in-programming',
'period-4/data-structures-and-algorithms': 'block-4/data-structures-and-algorithms',
'period-4/linear-algebra': 'block-4/linear-algebra',
};
const computerScience = {
label: 'Computer Science',
items: [
{ slug: 'computer-science' },
{ label: 'Year 1', items: [
{ slug: 'computer-science/year-1' },
{ label: 'Period 1', items: ['discrete-mathematics', 'introduction-to-computer-science', 'procedural-programming'].map((slug) => ({ slug: `computer-science/year-1/period-1/${slug}` })) },
{ label: 'Period 2', items: ['calculus', 'logic', 'objects-in-programming'].map((slug) => ({ slug: `computer-science/year-1/period-2/${slug}` })) },
{ label: 'Period 4', items: ['computer-architecture', 'data-structures-and-algorithms', 'linear-algebra'].map((slug) => ({ slug: `computer-science/year-1/period-4/${slug}` })) },
{ label: 'Period 5', items: ['algorithmic-design', 'databases', 'statistics'].map((slug) => ({ slug: `computer-science/year-1/period-5/${slug}` })) },
] },
],
};
const compact = { label: 'Bachelor programmes', items: [{ slug: 'computer-science' }, { slug: 'data-science-and-ai' }] };
const programmeSharedLabels = new Set(['Home', 'Previous exams and documents', 'Useful Information']);
export function programmeForPathname(pathname) {
if (pathname.startsWith('/computer-science')) return 'computer-science';
if (pathname.startsWith('/data-science-and-ai') || pathname.startsWith('/bachelor')) return 'data-science-and-ai';
return null;
}
export function programmeSwitchTargets(pathname) {
let computerScience = '/computer-science/';
let dataScience = '/data-science-and-ai/';
for (const [cs, ds] of Object.entries(shared)) {
if (pathname.includes(`/computer-science/year-1/${cs}`) || pathname.includes(`/data-science-and-ai/year-1/${ds}`)) {
computerScience = `/computer-science/year-1/${cs}/`;
dataScience = `/data-science-and-ai/year-1/${ds}/`;
break;
}
}
return { computerScience, dataScience };
}
export function sidebarForPathname(pathname) {
const programme = programmeForPathname(pathname);
const dataScience = sidebar.find(({ label }) => label === 'Data Science & AI');
if (programme) {
const undergraduate = programme === 'computer-science' ? computerScience : dataScience;
const home = sidebar.find(({ label }) => label === 'Home');
const previousExams = sidebar.find(({ slug }) => slug === 'previous-exams-and-documents');
const usefulInformation = sidebar.find(({ label }) => label === 'Useful Information');
return [home, previousExams, undergraduate, usefulInformation];
}
const withoutProgrammes = sidebar.filter(({ label }) => label !== 'Computer Science' && label !== 'Data Science & AI');
return [...withoutProgrammes.slice(0, 3), compact, ...withoutProgrammes.slice(3)];
}
export function filterResolvedSidebar(entries, pathname) {
const programme = programmeForPathname(pathname);
if (programme) {
const activeLabel = programme === 'computer-science' ? 'Computer Science' : 'Data Science & AI';
return entries.filter(({ label }) => programmeSharedLabels.has(label) || label === activeLabel);
}
return entries.map((entry) => {
if ((entry.label === 'Computer Science' || entry.label === 'Data Science & AI') && 'entries' in entry) {
return { ...entry, entries: entry.entries.slice(0, 1) };
}
return entry;
});
}

View file

@ -1,68 +1,68 @@
export const recoveredCourseSidebar = { export const recoveredCourseSidebar = {
'bachelor/year-1': [ 'data-science-and-ai/year-1': [
{ label: 'Block 1', items: [ { label: 'Block 1', items: [
{ slug: 'bachelor/year-1/block-1/discrete-mathematics' }, { slug: 'data-science-and-ai/year-1/block-1/discrete-mathematics' },
{ slug: 'bachelor/year-1/block-1/introduction-to-data-science-and-artifical-intelligence' }, { slug: 'data-science-and-ai/year-1/block-1/introduction-to-data-science-and-artifical-intelligence' },
{ slug: 'bachelor/year-1/block-1/procedural-programming' }, { slug: 'data-science-and-ai/year-1/block-1/procedural-programming' },
] }, ] },
{ label: 'Block 2', items: [ { label: 'Block 2', items: [
{ slug: 'bachelor/year-1/block-2/calculus' }, { slug: 'data-science-and-ai/year-1/block-2/calculus' },
{ slug: 'bachelor/year-1/block-2/logic' }, { slug: 'data-science-and-ai/year-1/block-2/logic' },
{ slug: 'bachelor/year-1/block-2/objects-in-programming' }, { slug: 'data-science-and-ai/year-1/block-2/objects-in-programming' },
] }, ] },
{ label: 'Block 4', items: [ { label: 'Block 4', items: [
{ slug: 'bachelor/year-1/block-4/data-structures-and-algorithms' }, { slug: 'data-science-and-ai/year-1/block-4/data-structures-and-algorithms' },
{ slug: 'bachelor/year-1/block-4/linear-algebra' }, { slug: 'data-science-and-ai/year-1/block-4/linear-algebra' },
{ slug: 'bachelor/year-1/block-4/principles-of-data-science' }, { slug: 'data-science-and-ai/year-1/block-4/principles-of-data-science' },
] }, ] },
{ label: 'Block 5', items: [ { label: 'Block 5', items: [
{ slug: 'bachelor/year-1/block-5/computational-and-cognitive-neuroscience' }, { slug: 'data-science-and-ai/year-1/block-5/computational-and-cognitive-neuroscience' },
{ slug: 'bachelor/year-1/block-5/numerical-methods' }, { slug: 'data-science-and-ai/year-1/block-5/numerical-methods' },
{ slug: 'bachelor/year-1/block-5/software-engineering' }, { slug: 'data-science-and-ai/year-1/block-5/software-engineering' },
] }, ] },
], ],
'bachelor/year-2': [ 'data-science-and-ai/year-2': [
{ label: 'Block 1', items: [ { label: 'Block 1', items: [
{ slug: 'bachelor/year-2/block-1/databases' }, { slug: 'data-science-and-ai/year-2/block-1/databases' },
{ slug: 'bachelor/year-2/block-1/graph-theory' }, { slug: 'data-science-and-ai/year-2/block-1/graph-theory' },
{ slug: 'bachelor/year-2/block-1/probability-and-statistics' }, { slug: 'data-science-and-ai/year-2/block-1/probability-and-statistics' },
] }, ] },
{ label: 'Block 2', items: [ { label: 'Block 2', items: [
{ slug: 'bachelor/year-2/block-2/machine-learning' }, { slug: 'data-science-and-ai/year-2/block-2/machine-learning' },
{ slug: 'bachelor/year-2/block-2/reasoning-techniques' }, { slug: 'data-science-and-ai/year-2/block-2/reasoning-techniques' },
{ slug: 'bachelor/year-2/block-2/simulation-and-statisical-analysis' }, { slug: 'data-science-and-ai/year-2/block-2/simulation-and-statisical-analysis' },
] }, ] },
{ label: 'Block 4', items: [ { label: 'Block 4', items: [
{ slug: 'bachelor/year-2/block-4/human-computer-interaction-and-affective-computing' }, { slug: 'data-science-and-ai/year-2/block-4/human-computer-interaction-and-affective-computing' },
{ slug: 'bachelor/year-2/block-4/mathematical-modelling' }, { slug: 'data-science-and-ai/year-2/block-4/mathematical-modelling' },
{ slug: 'bachelor/year-2/block-4/natural-language-processing' }, { slug: 'data-science-and-ai/year-2/block-4/natural-language-processing' },
] }, ] },
{ label: 'Block 5', items: [ { label: 'Block 5', items: [
{ slug: 'bachelor/year-2/block-5/game-theory' }, { slug: 'data-science-and-ai/year-2/block-5/game-theory' },
{ slug: 'bachelor/year-2/block-5/introduction-to-image-and-video-processing' }, { slug: 'data-science-and-ai/year-2/block-5/introduction-to-image-and-video-processing' },
{ slug: 'bachelor/year-2/block-5/linear-programming' }, { slug: 'data-science-and-ai/year-2/block-5/linear-programming' },
{ slug: 'bachelor/year-2/block-5/philosophy-and-artificial-intelligence' }, { slug: 'data-science-and-ai/year-2/block-5/philosophy-and-artificial-intelligence' },
] }, ] },
], ],
'bachelor/year-3': [ 'data-science-and-ai/year-3': [
{ label: 'Block 1', items: [ { label: 'Block 1', items: [
{ slug: 'bachelor/year-3/block-1/prolog' }, { slug: 'data-science-and-ai/year-3/block-1/prolog' },
{ slug: 'bachelor/year-3/block-1/robotics-and-embedded-systems' }, { slug: 'data-science-and-ai/year-3/block-1/robotics-and-embedded-systems' },
{ slug: 'bachelor/year-3/block-1/semantic-web' }, { slug: 'data-science-and-ai/year-3/block-1/semantic-web' },
{ slug: 'bachelor/year-3/block-1/software-and-systems-verification' }, { slug: 'data-science-and-ai/year-3/block-1/software-and-systems-verification' },
] }, ] },
{ label: 'Block 2', items: [ { label: 'Block 2', items: [
{ slug: 'bachelor/year-3/block-2/introduction-to-bio-informatics' }, { slug: 'data-science-and-ai/year-3/block-2/introduction-to-bio-informatics' },
{ slug: 'bachelor/year-3/block-2/logic-for-artificial-intelligence' }, { slug: 'data-science-and-ai/year-3/block-2/logic-for-artificial-intelligence' },
{ slug: 'bachelor/year-3/block-2/parallel-programming' }, { slug: 'data-science-and-ai/year-3/block-2/parallel-programming' },
{ slug: 'bachelor/year-3/block-2/quantum-computation' }, { slug: 'data-science-and-ai/year-3/block-2/quantum-computation' },
{ slug: 'bachelor/year-3/block-2/recommender-systems' }, { slug: 'data-science-and-ai/year-3/block-2/recommender-systems' },
{ slug: 'bachelor/year-3/block-2/secure-web-applications' }, { slug: 'data-science-and-ai/year-3/block-2/secure-web-applications' },
] }, ] },
{ label: 'Block 4', items: [ { label: 'Block 4', items: [
{ slug: 'bachelor/year-3/block-4/data-analysis' }, { slug: 'data-science-and-ai/year-3/block-4/data-analysis' },
{ slug: 'bachelor/year-3/block-4/intelligent-systems' }, { slug: 'data-science-and-ai/year-3/block-4/intelligent-systems' },
{ slug: 'bachelor/year-3/block-4/operations-research-case-studies' }, { slug: 'data-science-and-ai/year-3/block-4/operations-research-case-studies' },
] }, ] },
], ],
'master-ai/year-1': [ 'master-ai/year-1': [

View file

@ -5,37 +5,66 @@ export const sidebar = [
{ label: 'About Incognito', link: '/about-incognito/' }, { label: 'About Incognito', link: '/about-incognito/' },
{ slug: 'previous-exams-and-documents' }, { slug: 'previous-exams-and-documents' },
{ {
label: 'Bachelor', label: 'Computer Science',
items: [ items: [
{ slug: 'bachelor' }, { slug: 'computer-science' },
{ label: 'Year 1', items: [
{ slug: 'computer-science/year-1' },
{ label: 'Period 1', items: [
{ slug: 'computer-science/year-1/period-1/discrete-mathematics' },
{ slug: 'computer-science/year-1/period-1/introduction-to-computer-science' },
{ slug: 'computer-science/year-1/period-1/procedural-programming' },
] },
{ label: 'Period 2', items: [
{ slug: 'computer-science/year-1/period-2/calculus' },
{ slug: 'computer-science/year-1/period-2/logic' },
{ slug: 'computer-science/year-1/period-2/objects-in-programming' },
] },
{ label: 'Period 4', items: [
{ slug: 'computer-science/year-1/period-4/computer-architecture' },
{ slug: 'computer-science/year-1/period-4/data-structures-and-algorithms' },
{ slug: 'computer-science/year-1/period-4/linear-algebra' },
] },
{ label: 'Period 5', items: [
{ slug: 'computer-science/year-1/period-5/algorithmic-design' },
{ slug: 'computer-science/year-1/period-5/databases' },
{ slug: 'computer-science/year-1/period-5/statistics' },
] },
] },
],
},
{
label: 'Data Science & AI',
items: [
{ slug: 'data-science-and-ai' },
{ {
label: 'Year 1', label: 'Year 1',
items: [ items: [
{ slug: 'bachelor/year-1' }, { slug: 'data-science-and-ai/year-1' },
{ slug: 'bachelor/year-1/project-1-1' }, { slug: 'data-science-and-ai/year-1/project-1-1' },
{ slug: 'bachelor/year-1/project-1-2' }, { slug: 'data-science-and-ai/year-1/project-1-2' },
...recoveredCourseSidebar['bachelor/year-1'], ...recoveredCourseSidebar['data-science-and-ai/year-1'],
], ],
}, },
{ {
label: 'Year 2', label: 'Year 2',
items: [ items: [
{ slug: 'bachelor/year-2' }, { slug: 'data-science-and-ai/year-2' },
{ slug: 'bachelor/year-2/project-2-1' }, { slug: 'data-science-and-ai/year-2/project-2-1' },
{ slug: 'bachelor/year-2/project-2-2' }, { slug: 'data-science-and-ai/year-2/project-2-2' },
{ slug: 'bachelor/year-2/honours-programme' }, { slug: 'data-science-and-ai/year-2/honours-programme' },
...recoveredCourseSidebar['bachelor/year-2'], ...recoveredCourseSidebar['data-science-and-ai/year-2'],
], ],
}, },
{ {
label: 'Year 3', label: 'Year 3',
items: [ items: [
{ slug: 'bachelor/year-3' }, { slug: 'data-science-and-ai/year-3' },
{ slug: 'bachelor/year-3/bachelors-thesis' }, { slug: 'data-science-and-ai/year-3/bachelors-thesis' },
{ slug: 'bachelor/year-3/project-3-1' }, { slug: 'data-science-and-ai/year-3/project-3-1' },
{ slug: 'bachelor/year-3/study-abroad' }, { slug: 'data-science-and-ai/year-3/study-abroad' },
{ slug: 'bachelor/year-3/honours-programme' }, { slug: 'data-science-and-ai/year-3/honours-programme' },
...recoveredCourseSidebar['bachelor/year-3'], ...recoveredCourseSidebar['data-science-and-ai/year-3'],
], ],
}, },
], ],
@ -80,10 +109,13 @@ export const sidebar = [
label: 'Useful Information', label: 'Useful Information',
items: [ items: [
{ slug: 'useful-information' }, { slug: 'useful-information' },
{ slug: 'useful-information/housing-guide' },
{ slug: 'useful-information/laptop-buying-advice' },
{ slug: 'useful-information/linux-tricks' },
{ slug: 'useful-information/surviving-dacs' },
{ slug: 'useful-information/dke-locations' }, { slug: 'useful-information/dke-locations' },
{ slug: 'useful-information/handy-locations' }, { slug: 'useful-information/handy-locations' },
{ slug: 'useful-information/it-services' }, { slug: 'useful-information/it-services' },
{ slug: 'useful-information/laptop-buying-advice' },
], ],
}, },
]; ];

View file

@ -0,0 +1,14 @@
---
title: Computer Science
description: Recovered historical Computer Science course information.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University programme information before relying on it.
:::
This section contains the substantive Computer Science course information recovered from the previous Incognito wiki. Only courses with actual recovered content are published.
- [Year 1](./year-1/)
- [Previous exams and documents](/previous-exams-and-documents/)

View file

@ -0,0 +1,11 @@
---
title: Computer Science — Year 1
description: Recovered historical first-year Computer Science course information.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
The sidebar lists the Year 1 courses for which substantive historical content was recovered. Empty placeholders from the source branch are intentionally not published.

View file

@ -0,0 +1,9 @@
---
title: Discrete Mathematics
description: Historical course details for Discrete Mathematics, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/discrete-mathematics.mdx';
<SharedCourse />

View file

@ -0,0 +1,22 @@
---
title: Introduction to Computer Science
description: Historical course details for Introduction to Computer Science, recovered from the previous Incognito wiki.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
## Full course description
The course introduces algorithms, computer architecture and hardware, models of computation, computer networks, and operating systems. It develops abstraction, decomposition, pattern recognition, and algorithmic thinking, including practical work with a wirelessly controlled microcontroller device.
## Prerequisites
None.
## Recommended reading
- *Computational Thinking for the Modern Problem Solver* by David Riley and Kenny A. Hunt
- *Computer Science Illuminated* by Nell B. Dale

View file

@ -0,0 +1,9 @@
---
title: Procedural Programming
description: Historical course details for Procedural Programming, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/procedural-programming.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Calculus
description: Historical course details for Calculus, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/calculus.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Logic
description: Historical course details for Logic, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/logic.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Objects in Programming
description: Historical course details for Objects in Programming, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/objects-in-programming.mdx';
<SharedCourse />

View file

@ -0,0 +1,21 @@
---
title: Computer Architecture
description: Historical course details for Computer Architecture, recovered from the previous Incognito wiki.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
## Full course description
This course covers digital logic, combinational and sequential circuits, hardware description languages, processor architecture, memory systems, and input/output systems. Students gain experience designing and simulating hardware components.
## Prerequisites
None.
## Recommended reading
*Computer Architecture: A Quantitative Approach* by John L. Hennessy and David A. Patterson.

View file

@ -0,0 +1,9 @@
---
title: Data Structures and Algorithms
description: Historical course details for Data Structures and Algorithms, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/data-structures-and-algorithms.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Linear Algebra
description: Historical course details for Linear Algebra, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/linear-algebra.mdx';
<SharedCourse />

View file

@ -0,0 +1,21 @@
---
title: Algorithmic Design
description: Historical course details for Algorithmic Design, recovered from the previous Incognito wiki.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
## Full course description
Following Data Structures and Algorithms, this course develops algorithm design and analysis through greedy algorithms, dynamic programming, the master theorem, NP-completeness, backtracking, linear programming, and branch-and-bound.
## Prerequisites
Desired prior knowledge: Data Structures and Algorithms and Discrete Mathematics.
## Recommended reading
Goodrich and Tamassia, *Algorithm Design and Applications* (2015).

View file

@ -0,0 +1,21 @@
---
title: Databases
description: Historical course details for Databases, recovered from the previous Incognito wiki.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
## Full course description
This course introduces relational database design, implementation, optimization, data modelling, relational algebra, and SQL. It also covers concurrency, recovery, indexing, triggers, and alternative models such as NoSQL, with a group database project.
## Prerequisites
None.
## Recommended reading
*Readings in Database Systems*, fifth edition, by Peter Bailis, Joseph M. Hellerstein, and Michael Stonebraker.

View file

@ -0,0 +1,17 @@
---
title: Statistics
description: Historical course details for Statistics, recovered from the previous Incognito wiki.
---
:::caution[Historical information]
This information originated in the previous wiki and may be outdated. Check the current Maastricht University course information before relying on it.
:::
## Full course description
Statistics introduces probability distributions, random variables, expectation, standard deviation, independence, the central limit theorem, hypothesis testing, and confidence intervals.
## Prerequisites
None.

View file

@ -0,0 +1,9 @@
---
title: Discrete Mathematics
description: Historical course details for Discrete Mathematics, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/discrete-mathematics.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Procedural Programming
description: Historical course details for Procedural Programming, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/procedural-programming.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Calculus
description: Historical course details for Calculus, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/calculus.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Logic
description: Historical course details for Logic, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/logic.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Objects in Programming
description: Historical course details for Objects in Programming, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/objects-in-programming.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Data Structures and Algorithms
description: Historical course details for Data Structures and Algorithms, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/data-structures-and-algorithms.mdx';
<SharedCourse />

View file

@ -0,0 +1,9 @@
---
title: Linear Algebra
description: Historical course details for Linear Algebra, recovered from the previous Incognito wiki.
---
import SharedCourse from '../../../../shared-courses/year-1/linear-algebra.mdx';
<SharedCourse />

View file

@ -16,7 +16,7 @@ MSV Incognito is the study association for the Department of Advanced Computing
## Explore the wiki ## Explore the wiki
<CardGrid> <CardGrid>
<LinkCard title="Bachelor" href="./bachelor/" description="Programme and course information for the bachelor." /> <LinkCard title="Bachelor" href="./data-science-and-ai/" description="Programme and course information for the bachelor." />
<LinkCard title="Master AI" href="./master-ai/" description="Resources for the Artificial Intelligence masters programme." /> <LinkCard title="Master AI" href="./master-ai/" description="Resources for the Artificial Intelligence masters programme." />
<LinkCard title="Master DSDM" href="./master-dsdm/" description="Resources for the Data Science for Decision Making masters programme." /> <LinkCard title="Master DSDM" href="./master-dsdm/" description="Resources for the Data Science for Decision Making masters programme." />
<LinkCard title="Useful Information" href="./useful-information/" description="Practical information for students." /> <LinkCard title="Useful Information" href="./useful-information/" description="Practical information for students." />

View file

@ -0,0 +1,61 @@
---
title: Housing Guide
description: Practical guidance for finding student housing in Maastricht and recognizing scams.
---
This student-contributed guide was assembled by Incognito board member Botond to help incoming students find accommodation in Maastricht. Start searching early, compare several sources, and use [MyMaastricht](https://mymaastricht.nl/) and official university resources when you need authoritative information.
## Where to look
### Facebook groups and private listings
Private listings often advertise rooms in shared houses. They can offer direct contact with current tenants or landlords, but popular listings attract many responses and open marketplaces also attract scammers.
Check listings regularly, respond with a short introduction about yourself, and do not let artificial urgency push you into skipping basic checks.
### Maastricht Housing
[Maastricht Housing](https://www.maastrichthousing.com/) brings together listings from several housing providers. Review its current registration, eligibility, fee, and reservation information directly on the platform before relying on it.
### Housing agencies and student residences
Agencies and managed student residences can be useful when you prefer a studio, a furnished room, or a single organization handling the tenancy. Compare the complete monthly cost, deposits, agency charges, contract duration, cancellation terms, and reviews—not just the advertised base rent.
Search for the agency independently instead of following only the contact details in an advertisement. A polished website or professional-looking message is not proof that a business or listing is legitimate.
## Avoiding scams
- Request an in-person or live video viewing. If that is impossible, ask someone you trust locally to attend.
- Do not send a passport or identity document until you have verified who is requesting it, why it is required, and how it will be stored. Redact details that are not needed.
- Ask for the precise address and the landlord's or agent's full name.
- Reverse-search listing photos and compare details across every image.
- Ask which utilities, municipal taxes, internet costs, and service charges are included.
- Confirm the rental period, notice period, deposit, and any rules concerning registration at the address.
- Read and sign a complete rental contract before transferring money.
- Treat unusual payment methods, unexplained fees, pressure to decide immediately, and refusal to provide verifiable details as serious warning signs.
A foreign phone number, an owner living abroad, or a recently created social profile is not proof of fraud by itself. Treat such details as reasons to verify the person and property more carefully.
### Checking private listings
- Review the advertiser's profile history, public activity, and whether their identity is consistent across platforms.
- Search the address, contact details, and distinctive sentences from the advertisement.
- Look for comments from other prospective tenants and reports of copied advertisements.
- Ask current occupants about the room, house, landlord, and expected handover.
- Never let a convincing viewing replace verification of the contract and the person entitled to rent out the property.
### Checking agencies
- Search for the business independently and compare reviews across multiple sources.
- Verify its registration through the [Dutch Chamber of Commerce](https://www.kvk.nl/en/).
- Check that the company name on the contract, invoice, website, and bank account is consistent.
- Ask for a written breakdown of agency fees and services before agreeing to anything.
- Be cautious if representatives avoid written answers, change payment details, or cannot explain their relationship to the property owner.
## Practical tips after moving
Property ownership information can be requested from the [Dutch Cadastre](https://www.kadaster.nl/producten/woning/eigendomsinformatie) when an ownership check is appropriate.
Unfurnished rooms are manageable: Maastricht has second-hand shops, online marketplaces, and student groups where furniture is often inexpensive or free. Measure entrances and the room before arranging collection, and agree clearly on transport.
Photograph the room and any existing damage when you receive the keys. Save the signed contract, inspection report, payment records, inventory, and correspondence for the duration of the tenancy.

View file

@ -1,21 +1,23 @@
--- ---
title: Useful Information title: Useful Information
description: Historical student information for the Department of Data Science and Knowledge Engineering at Maastricht University. description: Practical guides and student information for Maastricht University DACS students.
--- ---
:::caution[Historical information] This section brings together practical guides created by students alongside reference information from the previous Incognito wiki.
This information originated in the previous wiki and may be outdated.
:::
The following pages contain useful information for students of the Department of Data Science and Knowledge Engineering at Maastricht University, including information about IT services and where to find the Rules and Regulations. For the official and most up-to-date overview of programmes and university services, visit the [Maastricht University education website](https://www.maastrichtuniversity.nl/education).
For the most up-to-date overview of our studies, visit the [Maastricht University education website](https://www.maastrichtuniversity.nl/education).
Repository maintainers can add useful information to the most relevant Markdown page and update the manual sidebar. They should add a new page only when no existing page fits. Repository maintainers can add useful information to the most relevant Markdown page and update the manual sidebar. They should add a new page only when no existing page fits.
## Useful pages ## Practical guides
- [Housing Guide](./housing-guide/)
- [Laptop Buying Advice](./laptop-buying-advice/)
- [Linux Tricks](./linux-tricks/)
- [Surviving DACS](./surviving-dacs/)
## Other useful information
- [Locations of DKE](./dke-locations/) - [Locations of DKE](./dke-locations/)
- [Handy Locations](./handy-locations/) - [Handy Locations](./handy-locations/)
- [IT Services](./it-services/) - [IT Services](./it-services/)
- [Laptop Buying Advice](./laptop-buying-advice/)

View file

@ -1,47 +1,206 @@
--- ---
title: Laptop Buying Advice title: Laptop Buying Advice
description: Historical laptop-buying guidance for DKE students. description: Practical laptop-buying guidance for DACS students.
--- ---
:::caution[Historical information] A practical guide for students of the Department of Advanced Computing Sciences at Maastricht University, especially:
This information originated in the previous wiki and may be outdated. Prices, Windows versions, hardware capacities, and processor availability below are historical and must be verified before making a purchase.
:::
MSV Incognito often received questions from prospective students about the hardware required to follow a programme in our studies. To streamline things, we created this short page. - BSc Computer Science
- BSc Data Science and Artificial Intelligence
- MSc Artificial Intelligence
- MSc Data Science for Decision Making
- MSc Responsible Data Science
## Is a laptop required? > **Who is the AI hardware advice for?** The sections about CUDA, GPU memory, local model inference, and neural-network training are aimed mainly at BSc Data Science and Artificial Intelligence students, MSc Artificial Intelligence students, and anyone intentionally choosing AI- or deep-learning-heavy electives or research projects. They are not general hardware requirements for every DACS student. In particular, most Computer Science, Data Science for Decision Making, and Responsible Data Science coursework does not require an AI-focused laptop.
Having a laptop is required for computer labs in certain courses. It is also very handy for group projects. You will need a laptop to write, compile, and run Java, Python, and C code. This will, of course, take longer on low-end laptops. ## Short recommendation
Guidelines from the previous wiki: For a **new laptop**, we recommend at least:
- No Chromebook. The laptop must run Windows, Linux, or macOS. This guide only covers Windows laptops, because that is what companies sell and new MacBooks are generally powerful enough anyway. - **16 GB RAM** (32 GB is preferable for serious local AI, large datasets, virtual machines, or a laptop you intend to keep for several years)
- AMD Ryzen or modern Intel processors are both fine. Please note that AMD Ryzen CPUs were only recently becoming available in laptops when this guidance was written. - **512 GB SSD storage** (1 TB is preferable for datasets, virtual machines, containers, and local AI models)
- A 15.6-inch laptop is the most commonly used size. A 14-inch laptop is still fine, depending on your preferences, but anything smaller can make it difficult to type a lot of code (keyboard size) and read code (screen size and resolution). Seventeen-inch laptops are rather large to transport. However, this is down to personal preference. - A recent **mid-range or better processor**, such as an Intel Core Ultra 5/Core i5, AMD Ryzen 5, Apple M-series chip, or equivalent
- Make sure you get a laptop with the keyboard layout you are used to: probably QWERTY for Dutch students, QWERTZ for Germans, AZERTY for French and some Belgian students. - A recent version of **Windows, macOS, or Linux**
- A good keyboard, screen, battery, and build quality: you will use this machine every day
## Laptop tiers For most coursework, **16 GB RAM and 512 GB storage are sufficient**. You do not need an expensive gaming laptop merely to complete your degree. University servers and cloud resources may be used for workloads that are too large for a laptop.
To be blunt: everything that could run Windows 10 or a recent Linux variant properly should suffice. In theory, you could manage your studies on a laptop with a low-power Intel or AMD processor, 2 GB RAM, and 16 GB flash memory. But that would lead to annoyances and is not something we recommend. On the other hand, everything would also run on a laptop with a high-end multi-core AMD or Intel processor, 32 GB RAM, and a 1 TB SSD. Depending on your budget, you could even add a dedicated graphics card. If you specifically want to train neural networks locally or work with software that expects CUDA, choose a laptop with an **NVIDIA RTX GPU**. Check its VRAM, not only the GPU model name.
The prices below are historical indications. Many other factors, such as screen quality, battery life, and build quality, influence the price. There are many websites that provide quality reviews to inform you about specific laptop models. Consider finding the right laptop as a small precursor to doing academic research for the projects during your studies. ## Do you need a laptop?
### Approximately €200 Yes. A laptop is used in computer labs, group projects, assignments, and presentations. It should be able to run development tools for languages such as Python, Java, C/C++, and R, as well as browsers, office software, containers, and possibly virtual machines.
This is the “it runs” category: the territory of an Intel Atom or Celeron processor, 2 GB RAM, and 16 GB storage. We do not recommend this, but as long as it runs Windows or Linux, it is technically possible to finish the tasks required for the study. The programmes do not all have the same emphasis. See [Suggested configurations by programme](#suggested-configurations-by-programme) for specific advice.
### Approximately €400 Course requirements can change. If a particular course publishes hardware or operating-system requirements, follow those over this general guide.
This territory is suitable for a student with a limited budget. Laptops with a recent Intel Core i3 or AMD Ryzen 3, 4 GB RAM, and a 128 GB SSD were available. ## Memory and storage
### Approximately €600 ### RAM
The best price-to-performance ratio was around this price range. A recent Intel Core i5 or AMD Ryzen 5, 8 GB RAM, and a 256 GB SSD was a good laptop configuration and would run anything required for your studies without problems. **16 GB is the minimum we recommend for a new laptop in 2026.** Avoid buying an 8 GB machine unless it is an inexpensive temporary solution and its memory can be upgraded. Many thin laptops have soldered memory, so check before buying.
### Approximately €800 Choose **32 GB** if you expect to use several of the following at once:
If you had a bigger budget, a laptop with a recent Intel Core i7 or AMD Ryzen 7, 8 GB RAM, and a 256 GB SSD would give a minor speed boost over the previous tier. This was also the range where laptops gained something resembling gaming-capable graphics cards. - IDEs, notebooks, and many browser tabs
- Docker containers or virtual machines
- Large datasets or in-memory data processing
- Local language, vision, or generative models
- GPU workloads that can offload data to system memory
### Gaming For unusually large local workloads, 64 GB may be useful, but it is not a general degree requirement.
If you wanted your laptop to run games beyond League of Legends or other light games, the options generally started around €900. There were laptops in that category with an AMD or Nvidia GPU capable of running most games. Medium- and high-end gaming laptops were €1,000€3,000. However, that is outside the scope of this page, since it is not needed for your studies. ### SSD storage
**512 GB is the minimum we recommend.** Operating systems, development environments, Docker images, datasets, virtual machines, and model weights consume space quickly. Choose **1 TB** if the upgrade is affordable, especially when storage cannot be replaced later.
Prefer an SSD over a hard drive. External storage is useful for archives and backups, but it is less convenient for active projects and datasets.
## Understanding GPUs for AI and machine learning
This section is primarily relevant to DSAI and AI-focused students. Other students may safely treat it as optional buying advice unless their electives, thesis, research, or personal projects involve machine learning.
The GPU is the most confusing part of buying an AI laptop. A GPU can make some machine-learning workloads much faster, but many introductory algorithms and data-science tasks still run on the CPU. A fast GPU also cannot compensate for insufficient memory.
### NVIDIA GPUs: the safest option for local training
NVIDIA remains the most compatible choice for deep learning because CUDA is widely assumed by research code, libraries, tutorials, and course projects. If local training matters to you, look for a recent **GeForce RTX** laptop GPU.
Pay close attention to **VRAM**:
- **6 GB:** usable for learning and small models, but restrictive
- **8 GB:** a reasonable entry point for smaller deep-learning experiments
- **12 GB or more:** preferable for local training, larger models, and more flexibility
- **16 GB or more:** excellent for a laptop, but expensive and still not enough for every workload
Laptop GPUs with the same product family name can have different power limits and performance. Read independent reviews of the exact laptop, not just the GPU specification.
### Apple GPUs: strong inference, different training ecosystem
Apple silicon MacBooks are fast, quiet, and power-efficient. Their CPU and GPU share a pool of **unified memory**, which can make a Mac with sufficient memory particularly good for running quantised local language models and other inference workloads. There is no separate VRAM capacity in the same sense as on a discrete NVIDIA laptop GPU.
Apple GPUs do **not support CUDA**. PyTorch can accelerate both inference and training through the MPS/Metal backend, and many popular local-inference applications support Apple silicon. However, some operations, libraries, research repositories, and course instructions are written specifically for CUDA. They may need workarounds, fall back to the CPU, or not work at all.
In short: a MacBook is a good general development laptop and can be excellent for local inference. It is less convenient for CUDA-based training and reproducibility. If buying one for AI work, prefer **at least 16 GB unified memory**, and seriously consider **24 GB or more**. Remember that macOS and applications also use this same memory pool.
### AMD GPUs
AMD GPUs can accelerate machine learning through ROCm and other backends. Support has improved substantially, including support for selected Radeon GPUs and Ryzen AI processors, but it remains more dependent on the exact GPU, operating system, framework, and software version than CUDA.
AMD can be a good option for an informed buyer who has checked the current compatibility matrix for the exact model. It is not the safest default when you need arbitrary research code to work without modification. Integrated Radeon graphics share system RAM and are mainly suited to light acceleration and inference, not heavy training.
### Intel GPUs
Recent Intel Arc discrete GPUs and Intel integrated GPUs can accelerate supported AI workloads through technologies such as the PyTorch XPU backend, oneAPI, OpenVINO, and platform-specific inference runtimes. They are increasingly useful for inference and experimentation.
As with AMD, compatibility depends on the exact hardware and software stack. Intel GPUs do not provide CUDA, and code written only for CUDA may require changes. An Intel Arc GPU can be a reasonable value choice for supported workloads, but NVIDIA remains the lower-friction option for general local deep-learning training.
### Integrated GPUs and NPUs
Integrated GPUs from Intel and AMD share system RAM with the CPU. They are useful for display work, media, light GPU computing, and some optimised inference workloads. Newer integrated GPUs are much more capable than older ones, but they usually have lower sustained performance and memory bandwidth than a good discrete GPU.
Many new processors also include an **NPU**. NPUs are efficient for supported on-device inference, such as transcription, image processing, or operating-system AI features. They are not a replacement for a CUDA GPU for general model training, and framework support varies.
An integrated GPU or NPU is perfectly adequate if you plan to do heavy computation on university servers or in the cloud. Do not pay a large premium for an "AI PC" label without checking which tools can actually use its NPU.
## VRAM, shared memory, and model size
For AI workloads, available memory often matters more than peak GPU speed. Model weights, activations, gradients, optimiser state, and data batches all consume memory. Training normally needs far more memory than inference.
On a laptop with a discrete GPU, **VRAM and system RAM are separate**. Some frameworks can offload model layers or data from VRAM into system RAM when the model does not fit. This is not guaranteed to happen automatically, and it is much slower because data must move between the CPU and GPU. If you intend to rely on offloading, buy enough system RAM as well: **32 GB or more is sensible**.
Apple silicon and some integrated-GPU systems use shared or unified memory. This can allow the GPU to access a larger memory pool, but the operating system and applications need part of that pool too. Large memory capacity makes a model possible to run; it does not necessarily make it fast.
Quantisation can greatly reduce the memory required for inference. Large-scale training, full fine-tuning, and high-throughput experiments generally belong on a server, desktop workstation, or cloud platform rather than a student laptop.
## Chromebooks, tablets, and unusual devices
We do **not recommend a Chromebook as your primary study laptop**. Some Chromebooks provide a Linux development environment and can run editors, terminals, and lightweight programming tools, but compatibility with virtualisation, containers, specialised drivers, required desktop software, and GPU computing is inconsistent. Storage and RAM are often limited as well.
An Android tablet or iPad is useful as a companion for notes and reading, but it should not be your only computer. Windows-on-ARM laptops may be excellent portable machines, but check that required development tools, virtualisation software, and drivers support ARM before buying.
If you already own one of these devices, it may cover part of the programme with remote access to another machine. That is different from recommending it as a safe new purchase.
## Operating system
- **Windows:** broadly compatible and supports NVIDIA CUDA. WSL 2 provides a convenient Linux environment, although virtualisation and storage require extra disk space.
- **Linux:** excellent for development and closely matches many servers. Hardware support, battery life, fingerprint readers, sleep, and vendor utilities vary by laptop model.
- **macOS:** excellent Unix-based development environment and strong Apple-silicon efficiency. It does not support CUDA, and some x86-only or Windows-only tools may require alternatives or virtualisation.
No operating system is universally best. Choose one you can work with, while considering the GPU software you expect to use.
## Other things worth checking
- **Screen:** 14 to 16 inches is a practical range. Prefer a readable resolution and enough brightness over an unnecessarily high refresh rate.
- **Keyboard layout:** make sure it is one you are comfortable using, such as QWERTY, QWERTZ, or AZERTY.
- **Battery and weight:** a powerful gaming laptop may spend much of the day near a power socket and can be tiring to carry.
- **Cooling:** sustained CPU and GPU performance depends on cooling. Thin designs may slow down under long workloads.
- **Ports and charging:** check for the displays, USB devices, and chargers you use.
- **Repairability:** replaceable storage, memory, and battery can extend the useful life of a laptop.
- **Warranty:** international students should check where warranty service is available.
## Suggested configurations by programme
These specifications are intended for a **new primary laptop**. "Minimum" means a practical floor for completing the programme, not the cheapest computer that could technically run an editor. "Recommended" provides more room for several years of coursework, development tools, datasets, containers, and virtual machines.
| Programme | Minimum | Recommended | GPU advice |
| --- | --- | --- | --- |
| **BSc Computer Science** | 16 GB RAM, 512 GB SSD | 32 GB RAM, 1 TB SSD | A dedicated GPU is recommended, especially for graphics, game development, computer vision, parallel computing, and AI electives. It is not required for most core programming work. |
| **BSc Data Science and Artificial Intelligence** | 16 GB RAM, 512 GB SSD | 32 GB RAM, 1 TB SSD | For local deep learning, choose an NVIDIA GPU with at least 8 GB VRAM. 12 GB or more provides substantially more flexibility. |
| **MSc Artificial Intelligence** | 16 GB RAM, 512 GB SSD | 32 GB RAM, 1 TB SSD | An NVIDIA GPU with 8 GB VRAM is the practical entry point for local AI work; 12 GB or more is strongly preferred. Large training jobs will still require university or cloud compute. |
| **MSc Data Science for Decision Making** | 16 GB RAM, 512 GB SSD | 32 GB RAM, 1 TB SSD | Prioritise RAM and CPU performance for data processing, optimisation, notebooks, and containers. A dedicated GPU is optional unless electives, thesis work, or other projects involve deep learning. |
| **MSc Responsible Data Science** | 16 GB RAM, 512 GB SSD | 32 GB RAM, 1 TB SSD if the budget allows | Prioritise RAM, CPU performance, portability, battery life, and software compatibility. A dedicated GPU depends on electives, thesis work, and research direction. |
### Choosing the CPU
Choose a recent mid-range or better processor, such as an Intel Core Ultra 5/Core i5, AMD Ryzen 5, Apple M-series chip, or equivalent. Avoid Intel N-series, Celeron, Pentium, and similarly low-end processors in a new primary laptop.
A Core Ultra 7/Core i7, Ryzen 7, or higher-tier Apple chip can help with compilation, data processing, and CPU-based modelling, but do not overpay for the CPU if it forces you to accept too little RAM, storage, or GPU memory. Cooling also matters: a high-end processor in a very thin laptop may not sustain its advertised performance.
### Choosing a dedicated GPU
For BSc Computer Science, "dedicated GPU recommended" does not mean that any dedicated GPU is worth buying. Do not sacrifice RAM, storage, battery life, or build quality merely to obtain a weak GPU. If AI or GPU computing is the reason for buying one, an NVIDIA RTX GPU is generally preferable because of CUDA support.
For local AI work, use the following VRAM guidance:
- **Less than 8 GB:** suitable for light acceleration and small experiments, but difficult to recommend for a new AI-focused laptop
- **8 GB:** practical entry point for smaller training jobs, computer vision, and modest local inference
- **12 GB:** preferred starting point for meaningful local AI experimentation
- **16 GB or more:** desirable for larger models and workloads, although it still does not replace server hardware
A faster GPU with insufficient VRAM may be less useful for AI than a slower GPU with more VRAM. Always verify the VRAM and power limit of the exact laptop GPU; the product name alone is not enough.
### Apple configuration for local inference
For a MacBook, consider the unified-memory capacity separately from the minimum needed for coursework:
- **16 GB:** coursework, development, and light local inference
- **24-32 GB:** a practical range for local inference and moderate models
- **48-64 GB or more:** useful for larger quantised models that cannot fit in typical laptop VRAM
The operating system and applications share this memory with the GPU, so not all of it is available to the model. More unified memory can make a model possible to run, but does not make macOS compatible with CUDA-based software.
### Budget priority
If the budget cannot cover every upgrade, use this general order:
1. Meet the **16 GB RAM and 512 GB SSD minimum**.
2. Increase RAM to **32 GB**, particularly for DSAI and MSc programmes.
3. Increase storage to **1 TB**.
4. Add an appropriate dedicated GPU if your intended work benefits from it.
For an AI-focused purchase, GPU memory can move ahead of SSD capacity once the minimum storage requirement is met. Avoid buying a powerful GPU in a system with inadequate RAM, storage, cooling, or battery life.
## Final advice
Do not choose a laptop from CPU or GPU branding alone. Check the exact amount of RAM, SSD storage, GPU memory, power limits, upgradeability, battery life, and independent reviews of the complete model.
For most students, **16 GB RAM, a 512 GB SSD, and a recent mid-range processor are the floor**. If your budget allows one major upgrade, 32 GB RAM or a 1 TB SSD will often improve everyday student life more than a low-end dedicated GPU. Choose NVIDIA when local CUDA work is a priority; choose Apple silicon when portability, battery life, and local inference matter more than CUDA compatibility.
## Further reading
- [PyTorch MPS backend for Apple GPUs](https://docs.pytorch.org/docs/stable/notes/mps.html)
- [PyTorch XPU support for Intel GPUs](https://docs.pytorch.org/docs/stable/xpu.html)
- [AMD ROCm compatibility for Radeon and Ryzen](https://rocm.docs.amd.com/projects/radeon/en/latest/docs/compatibility.html)
- [NVIDIA CUDA GPU compatibility](https://developer.nvidia.com/cuda/gpus)

View file

@ -0,0 +1,75 @@
---
title: Linux Tricks
description: Linux-oriented tips for connecting to Maastricht University services.
---
This guide collects practical starting points for Linux users who need Maastricht University network and remote-access services. Distribution packages and university connection details can change, so obtain current hostnames, identity formats, and configuration profiles from UM before connecting.
## Eduroam
Most desktop environments can configure Eduroam through NetworkManager or their built-in Wi-Fi settings. Prefer a university-provided installer or configuration profile when one is available: it can set the expected authentication method and certificate validation safely.
When entering an institutional username or realm, copy the format from current UM instructions. Do not disable certificate validation merely to make the connection succeed, and do not save your password in a world-readable configuration file.
Useful background documentation:
- [NetworkManager](https://networkmanager.dev/docs/)
- [iwd](https://wiki.archlinux.org/title/Iwd)
- [WPA supplicant](https://wiki.archlinux.org/title/Wpa_supplicant)
## VPN and library access
[OpenConnect](https://www.infradead.org/openconnect/) supports several enterprise VPN protocols and is packaged by many Linux distributions. Install it through your distribution's package manager.
**Arch Linux:**
```sh
sudo pacman -S openconnect
```
**Debian, Ubuntu, and derivatives:**
```sh
sudo apt install openconnect
```
Use the VPN gateway, protocol, and username format supplied by UM. Avoid copying old hostnames from forum posts or archived instructions. Let the client prompt securely for credentials instead of placing a password in shell history or a script.
For journal and database access, the [Maastricht University Library](https://library.maastrichtuniversity.nl/) may offer browser-based institutional access that does not require a full VPN connection.
If UM supplies a graphical VPN client, use the vendor's current Linux package and the university's setup instructions. Package names and supported authentication methods vary by distribution.
## University file services
Linux can access SMB file services with tools such as `smbclient` and `mount.cifs`. Install the client packages with your distribution's package manager, then use the current server and share paths supplied by UM.
List shares without embedding a password in the command:
```sh
smbclient -L //server.example.edu -U your-username
```
For a temporary mount, create a mount point and let the mount command request credentials or use a protected credentials file:
```sh
sudo mkdir -p /mnt/um-drive
sudo mount -t cifs //server.example.edu/share /mnt/um-drive \
-o username=your-username,uid="$(id -u)",gid="$(id -g)"
```
Replace the example server and share with current institutional details. If you use a credentials file, restrict it to your account with `chmod 600`.
## Remote desktop
UM may provide browser-based or client-based access to a managed desktop environment. If a native Linux client is offered, install it from the official vendor or distribution repository and use the connection address from current university documentation.
A remote desktop can help when required software is available only on university-managed Windows systems, but performance and file-transfer behavior depend on your connection.
## Useful resources
- [Maastricht University Library](https://library.maastrichtuniversity.nl/)
- [ArchWiki networking documentation](https://wiki.archlinux.org/title/Network_configuration)
- [OpenConnect documentation](https://www.infradead.org/openconnect/manual.html)
- [Samba client documentation](https://www.samba.org/samba/docs/current/man-html/smbclient.1.html)
When troubleshooting, record the exact client, Linux distribution, authentication stage, and error message—but never publish passwords, recovery codes, private keys, or full authentication logs.

Some files were not shown because too many files have changed in this diff Show more