Restructure student tracks by cross-reference chains, add project rules

Student tracks restructured based on document cross-reference analysis:
- Track 1 "Gemalen & CRC Keten": Grace→Kevin→Robin→Adriaan (chain)
- Track 2 "Condition Monitoring": Kevin→Yamai (fork from Track 1)
- Track 3 "Zuivering & Beluchting": Noa‖Elise (parallel, same problem)
- Track 4 "EVOLV Reactor Model": Pieter (standalone, no student cross-refs)
- Track 5 "IoT & ICT Infra": Bart→Fontys ICT→Fontys TOGAF (chain)

Inter-student dependencies added (7 new):
- Grace→Kevin, Kevin→Robin, Robin→Adriaan (Track 1 chain)
- Kevin→Yamai (Track 2 fork)
- Bart→Fontys ICT→Fontys TOGAF (Track 5 chain)

CLAUDE.md: Added "Metro Map Data Rules" section documenting:
- Thema=line, Project=station, speerpunt=track grouping
- Student work rules: cross-references determine track sharing
- Identified chains with evidence (reference counts from source docs)
- Grid alignment requirements

MetroCanvas: Truncate long station labels to prevent overlap (max ~20 chars
based on grid spacing, with ellipsis)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-04-08 14:19:16 +02:00
parent 28d7ac008f
commit 9f033835cd
3 changed files with 148 additions and 44 deletions

View File

@@ -59,6 +59,37 @@ The project wiki lives in `wiki/` and follows the wiki-template schema. It is th
**After making significant changes:** update relevant wiki pages, `knowledge-graph.yaml`, and `wiki/log.md`.
## Metro Map Data Rules
The metro map is the primary navigation and knowledge structure. These rules govern how content maps to the visual metro metaphor.
### Thema = Metro Line (Dim 1)
Each thema is a horizontal metro line at the strategy level. Keep themas to major organizational concerns:
- **R&D Planning 2026** themes (Architectuur, Productiewaardig, Lab, Governance)
- **Onderzoek** — all research work (R&D-driven and student)
### Project = Station (Dim 1)
Each project is a station on its thema's line. Stations are zoomable into Dim 2.
### Student Work Rules
Student projects live under the **Onderzoek** thema. Structure:
1. **Each student gets their own track (metro line) within the Onderzoek thema** — unless they build on another student's work
2. **Students who build on each other share a track** — chronological stations on the same line, earlier work first
3. **Cross-references determine track grouping**, not topic similarity. Evidence: explicit mentions of another student's work, reuse of their system/data, or continuation of their project
4. **Identified chains** (from cross-reference analysis of source documents):
- **Gemalen & CRC track**: Grace van Geel (pump curves) → Kevin Haest (CRC/IOT2050, references Grace) → Robin van Weelie (D.R.A.I.N., references CRC) → Adriaan Sol (pilot 3, references D.R.A.I.N.)
- **Monitoring branch** (forks from Kevin): Kevin Haest → Yamai van Rooij (CRC 78x, MCSA, references Kevin)
- **Zuivering track**: Noa de Groot (beluchting) ‖ Elise Nouws (algen, parallel approach to same problem)
- **EVOLV Reactor**: Pieter van der Wilt (standalone — EVOLV ASM model, no student cross-refs)
- **IoT & Infra track**: Bart van Gool (IoT-hub) → Fontys ICT (Docker/CI-CD/SOC) → Fontys TOGAF (security)
5. **Dependencies to 2026 projects** are tracked as `Afhankelijkheid` records (type: technisch/data/infrastructuur)
### Kennis Artikelen
Standalone knowledge articles extracted from research. Not tied to a project lifecycle. Used by the AI/search engine for semantic retrieval.
### Grid Alignment
All node positions must be multiples of GRID=50. Server-side: `MapDataService::snapToGrid()`. Client-side: `gridConstants.js`.
## What Requires Human Validation
- Architecture decisions
- Domain model changes

View File

@@ -601,50 +601,71 @@ class DatabaseSeeder extends Seeder
'status' => 'open',
]);
// ══════════════════════════════════════════════════════════════
// 10. STUDENTEN — Thema met afstudeer-/stageprojecten
// ══════════════════════════════════════════════════════════════════
// 10. ONDERZOEK — Research thema with student tracks
//
// Source: raw/*.docx, raw/*.pdf, raw/*.pptx
// ══════════════════════════════════════════════════════════════
// Track grouping based on cross-reference analysis of source docs.
// Students who build on each other share a track (same speerpunt).
// See CLAUDE.md "Metro Map Data Rules > Student Work Rules".
// ══════════════════════════════════════════════════════════════════
$themaStudenten = Thema::create([
'naam' => 'Studenten & Onderzoek',
'beschrijving' => 'Afstudeer-, stage- en groepsprojecten van studenten (Avans, Fontys, TU/e) binnen het R&D-lab. Kennisbasis voor toekomstige innovaties.',
$themaOnderzoek = Thema::create([
'naam' => 'Onderzoek',
'beschrijving' => 'Afstudeer-, stage- en groepsprojecten (Avans, Fontys, TU/e). Tracks gegroepeerd op basis van cross-referenties: studenten die op elkaars werk voortbouwen delen een track.',
'prioriteit' => Prioriteit::Midden,
'periode_start' => '2021-07-01',
'periode_eind' => '2026-12-31',
]);
// --- Speerpunten per domein ---
// --- Speerpunten = tracks based on cross-reference chains ---
// See CLAUDE.md "Student Work Rules" for evidence
$spGemalenPompen = Speerpunt::create([
'thema_id' => $themaStudenten->id,
'naam' => 'Gemalen & Pompen',
'beschrijving' => 'Studentenonderzoek naar rioolgemalen, pompefficiëntie, regelsoftware en digitale tweelingen voor pompsystemen.',
// TRACK 1: Gemalen & CRC chain
// Grace (pump curves, refs CRC 18x) → Kevin (CRC/IOT2050, refs Grace) → Robin (D.R.A.I.N., refs CRC 3x) → Adriaan (pilot 3, refs D.R.A.I.N.)
$spGemalenCRC = Speerpunt::create([
'thema_id' => $themaOnderzoek->id,
'naam' => 'Gemalen & CRC Keten',
'beschrijving' => 'Ketensamenwerking: pompefficiëntie → CRC edge computing → regelsoftware → pilot gemaalketen. Grace → Kevin → Robin → Adriaan.',
'eigenaar_id' => $rene->id,
'status' => SpeerpuntStatus::Actief,
]);
// TRACK 2: Monitoring branch (forks from Kevin's CRC)
// Kevin → Yamai (CRC 78x, MCSA, explicitly references Kevin)
$spMonitoring = Speerpunt::create([
'thema_id' => $themaOnderzoek->id,
'naam' => 'Condition Monitoring (CRC→MCSA)',
'beschrijving' => 'Vertakking vanuit Kevin\'s CRC: Yamai bouwt voort met Motor Current Signal Analysis voor condition-based monitoring.',
'eigenaar_id' => $rene->id,
'status' => SpeerpuntStatus::Actief,
]);
// TRACK 3: Zuivering & Beluchting
// Noa (beluchting 24x) ‖ Elise (algen 289x, beluchting 92x) — parallel approaches to same problem
$spZuivering = Speerpunt::create([
'thema_id' => $themaStudenten->id,
'naam' => 'Zuiveringsproces & Biologie',
'beschrijving' => 'Onderzoek naar beluchtingsoptimalisatie, algenproductie, activated sludge modellen en biologische processen.',
'thema_id' => $themaOnderzoek->id,
'naam' => 'Zuivering & Beluchting',
'beschrijving' => 'Parallelle aanpak beluchtingsprobleem: Noa (mechanisch model) en Elise (biologisch via algen). Geen directe cross-refs maar zelfde onderzoeksvraag.',
'eigenaar_id' => $rene->id,
'status' => SpeerpuntStatus::Actief,
]);
$spEdgeArch = Speerpunt::create([
'thema_id' => $themaStudenten->id,
'naam' => 'EDGE & Architectuur',
'beschrijving' => 'Studentenonderzoek naar edge computing, IoT-hubs, Carbon Reducing Controller, condition-based monitoring.',
'eigenaar_id' => $pim->id,
// TRACK 4: EVOLV Reactor (standalone)
// Pieter (EVOLV 25x, reactor 55x) — no cross-refs to other students
$spReactor = Speerpunt::create([
'thema_id' => $themaOnderzoek->id,
'naam' => 'EVOLV Reactor Model',
'beschrijving' => 'Standalone masterstage: ASM reactor model voor EVOLV. Geen cross-refs naar andere studenten, directe EVOLV-bijdrage.',
'eigenaar_id' => $rene->id,
'status' => SpeerpuntStatus::Actief,
]);
$spIctSecurity = Speerpunt::create([
'thema_id' => $themaStudenten->id,
'naam' => 'ICT & Security',
'beschrijving' => 'Groepsprojecten over Docker, CI/CD, SOC-implementatie en TOGAF security architectuur voor edge-apparaten.',
// TRACK 5: IoT & Infrastructuur
// Bart (IoT-hub, EDGE) → Fontys ICT (Docker/CI-CD/SOC) → Fontys TOGAF (EDGE security)
$spIoTInfra = Speerpunt::create([
'thema_id' => $themaOnderzoek->id,
'naam' => 'IoT & ICT Infrastructuur',
'beschrijving' => 'Infrastructuurketen: Bart (IoT-hub integratielaag) → Fontys ICT (Docker/CI-CD/SOC) → Fontys TOGAF (security architectuur). Alle drie raken edge-infra.',
'eigenaar_id' => $pim->id,
'status' => SpeerpuntStatus::Actief,
]);
@@ -653,7 +674,7 @@ class DatabaseSeeder extends Seeder
// Adriaan Sol — Pilot 3 rioolgemaalketen
$pAdriaan = $this->createProject([
'speerpunt_id' => $spGemalenPompen->id,
'speerpunt_id' => $spGemalenCRC->id,
'naam' => 'Ontwerp Pilot 3 Rioolgemaalketen',
'beschrijving' => 'Afstudeerstage Avans Chemische Technologie. Ontwerp van pilot 3 rioolgemaalketen bij RWZI Nieuwveer. Student: Adriaan Sol (2154257).',
'eigenaar_id' => $rene->id,
@@ -678,7 +699,7 @@ class DatabaseSeeder extends Seeder
// Robin van Weelie — D.R.A.I.N.
$pRobin = $this->createProject([
'speerpunt_id' => $spGemalenPompen->id,
'speerpunt_id' => $spGemalenCRC->id,
'naam' => 'D.R.A.I.N. Regelsoftware Rioolgemalen',
'beschrijving' => 'Afstudeeronderzoek naar data-gedreven en energie-efficiënte aansturing rioolgemalen. Technische analyse Hooge- en Lage Zwaluwe, invloed kelderhoogte en drukverschil op energetisch gedrag.',
'eigenaar_id' => $rene->id,
@@ -715,7 +736,7 @@ class DatabaseSeeder extends Seeder
// Grace van Geel — Pompefficiëntie
$pGrace = $this->createProject([
'speerpunt_id' => $spGemalenPompen->id,
'speerpunt_id' => $spGemalenCRC->id,
'naam' => 'Efficiëntie-curve Centrifugaalpompen',
'beschrijving' => 'Afstudeeronderzoek Avans naar invloed van voordruk, verschildruk en parallel werkende pompen op de efficiëntie-curve van centrifugaalpompen. Student: Grace van Geel.',
'eigenaar_id' => $rene->id,
@@ -787,8 +808,9 @@ class DatabaseSeeder extends Seeder
]);
// Pieter van der Wilt — Reactor model EVOLV
// TRACK 4: EVOLV Reactor (standalone, no student cross-refs)
$pPieter = $this->createProject([
'speerpunt_id' => $spZuivering->id,
'speerpunt_id' => $spReactor->id,
'naam' => 'Non-Ideal ASM Reactor Model (EVOLV)',
'beschrijving' => 'Masterstage: ontwikkeling van een niet-ideaal activated sludge reactor model voor het EVOLV-framework. Focus op computational setup, reaction model, transfer terms. Student: Pieter van der Wilt.',
'eigenaar_id' => $rene->id,
@@ -813,7 +835,7 @@ class DatabaseSeeder extends Seeder
// Kevin Haest — Carbon Reducing Controller
$pKevin = $this->createProject([
'speerpunt_id' => $spEdgeArch->id,
'speerpunt_id' => $spGemalenCRC->id,
'naam' => 'Carbon Reducing Controller (Siemens IOT2050)',
'beschrijving' => 'Afstudeerproject Avans E&T: CRC-systeem met edge computing op de Siemens IOT2050. Integratie IoT in afvalwaterzuivering. Samenwerking met Minor Procesautomatisering. Student: Kevin Haest.',
'eigenaar_id' => $rene->id,
@@ -838,8 +860,9 @@ class DatabaseSeeder extends Seeder
]);
// Yamai van Rooij — Condition-based monitoring + CRC + MCSA
// TRACK 2: Monitoring branch (forks from Kevin's CRC)
$pYamai = $this->createProject([
'speerpunt_id' => $spEdgeArch->id,
'speerpunt_id' => $spMonitoring->id,
'naam' => 'Condition-Based Monitoring & MCSA',
'beschrijving' => 'Stage Avans IE&M: verbetering operationele efficiëntie via CRC-systeem en Motor Current Signal Analysis. Innovatie van onderhoudspraktijken en energieverbruik reductie.',
'eigenaar_id' => $rene->id,
@@ -863,8 +886,9 @@ class DatabaseSeeder extends Seeder
]);
// Bart van Gool — IoT-hub integratielaag
// TRACK 5: IoT & Infrastructuur chain
$pBart = $this->createProject([
'speerpunt_id' => $spEdgeArch->id,
'speerpunt_id' => $spIoTInfra->id,
'naam' => 'IoT-hub Implementatie Integratielaag',
'beschrijving' => 'Afstudeerproject Avans TI: implementatie van een IoT-hub in de integratielaag van het waterschap. Koppeling R&D en integratieteam. Student: Bart van Gool. Begeleider: Pim Moerman.',
'eigenaar_id' => $pim->id,
@@ -890,7 +914,7 @@ class DatabaseSeeder extends Seeder
// Fontys ICT groep — Docker, CI/CD, SOC
$pFontysICT = $this->createProject([
'speerpunt_id' => $spIctSecurity->id,
'speerpunt_id' => $spIoTInfra->id,
'naam' => 'Fontys ICT Proftaak Docker & CI/CD & SOC',
'beschrijving' => 'Groepsopdracht Fontys ICT: (1) Docker Swarm & load balancing, (2) Geautomatiseerde CI/CD-pijplijn ter vervanging van USB-deployments, (3) SOC-implementatie voor procesautomatisering.',
'eigenaar_id' => $pim->id,
@@ -916,7 +940,7 @@ class DatabaseSeeder extends Seeder
// Fontys SOC/TOGAF groep
$pTOGAF = $this->createProject([
'speerpunt_id' => $spIctSecurity->id,
'speerpunt_id' => $spIoTInfra->id,
'naam' => 'TOGAF Security Architectuur Edge-Devices',
'beschrijving' => 'Fontys groepsproject: beveiligingsadvies op basis van TOGAF-methodiek + NIST Cybersecurity Framework voor edge-apparaten waterschap.',
'eigenaar_id' => $pim->id,
@@ -940,27 +964,72 @@ class DatabaseSeeder extends Seeder
'auteur_id' => $pim->id,
]);
// --- Dependencies: student projects that feed into 2026 work ---
// --- Inter-student dependencies (track chains) ---
// TRACK 1 chain: Grace → Kevin → Robin → Adriaan
Afhankelijkheid::create([
'project_id' => $pKevin->id,
'afhankelijk_van_project_id' => $pGrace->id,
'type' => 'data',
'beschrijving' => 'Kevin references Grace\'s pump efficiency curves (CRC 18x in Grace\'s report). CRC builds on pump characterization.',
'status' => 'opgelost',
]);
Afhankelijkheid::create([
'project_id' => $pRobin->id,
'afhankelijk_van_project_id' => $pKevin->id,
'type' => 'technisch',
'beschrijving' => 'Robin references CRC (3x) and uses Digital Twin approach enabled by Kevin\'s edge platform.',
'status' => 'opgelost',
]);
Afhankelijkheid::create([
'project_id' => $pAdriaan->id,
'afhankelijk_van_project_id' => $pRobin->id,
'type' => 'technisch',
'beschrijving' => 'Adriaan references D.R.A.I.N. (1x). Pilot 3 keten design builds on Robin\'s gemaal analysis.',
'status' => 'opgelost',
]);
// TRACK 2 branch: Kevin → Yamai
Afhankelijkheid::create([
'project_id' => $pYamai->id,
'afhankelijk_van_project_id' => $pKevin->id,
'type' => 'technisch',
'beschrijving' => 'Yamai\'s MCSA work directly extends Kevin\'s CRC system (CRC referenced 78x). Explicitly references Kevin.',
'status' => 'opgelost',
]);
// TRACK 5 chain: Bart → Fontys ICT → Fontys TOGAF
Afhankelijkheid::create([
'project_id' => $pFontysICT->id,
'afhankelijk_van_project_id' => $pBart->id,
'type' => 'infrastructuur',
'beschrijving' => 'Fontys ICT Docker/CI-CD work extends Bart\'s IoT-hub integration layer infrastructure.',
'status' => 'open',
]);
Afhankelijkheid::create([
'project_id' => $pTOGAF->id,
'afhankelijk_van_project_id' => $pFontysICT->id,
'type' => 'technisch',
'beschrijving' => 'TOGAF security architecture applies to the Docker/edge infrastructure set up by Fontys ICT.',
'status' => 'open',
]);
// --- Student → 2026 project dependencies ---
// Kevin's CRC work feeds into BRIDGE (edge computing proof)
Afhankelijkheid::create([
'project_id' => $bridge->id,
'afhankelijk_van_project_id' => $pKevin->id,
'type' => 'technisch',
'beschrijving' => 'BRIDGE bouwt voort op de CRC edge computing ervaring van Kevin Haest met de Siemens IOT2050.',
'beschrijving' => 'BRIDGE bouwt voort op Kevin\'s CRC edge computing met Siemens IOT2050.',
'status' => 'opgelost',
]);
// Robin's D.R.A.I.N. feeds into Gemaal 3.0 / Afvlakkingsregeling
Afhankelijkheid::create([
'project_id' => $gemaal->id,
'afhankelijk_van_project_id' => $pRobin->id,
'type' => 'data',
'beschrijving' => 'Gemaal 3.0 gebruikt de meetdata en analyse van Robin over gemalen Hooge-/Lage Zwaluwe.',
'beschrijving' => 'Gemaal 3.0 gebruikt Robin\'s meetdata en analyse gemalen Hooge-/Lage Zwaluwe.',
'status' => 'opgelost',
]);
// Pieter's reactor model is direct EVOLV contribution
Afhankelijkheid::create([
'project_id' => $gemaal->id,
'afhankelijk_van_project_id' => $pPieter->id,

View File

@@ -816,15 +816,19 @@ const renderDimension = (dimData, opacity, parentGroup) => {
.attr('opacity', 0.5)
.text('[○]')
// Station label
// Station label (truncated to prevent overlap, max ~20 chars)
nodeGroups.append('text')
.attr('x', 0)
.attr('y', 28)
.attr('text-anchor', 'middle')
.attr('fill', '#e8e8e8')
.attr('font-family', "'VT323', monospace")
.attr('font-size', '14px')
.text(d => d.name)
.attr('font-size', '13px')
.text(d => {
const name = d.name ?? ''
const maxLen = Math.max(12, Math.floor(GRID_STEP_X / 10))
return name.length > maxLen ? name.slice(0, maxLen - 1) + '…' : name
})
// Status badge
nodeGroups.filter(d => d.badge)