# ROOT_ V1 Prerequisite Map Phase 6F Step 6 — the full, real, server-enforced V1 prerequisite graph, generated from the live `courses`/`course_modules` tables (not hand-drawn), and the QA performed against it. ## Course-level graph ``` ROOT 01 → ROOT 02 → ROOT 03 → ROOT 04 → ROOT 05 → ROOT 06 ─┬─→ ROOT 07 ├─→ ROOT 09 ─┐ └─→ ROOT 10 ─┼─→ ROOT 12 → CAPSTONE │ (also requires ROOT 11 (bonus, │ ROOT 09 ─────┘ non-gating, jointly) after ROOT 10) ROOT 08 (Storage & Scheduling): excluded from V1 (visibility=draft). Its own prerequisite (ROOT 07-m03) still resolves if ever re-included, but nothing in V1 depends on it. ``` CAPSTONE's real prerequisite array is `['root-12-real-ops', 'root-09-kubernetes-security']` — both required, order-independent (see Step 7 decision in `ROOT_V1_CURRICULUM_MANIFEST.md`). ## Module-level cross-course edge `root-09-m03-network-and-supplychain-security`'s prerequisites include `root-07-m04-ingress-networkpolicy-foundations` (not just its own course's `root-09-m02`) — a real, intentional cross-course module dependency (network policy concepts build on Ingress concepts taught in ROOT 07). This is the one module-level edge that crosses a course boundary in the V1 graph. ## QA performed - **No cycles**: `infra/scripts/validate-course-content.js`'s cycle-detection pass (course-level and module-level, run separately) — 0 errors, confirmed after every migration this phase. - **No missing nodes**: every `prerequisites` array entry resolves to a real course/module id — same validator, "every course/module prerequisite id resolves to a real course/module" check. - **No excluded course blocking V1 progression**: ROOT 08 is referenced by nothing (audited by grep across every course's and module's `prerequisites` array before excluding it) — its `visibility=draft` change cannot silently lock anything downstream. - **No hidden prerequisite through ROOT 08/11**: confirmed the same way — grepped for `root-08` and `root-11-m01` inside every other course/module's `prerequisites` array; zero hits in either direction. - **Server-side enforcement**: `buildCourseTree()`'s `prerequisitesSatisfied()` is the single computation both the student and instructor views read — proven end to end for the full ROOT 01→02→03→04→05 chain in `e2e-phase6e.js` and for ROOT 06→09/10→12→Capstone in `e2e-phase6b.js` and `e2e-phase6f.js` (Step 31). - **Direct API cannot bypass**: `POST /api/student/activities/:id/complete` resolves the activity's own course+module through the same `buildCourseTree()` and 403s if either is locked (the Phase 6E fix) — re-verified this phase against the NEW root-capstone/root-09 edge specifically (a direct attempt to mark a `root-capstone-m02` activity complete before root-09 is done is rejected; see `e2e-phase6f.js`). - **Optional activities never unexpectedly block progression**: `is_required=false` activities are excluded from a module's/course's "required" set (`totals()`); confirmed for ROOT 09's own optional CKS-advanced labs and ROOT 08's excluded (draft) content — neither can block a V1 course from reaching `complete=true`.