From 2db136875f30809afa9c82f36e96605802bf7ee2 Mon Sep 17 00:00:00 2001 From: ced4568 Date: Wed, 26 Aug 2026 09:09:47 -0500 Subject: [PATCH] docs(phase6b): document curriculum expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHASE6B_ROOT_CURRICULUM_EXPANSION.md — the phase summary: what was authored per course, the is_required extension, the capstone unlock fix, verified scenario coverage before/after (44 -> 88 of 98, 89.8%, by direct query not estimate), and honest known limitations/Phase 6C recommendations. ROOT_CAPSTONE_READINESS.md — the readiness module and the server-side unlock fix in full detail, including why the module's completion and the real assessment's unlock gate are deliberately independent systems. Updated ROOT_CURRICULUM_INVENTORY.md (Phase 6B coverage numbers), ROOT_PROGRAM_ARCHITECTURE.md (ROOT 08/09 PARTIAL, new module lists per course), COURSE_CONTENT_MODEL.md (is_required completion rule, a new content-versioning section per this phase's own requirement), COURSE_AUTHORING_STANDARD.md (classifying near-duplicate/advanced scenarios), ENGINEERING_SOP.md (new Phase 6B section), REMOTE_AND_SELF_PACED_ARCHITECTURE.md (cross-reference), and platform/README.md (e2e-phase6b.js, the Phase 6B rehearsal, isolation- per-suite regression guidance). Co-Authored-By: Claude Sonnet 5 --- docs/COURSE_AUTHORING_STANDARD.md | 11 ++ docs/COURSE_CONTENT_MODEL.md | 44 ++++++ docs/ENGINEERING_SOP.md | 31 +++++ docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md | 155 +++++++++++++++++++++ docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md | 5 +- docs/ROOT_CAPSTONE_READINESS.md | 87 ++++++++++++ docs/ROOT_CURRICULUM_INVENTORY.md | 12 ++ docs/ROOT_PROGRAM_ARCHITECTURE.md | 23 +-- platform/README.md | 25 +++- 9 files changed, 377 insertions(+), 16 deletions(-) create mode 100644 docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md create mode 100644 docs/ROOT_CAPSTONE_READINESS.md diff --git a/docs/COURSE_AUTHORING_STANDARD.md b/docs/COURSE_AUTHORING_STANDARD.md index d62a2aa..7f45c0b 100644 --- a/docs/COURSE_AUTHORING_STANDARD.md +++ b/docs/COURSE_AUTHORING_STANDARD.md @@ -64,6 +64,17 @@ connects what was just taught to the incident the student is about to solve, wit the scenario's own hints/solution — the Lab's own progressive hints stay the only place the actual answer path lives. +### Classifying near-duplicate or advanced scenarios (Phase 6B) + +When two scenarios teach essentially the same concept (e.g. `pod-security-context` and +`cks-pod-security-context`), don't wire both as required core labs — pick the better-fitting one +as CORE and mark the other `is_required = false` as INDEPENDENT PRACTICE (see +`docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md`'s "duplicate concepts identified" for worked examples). +Likewise, a genuinely harder or more niche scenario (CKS-exam-style, a less commonly needed +skill) belongs as `is_required = false` GUIDED/ADVANCED practice, not force-fit into the +required core path just because it exists — see `docs/COURSE_CONTENT_MODEL.md`'s optional-vs- +required section for how this affects module completion. + ## 9. Knowledge check A handful of self-check questions with answers shown inline (not a graded exam — that's what the diff --git a/docs/COURSE_CONTENT_MODEL.md b/docs/COURSE_CONTENT_MODEL.md index df8fd47..2c0f796 100644 --- a/docs/COURSE_CONTENT_MODEL.md +++ b/docs/COURSE_CONTENT_MODEL.md @@ -84,6 +84,16 @@ decided, and it never invents new state for anything that's independently verifi Course and module completion are pure aggregates over their activities' completion (`total`/ `completed`/`pct`/`complete` — see `totals()`), never separately stored. +### Optional vs. required activities (Phase 6B) + +`module_activities.is_required` (defaults `true`) lets a module distinguish CORE teaching from +GUIDED/INDEPENDENT-practice duplicates and ADVANCED/optional extras — see +`docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md`'s classification scheme, used throughout ROOT 09. A +module's `complete` flag is true once every REQUIRED activity is done; an incomplete optional +activity never blocks it, though `total`/`pct` still count every activity, required or not. +Every Phase 6A activity has `is_required = true` (the column's default) — this is purely +additive, nothing that was complete before Phase 6B becomes incomplete now. + ## 5. Prerequisite/locking rule A course or module lists `prerequisites` (an array of other course/module ids). It's locked if @@ -109,6 +119,40 @@ stop, regardless of its `status`. Use `draft` for genuinely unfinished authoring want a student to see at all yet; use `status = planned` for an honest "not built yet" that's fine to be visible. +## 6b. Content versioning (Phase 6B §21) + +Follows the exact same convention `assessment_definitions`/`programs` already established — a +plain incrementing `version` integer, bumped by hand, informational rather than enforced: + +- **A typo-only edit** (fixing a spelling mistake, a broken link, rewording a sentence without + changing its meaning) does NOT require a version bump — nothing about what a student is being + taught or held to changed. +- **A semantic edit** (changing a learning objective, swapping a lab's `scenario_id`, changing + `completion_behavior`, adding/removing/reordering activities in a way that changes what + "complete" means) SHOULD bump `version` on the affected row(s) — see + `023_phase6b_curriculum.sql`'s own `version = version + 1` on every row it semantically + changes (resequencing, prerequisite changes, `is_required` changes). +- **What happens to existing completions**: nothing, automatically. Unlike an assessment + session (which snapshots the definition version it was drawn against, because a session is a + point-in-time timed attempt), a course/module/activity's completion is derived LIVE every time + it's read (`course-progress.js`) from the CURRENT row plus the student's progress/achievements/ + activity_completions — there is no snapshot to invalidate. A `manual_marker` completion + (`activity_completions`) is keyed by `activity_id`, which is stable across a version bump + (the id doesn't change, only its content/metadata does) — so a student's prior "I did this" + record stays valid even after a semantic edit, exactly like editing a scenario's own + Markdown description doesn't retroactively un-complete it in `progress`. +- **Scenario references across versions**: a `lab` activity's `scenario_id` is just a string + reference into `scenarios/data/*.json` — if that scenario file itself changes, the activity + automatically reflects the new content next time it's loaded (same as every other consumer of + `curriculum.js`'s cache). If an activity's `scenario_id` itself is changed to point at a + DIFFERENT scenario, that's a semantic edit (bump the version) and — depending on intent — may + warrant a new `activity_id` instead of mutating the existing one, so a student's prior + completion of the OLD scenario isn't silently reinterpreted as completion of the new one. +- **What this deliberately does NOT build**: no content-revision history table, no diffing, no + "students who saw v1 vs v2" cohort tracking. If a future phase needs that level of rigor + (e.g. for compliance/audit reasons), it's a real, separate feature — not something to bolt + onto a plain integer column. + ## 7. Why this doesn't touch Phase 1-5 systems No column was added to `scenarios/*.json`, `assessment_definitions`, `achievements`, diff --git a/docs/ENGINEERING_SOP.md b/docs/ENGINEERING_SOP.md index 9de084e..6b66d73 100644 --- a/docs/ENGINEERING_SOP.md +++ b/docs/ENGINEERING_SOP.md @@ -461,3 +461,34 @@ Full design: `docs/PHASE6A_ROOT_COURSE_FOUNDATION.md`, plus `docs/ROOT_CURRICULU the gold module's real lesson content, launches a real Lab, the Lab's own `setup_commands` stand up the broken deployment, a real `kubectl patch` fixes it, and the module completes and unlocks the next one) — both against a live stack. + +## 18. ROOT_ Curriculum Authoring & Coverage Expansion (Phase 6B) + +Full design: `docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md`, plus +`docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md` and `docs/ROOT_CAPSTONE_READINESS.md`. + +- **What it is:** 13 new teaching modules across ROOT 05-10/12/Capstone + (`023_phase6b_curriculum.sql`), built entirely from the 98-scenario inventory Phase 6A already + catalogued — no new scenarios needed. ROOT 08/09 moved from PLANNED to PARTIAL; scenario + coverage in structured curriculum went from 44/98 to 88/98 (89.8%), verified by direct query, + not estimated. 3 modules (plus the pre-existing gold standard) reach the full READY bar; every + other new module is honestly PARTIAL, missing a demonstration or guided exercise, documented + per-module rather than glossed over. +- **`is_required`** (new `module_activities` column, defaults `true`): distinguishes CORE + teaching from GUIDED/INDEPENDENT-practice duplicates and ADVANCED/optional extras — a module + is complete once every REQUIRED activity is done; an incomplete optional one never blocks it. + Every Phase 6A activity is unaffected (defaults to required). +- **The capstone server-side unlock fix:** `POST /api/assessments/:id/start` never checked + `unlock_rule` before this phase — a direct API call could start the capstone before its + prerequisite stages were complete, even though the UI never exposed that path. Fixed by + binding to the same `computeCurriculumState()` gate `GET /api/capstone` already used. See + `docs/ROOT_CAPSTONE_READINESS.md` §4 for the full story, including a pre-existing gap this + fix exposed in `e2e-phase5b.js`'s own capstone test (fixed alongside). +- **Tested, not just designed:** `platform/test/e2e-phase6b.js` (24 checks: new-module + hierarchy, prerequisite chains through the newly-inserted front-door modules, the + optional-vs-required completion rule, the capstone bypass fix in both directions, persistence/ + isolation/RBAC/draft-hiding proven against the new content) and + `infra/scripts/drills/test-phase6b-root-course-rehearsal.js` (11 checks, real k3s: a student + walks a brand-new module — ROOT 07 Services & Service Discovery — end to end including a real + `kubectl expose` against a live cluster, plus an instructor narrative proving Curriculum view, + Prepare Labs, and classroom tooling all still work against the expanded curriculum). diff --git a/docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md b/docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md new file mode 100644 index 0000000..a1ac313 --- /dev/null +++ b/docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md @@ -0,0 +1,155 @@ +# Phase 6B — ROOT_ Curriculum Authoring & Coverage Expansion + +**Status: PHASE 6B COMPLETE. 13 NEW TEACHING MODULES AUTHORED ACROSS ROOT 05-10/12/CAPSTONE. +CAPSTONE SERVER-SIDE UNLOCK ENFORCEMENT FIXED. VALIDATED AGAINST A REAL LAB.** + +Phase 6A built the course/module/activity content model and one gold-standard module. Phase 6B +is the primary content-authoring phase: real, substantial teaching content across the +Kubernetes/operations portion of `root_`, built strictly from the existing 98-scenario +inventory — no new scenarios were needed or created (see §7). + +## 1. What was authored + +13 new modules across 7 courses, all evidence-mapped in `docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md`: + +| Course | New module(s) | Depth | +|---|---|---| +| ROOT 05 | Kubernetes Mental Model & Core Objects | **READY** — full gold-standard pattern | +| ROOT 06 | Deployments, Scaling & Updates | **READY** — full gold-standard pattern | +| ROOT 06 | Jobs, CronJobs & DaemonSets | PARTIAL — lesson + knowledge check + labs | +| ROOT 07 | Services & Service Discovery | **READY** — full gold-standard pattern | +| ROOT 07 | Ingress & NetworkPolicy Foundations | PARTIAL — lesson + knowledge check + labs | +| ROOT 08 | Storage Fundamentals | PARTIAL — lesson + knowledge check + labs | +| ROOT 08 | Scheduling & Resource Governance | PARTIAL — lesson + knowledge check + labs | +| ROOT 09 | Security Model & SecurityContext | **READY** — full gold-standard pattern + guided/independent/advanced practice | +| ROOT 09 | ServiceAccounts & RBAC | PARTIAL — lesson + knowledge check + labs | +| ROOT 09 | Network & Supply Chain Security | PARTIAL — lesson + knowledge check + labs | +| ROOT 09 | Cluster Hardening & Runtime Security | PARTIAL — survey-depth lesson + labs | +| ROOT 10 | The RootNode Troubleshooting Framework | PARTIAL — lesson + demonstration + knowledge check + labs | +| ROOT 12 | Incident-response framing added to the existing Real Ops module | PARTIAL — framework lesson + reflection wrapping the existing incidents | +| ROOT Capstone | Capstone Readiness | **READY** — orientation module, see `docs/ROOT_CAPSTONE_READINESS.md` | + +3 modules reach **READY** by the honest bar in `docs/COURSE_AUTHORING_STANDARD.md` (introduction, +objectives, concept lesson, demonstration, knowledge check, guided practice, lab, reflection, +instructor notes, estimated duration, all present) — plus the pre-existing gold-standard Health +Probes module, unchanged. Every other new module is honestly **PARTIAL**: real lesson content +and real labs, missing one or more of demonstration/guided-exercise (documented per-module in the +coverage matrix, not glossed over). + +ROOT 08 and ROOT 09 moved from **PLANNED** to **PARTIAL** at the course level — real content now +exists where Phase 6A left them structurally empty. + +## 2. The is_required extension (Phase 6B, not Phase 6A) + +`module_activities.is_required` (new column, defaults `true` — every Phase 6A activity is +unaffected) lets a module distinguish CORE activities from GUIDED/INDEPENDENT-practice +duplicates and ADVANCED/optional extras (ROOT 09's CKS-style content, mostly). A module is +"complete" once every REQUIRED activity is done — an incomplete optional activity never blocks +it, though it still counts toward the displayed percent. See `docs/COURSE_CONTENT_MODEL.md`'s +updated completion-rules section. + +## 3. The capstone server-side unlock fix + +Full detail in `docs/ROOT_CAPSTONE_READINESS.md` §4. Summary: `POST /api/assessments/ +:definitionId/start` previously never checked `unlock_rule` — a direct API call could start the +capstone before its prerequisite stages were genuinely complete, even though the UI never +exposed a way to trigger this. Fixed by binding to the same `computeCurriculumState()` gate +`GET /api/capstone` already used. Found while fixing this: `e2e-phase5b.js`'s own capstone test +had been passing only because of this exact gap (it never completed the `real-ops` stage before +attempting to start) — fixed alongside the platform code, not routed around. + +## 4. Content quality bar applied honestly + +No module was marked READY merely for having activity rows, headings, or scenario references. +The ROOT 08/09 storage/scheduling/security modules are real, substantive lessons with real +knowledge checks — but without a demonstration or guided exercise, they're PARTIAL, not READY, +by this project's own stated standard. See the coverage matrix for the specific gap in each. + +## 5. Scenario coverage before/after + +Verified by direct query against `module_activities.scenario_id`, not estimated. + +Before Phase 6B: 44 of 98 scenarios wired into structured curriculum (all via Phase 6A's +mechanical mapping of `rootnode-path.json`'s existing stages). + +After Phase 6B: **88 of 98 scenarios (89.8%) now in structured curriculum** — 44 newly wired +this phase: `deployment-rollback` (added to ROOT 06 m02 as hands-on rollback practice +alongside its lesson), `job-task`, `cronjob-task` (ROOT 06 m03); `nodeport-task`, +`dns-resolution-mcq` (ROOT 07 m01), `ingress-task`, `network-policy` (ROOT 07 m04); +`emptydir-pod`, `pv-pvc-mount`, `pvc-dynamic-task`, `storageclass-mcq` (ROOT 08 m01), +`node-label-selector`, `node-taint-toleration`, `pod-affinity-mcq`, `priority-class-task`, +`pod-disruption-budget-task`, `resource-quota-ns`, `limitrange-task` (ROOT 08 m02); +`cks-non-root-enforce`, `cks-drop-capabilities`, `cks-readonly-filesystem`, +`cks-pod-security-context`, `cks-seccomp-profile`, `cks-apparmor-profile`, +`pod-security-context` (ROOT 09 m01); `serviceaccount-pod`, `cks-automount-token`, `rbac-role`, +`cks-rbac-clusterrole`, `cks-rbac-least-privilege`, `rbac-clusterrole` (ROOT 09 m02); +`cks-network-policy`, `cks-egress-namespace`, `cks-tls-ingress`, `cks-image-pull-secret`, +`networkpolicy-egress`, `cks-network-policy-metadata` (ROOT 09 m03); `cks-psa-namespace`, +`cks-immutable-secret`, `cks-runtime-class`, `cks-audit-policy` (ROOT 09 m04); `crashloop-fix`, +`endpoint-fix-task` (ROOT 10 m03), `container-logging-mcq` (ROOT 10 m02, existing bank). + +**10 scenarios remain unwired**, each with a specific, documented reason rather than an +oversight: `cluster-upgrade-mcq`, `etcd-backup-mcq` (ROOT 11/DevOps territory, explicitly out of +Phase 6B's authoring scope); 7 CKS-style MCQs classified as ASSESSMENT BANK material +(`cks-mcq-api-server`, `cks-mcq-audit-policy`, `cks-mcq-kubelet-auth`, `cks-mcq-psp-replacement`, +`cks-mcq-runtime-security`, `cks-mcq-sandboxing`, `cks-mcq-image-footprint`) — recall-style +questions better suited to a future structured exam pool than hands-on core teaching, flagged +for that future use rather than silently dropped; and `readiness-probe-task` — a real, unused +candidate for independent practice in ROOT 06 or ROOT 10 that this phase simply didn't get to, +honestly left for Phase 6C rather than force-mapped at the last minute. Quality of mapping, not +percentage, drove every placement decision — see the coverage matrix's "duplicate concepts +identified" section for how near-duplicate scenarios were handled deliberately rather than both +force-fit into "core." + +## 6. Knowledge-check and guided-exercise approach + +Every new knowledge check uses scenario-interpretation and reasoning questions ("A Deployment +has three replicas but the Service has zero endpoints — which part of the system should you +inspect first, and why?"), never "what command was shown two paragraphs ago." Every guided +exercise bridges lesson to lab without revealing the lab's answer — modeled directly on the +existing gold module's own guided exercise. No new knowledge-check engine capability was needed; +the existing Markdown + manual-marker model was sufficient throughout. + +## 7. Why no new scenarios were created + +Every learning objective this phase authored had a suitable existing scenario — the 54-scenario +unused bank Phase 6A inventoried was exactly the content Phase 6B needed. No objective was found +that genuinely required new hands-on lab content with no existing scenario able to serve it, so +no new `rootnode-*` scenario was created this phase (see the authorization's explicit +justification bar in its §17 — none of the three required conditions were met for any specific +gap identified during authoring). + +## 8. What Phase 6B deliberately did not build + +Per the authorization's explicit scope line: `init_`/`compile_` curriculum, deep authoring of +ROOT 01-04 or ROOT 11 (both remain structurally PLANNED, as Phase 6A left them — genuinely no +scenario content exists for either), a Parent Portal, payments, public registration, an AI +tutor, chat/video/voice, proctoring, a mobile app, certificates, a multi-host scheduler, +production deployment changes, an LMS/CMS, a WYSIWYG authoring system, or complex drip +scheduling. The Provisioner, Gateway, Lab Router, student/instructor authentication, enrollment +architecture, and notification architecture were not redesigned — the one fix made +(capstone unlock) was the smallest correct change to the assessment engine's existing route, +not a redesign of the engine itself. + +## 9. Test results + +- `e2e-phase6b.js`: **24 passed, 0 failed.** +- `test-phase6b-root-course-rehearsal.js` (real k3s Lab, real `kubectl expose`, real classroom + tooling): **11 passed, 0 failed.** +- Full regression (2A/2B/3A/3B/5A/5B/5C/6A), each run in isolation per the convention Phase 6A + established — see the final report for the run-by-run result and the two pre-existing test + gaps found and fixed along the way (in `e2e-phase5b.js` and `e2e-phase6a.js`, both caused by + this phase's own intentional, documented behavior changes, not by a defect). + +## 10. Known limitations / recommended Phase 6C scope + +- ROOT 06/07/08/09/10's PARTIAL modules are missing a demonstration and/or guided exercise — + the natural next authoring pass, module by module, using the 3 READY modules as templates. +- ROOT 09's 7 ASSESSMENT-BANK-classified MCQs are inventoried but not yet wired into an actual + assessment definition. +- ROOT 11 (DevOps & Platform Engineering) remains entirely PLANNED — no scenario content exists + anywhere in this repository for CI/CD, IaC, or GitOps concepts; a future phase would need to + either author new `rootnode-*` scenarios for it or explicitly scope it out of `root_`. +- The Capstone Readiness module's completion is intentionally independent of the real capstone's + unlock gate (see `docs/ROOT_CAPSTONE_READINESS.md` §3) — worth revisiting only if a future + phase has a concrete reason to couple them, not as a default assumption. diff --git a/docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md b/docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md index 53f26b0..94d47f4 100644 --- a/docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md +++ b/docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md @@ -91,7 +91,10 @@ this flow specifically — see the Phase 4.1 report for the full run results. activity tree (lessons, demonstrations, knowledge checks, guided exercises, labs, reflections, assessments/capstone) instead of just a bare scenario id list — the same tree an instructor-led classroom student sees, consumed through the identical `buildCourseTree()`. - See `docs/COURSE_CONTENT_MODEL.md` and `docs/PHASE6A_ROOT_COURSE_FOUNDATION.md`. + See `docs/COURSE_CONTENT_MODEL.md` and `docs/PHASE6A_ROOT_COURSE_FOUNDATION.md`. **Populated + with real teaching content across most of the Kubernetes/operations courses in Phase 6B** — + see `docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md`. The architecture itself (this closed gap) + didn't change; what a self-paced student actually finds inside it did. ## What Phase 4.1 deliberately did NOT do here diff --git a/docs/ROOT_CAPSTONE_READINESS.md b/docs/ROOT_CAPSTONE_READINESS.md new file mode 100644 index 0000000..d4b97b2 --- /dev/null +++ b/docs/ROOT_CAPSTONE_READINESS.md @@ -0,0 +1,87 @@ +# ROOT Capstone Readiness + +Phase 6B §9/§10 — the instructional runway that prepares a student for the existing +`rootnode-capstone` assessment, and the server-side fix that makes its unlock rule actually +enforced. This document does not redesign the capstone engine itself (`assessments.js`, +`013_assessments.sql`) — see `docs/PHASE3B_LEARNING_AND_ASSESSMENT.md` for that. + +## 1. The Capstone Readiness module + +`root-capstone-m02-capstone-readiness` (sequenced BEFORE the real capstone module, +`root-capstone-m01-operate-and-recover`, within the `root-capstone` course) — 4 activities: + +1. **Lesson — What the Capstone Measures & How the Draw Works.** Explains the required-vs-random + scenario draw, what "unlocked" means, and what completion records. Never publishes the exact + scenario IDs in the current pool or any answer. +2. **Lesson — Time Management & Troubleshooting Strategy Under a Clock.** Budgeting the 90 + minutes, reusing the ROOT 10/ROOT 12 frameworks under pressure, and when to move on from a + stuck scenario. +3. **Knowledge check — Readiness Check: Are You Actually Ready?** A genuine self-assessment + (not a graded quiz) against concrete criteria, pointing back to specific ROOT_ modules for + any "no" answer. +4. **Reflection — What Completion Means.** Connects passing the Capstone to a durable, + demonstrated skill rather than a completion checkbox. + +This module derives its own completion the same way every other lesson-only module does +(`activity_completions`, `manual_marker`) — it is intentionally NOT a prerequisite the real +capstone assessment engine checks before allowing a start (see §3 below for why these two gates +are deliberately independent). + +## 2. What was NOT built + +No answers, no exact scenario IDs from the current pool, no capstone-specific hint content +beyond what the Lab's own progressive hints already provide. No new assessment engine, no +second capstone, no readiness "score." This is orientation content, not a shadow exam. + +## 3. Why readiness-module completion and the real unlock gate are independent + +The real capstone start (`POST /api/assessments/rootnode-capstone/start`) is gated by the +curriculum path's own `unlock_rule` — all 6 content stages of `rootnode-path.json` complete +(Foundation, Workloads, Networking, Configuration, Troubleshooting, Real Ops) — computed by +`computeCurriculumState()` (`curriculum-rules.js`), the exact same function `GET /api/capstone` +already used to display lock state. Whether a student has viewed or completed the Capstone +Readiness module has NO bearing on this gate. + +This is deliberate, not an oversight: hard-locking the real assessment behind a CONTENT-MODEL +module (course_modules/module_activities) would mean the security-critical enforcement boundary +lives in two different systems that could drift out of sync — the curriculum-path unlock_rule +(Postgres-backed, achievement-derived, already proven) and the course-content prerequisite chain +(newer, additive, built for teaching sequencing rather than security enforcement). Keeping them +independent means: the readiness module is free to be completed early, late, or skipped by a +student who's already prepared some other way, while the real gate remains exactly as strict as +`docs/PHASE3B_LEARNING_AND_ASSESSMENT.md` originally specified. + +## 4. The server-side unlock enforcement fix (Phase 6B §10) + +**The gap** (documented in `docs/PHASE6A_ROOT_COURSE_FOUNDATION.md` §10): `POST /api/assessments/ +:definitionId/start` (`learning-routes.js`) checked only that the assessment definition existed +and was `status = 'active'` — it never checked `unlock_rule` at all. `GET /api/capstone` +correctly computed and displayed lock state, and the student UI never exposed a "start" action +while locked, but a direct POST to the start route bypassed the check entirely. Student UI state +was the only thing standing between a student and an early capstone attempt. + +**The fix**: the start route now resolves whether the requested definition is the curriculum +path's OWN configured capstone (`path.stages.find(s => s.type === 'capstone' && s +.capstone_assessment_id === definition.assessment_definition_id)`), and if so, computes the +exact same `computeCurriculumState()` gate `/api/capstone` already relies on. A `locked` gate +now returns a clean `403 { error: 'locked', message, blocking_stage_ids }` — the same +`blocking_stage_ids` shape `/api/capstone` already exposes, so a client integrating against this +route gets consistent, already-documented error shape rather than a new one. No internal state +beyond the stage ids that are actually incomplete is ever returned. + +**What is NOT gated**: any assessment definition the curriculum path does NOT name as its +capstone (e.g. `rootnode-quick-assessment`) is completely unaffected — the check only ever +triggers for the one definition the path itself points at via `capstone_assessment_id`. + +**What remains valid**: no existing `assessment_sessions` row, no achievement, no enrollment +completion record was touched by this fix — it only affects a NEW start attempt going forward. +A student who already legitimately completed the capstone before this fix keeps that record +exactly as it was. + +**Regression coverage**: `platform/test/e2e-phase6b.js` proves both directions — a direct start +attempt before prerequisites are met is rejected (`403 locked`), and a legitimately unlocked +attempt (all 6 stages genuinely complete) still succeeds and completes normally. A pre-existing +gap was also found and fixed in `e2e-phase5b.js`'s own capstone-completion test, which had never +included the `real-ops` stage in its completion loop — it was passing only because the very +bug this phase fixes let an incomplete curriculum still start the capstone. See that test file's +own comment for detail. diff --git a/docs/ROOT_CURRICULUM_INVENTORY.md b/docs/ROOT_CURRICULUM_INVENTORY.md index 2306564..f12c285 100644 --- a/docs/ROOT_CURRICULUM_INVENTORY.md +++ b/docs/ROOT_CURRICULUM_INVENTORY.md @@ -207,3 +207,15 @@ ROOT 07/08/09/10 can draw on in Phase 6B, not content that needs to be invented: - No DevOps/CI/CD/IaC/GitOps scenario content exists (ROOT 11 is necessarily 100% PLANNED). - No Linux-fundamentals, Git, or Docker-specific scenario content exists (ROOT 01-04 are necessarily 100% PLANNED) — everything in `scenarios/data/` is Kubernetes/CKS content. + +## 8. Phase 6B update — scenario coverage after curriculum authoring + +Phase 6B wired 44 more of the scenarios above into structured curriculum (verified by direct +query against `module_activities.scenario_id`, not estimated): **88 of 98 scenarios (89.8%)** +are now in structured curriculum, up from 44 at the end of Phase 6A. The remaining 10 — +`cluster-upgrade-mcq`, `etcd-backup-mcq` (ROOT 11 territory), 7 CKS-style MCQs classified as +future ASSESSMENT BANK material, and `readiness-probe-task` (a genuine, documented gap for +Phase 6C) — are listed with their specific reasons in +`docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md` §5. The full course-by-course mapping, including +which scenarios were classified CORE/GUIDED/INDEPENDENT/ADVANCED/ASSESSMENT-BANK and why, lives +in `docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md` — this file is intentionally not duplicated there. diff --git a/docs/ROOT_PROGRAM_ARCHITECTURE.md b/docs/ROOT_PROGRAM_ARCHITECTURE.md index 7a3cb1d..6d41394 100644 --- a/docs/ROOT_PROGRAM_ARCHITECTURE.md +++ b/docs/ROOT_PROGRAM_ARCHITECTURE.md @@ -9,6 +9,11 @@ All 13 rows below are `courses` under the ONE existing program, `root-kubernetes (`programs` table, unchanged — see §"why one program, not thirteen" below). Every status claim here is grounded in `docs/ROOT_CURRICULUM_INVENTORY.md`, not aspirational. +**Updated for Phase 6B** (`platform/api/migrations/023_phase6b_curriculum.sql`, +`docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md`): ROOT 08 and 09 moved from PLANNED to PARTIAL, and +6 courses gained new teaching modules — the module-level detail and evidence for every new +placement lives in `docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md`, not duplicated here. + ## Course sequence | # | Course | Status | Why | @@ -17,15 +22,15 @@ here is grounded in `docs/ROOT_CURRICULUM_INVENTORY.md`, not aspirational. | 2 | Linux Administration & Networking | **PLANNED** | Same — no Linux-admin scenario content exists. | | 3 | Git & Collaborative Development | **PLANNED** | No Git scenario content exists. | | 4 | Containers & Docker | **PLANNED** | No Docker-specific scenario content exists (the Lab itself runs k3s, but no student-facing "learn Docker" scenarios exist). | -| 5 | Kubernetes Foundations | **PARTIAL** | Real content: the existing "Foundation" stage's 20 scenarios, wrapped as one module (`root-05-m01-pods-and-kubectl-basics`). No teaching lesson content authored yet — bare labs only. | -| 6 | Kubernetes Application Workloads | **PARTIAL** | The existing "Workloads" stage's 8 scenarios, one module. Bare labs only. | -| 7 | Kubernetes Networking & Configuration | **PARTIAL** | The existing "Networking" (2) and "Configuration" (5) stages, two modules. Bare labs only. | -| 8 | Kubernetes Storage & Scheduling | **PLANNED** | Real scenario content exists (PV/PVC, StorageClass, taints/tolerations, PDB — see inventory §5) but is not yet wired into a module. Deliberately left PLANNED rather than fabricating a module shell with no honest content behind it — Phase 6B. | -| 9 | Kubernetes Security | **PLANNED** | A substantial 22-scenario CKS-style bank exists (RBAC, PSA, network policy, supply chain, runtime hardening) but is not yet wired into a module — Phase 6B. | -| 10 | Kubernetes Troubleshooting & Observability | **PARTIAL** | Contains the Phase 6A gold-standard module (READY) plus the existing "Troubleshooting" stage's remaining 5 scenarios as a second, bare-lab module. | -| 11 | DevOps & Platform Engineering Foundations | **PLANNED** | No CI/CD/IaC/GitOps scenario content exists anywhere in this repository. | -| 12 | RootNode Real Ops | **PARTIAL** | The existing "RootNode: Real Ops" stage's 4 scenarios (3 RootNode-authored + 1 generic), one module. Bare labs, no teaching content — the gold module already gives one of these four (`rootnode-probe-port-mismatch`) a full teaching treatment elsewhere; this module exercises it again alongside the other three, deliberately (see module notes below). | -| 13 | ROOT Capstone — Operate and Recover a Production-Like Environment | **PARTIAL** | The existing, fully working `rootnode-capstone` assessment (90 min, mixed required/random pool). No capstone orientation/briefing lesson authored yet. | +| 5 | Kubernetes Foundations | **PARTIAL** | Phase 6B added a READY front-door module (`root-05-m00-kubernetes-mental-model` — full lesson/demo/knowledge-check/guided-exercise/lab/reflection) sequenced before the existing 20-scenario bare-lab bank (`root-05-m01`), which now sits behind it. | +| 6 | Kubernetes Application Workloads | **PARTIAL** | Phase 6B added a READY `root-06-m02-deployments-scaling-updates` and a PARTIAL `root-06-m03-jobs-cronjobs-daemonsets`, both in front of the existing bare-lab bank. | +| 7 | Kubernetes Networking & Configuration | **PARTIAL** | Phase 6B added a READY `root-07-m03-services-and-service-discovery` and a PARTIAL `root-07-m04-ingress-networkpolicy-foundations`. The existing configuration bank (`root-07-m02`) still has no authored teaching content — an honest, documented gap. | +| 8 | Kubernetes Storage & Scheduling | **PARTIAL** | Phase 6B wired the real scenario content the inventory identified (PV/PVC, StorageClass, taints/tolerations, PDB, quotas/limits) into two new PARTIAL modules — moved off PLANNED for the first time. | +| 9 | Kubernetes Security | **PARTIAL** | Phase 6B built the 22-scenario CKS-style bank into four modules (Security Model & SecurityContext — READY; ServiceAccounts & RBAC, Network & Supply Chain Security, Cluster Hardening & Runtime — all PARTIAL), classifying scenarios CORE/GUIDED/INDEPENDENT/ADVANCED/ASSESSMENT-BANK rather than exposing them as 22 disconnected tasks — moved off PLANNED. | +| 10 | Kubernetes Troubleshooting & Observability | **PARTIAL** | The Phase 6A gold-standard module (READY, unmodified) plus a new PARTIAL `root-10-m03-troubleshooting-framework` (the generalized Observe/Evidence/Hypothesis/Test/Repair/Validate/Prevent framework) and the existing bare-lab bank, now sequenced last. | +| 11 | DevOps & Platform Engineering Foundations | **PLANNED** | No CI/CD/IaC/GitOps scenario content exists anywhere in this repository — unchanged, out of Phase 6B's authoring scope. | +| 12 | RootNode Real Ops | **PARTIAL** | Phase 6B added an incident-response framing lesson and a post-incident reflection wrapping the existing 4-scenario module — the incidents themselves are unchanged, still deliberately un-guided. | +| 13 | ROOT Capstone — Operate and Recover a Production-Like Environment | **PARTIAL** | Phase 6B added a READY Capstone Readiness module (orientation, no answers) sequenced before the existing capstone assessment module — see `docs/ROOT_CAPSTONE_READINESS.md`. | No course is marked READY at the course level in Phase 6A — that's honest: a course only earns READY once every module in it has real teaching content, and Phase 6A deliberately built exactly diff --git a/platform/README.md b/platform/README.md index 0db01b9..c09aea9 100644 --- a/platform/README.md +++ b/platform/README.md @@ -14,7 +14,9 @@ entrypoint/Dockerfile unmodified; frontend bundle gained Phase 3B learning UX, s (Phase 5A), self-paced programs/enrollment + the website integration boundary (Phase 5B), and a provider-neutral enrollment-communications pipeline (activation/enrollment-confirmation/ password-reset email, Phase 5C), the ROOT_ course/module/activity content model layered over - curriculum/assessments (Phase 6A), and the instructor/admin control plane. No k3s, no + curriculum/assessments (Phase 6A) with real teaching content across most of the Kubernetes + courses and a server-side capstone unlock enforcement fix (Phase 6B), and the instructor/admin + control plane. No k3s, no `--privileged` — this replaced Phase 2A's "identity" instance, which was a full Lab image booting an unused k3s cluster just to serve these APIs. Design: `docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md` (split + Postgres migration), @@ -24,7 +26,8 @@ entrypoint/Dockerfile unmodified; frontend bundle gained Phase 3B learning UX, s `docs/PHASE5B_SELF_PACED_ENROLLMENT.md` (programs, enrollment, website integration boundary), `docs/PHASE5C_ENROLLMENT_COMMUNICATIONS.md` (notifications, email delivery), `docs/PHASE6A_ROOT_COURSE_FOUNDATION.md` (course/module/activity content model, gold-standard - module). Operational reference: `api/README.md`. + module), `docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md` (curriculum authoring across ROOT + 05-10/12/Capstone, the capstone unlock fix). Operational reference: `api/README.md`. - `gateway/` — the only student-facing path to a Lab (HTTP + WebSocket proxy, environment-aware routing derived from a server-controlled session — not a client-suppliable header/param — and, as of Phase 3B, server-side gating of progressive hint reveal). Design: @@ -88,15 +91,25 @@ further instructor accounts from the Instructors tab). - `test/e2e-phase6a.js` — course/module/activity hierarchy and ordering, prerequisite-based locking, all three completion behaviors (manual marker/scenario/assessment-derived), resume, cross-student isolation, RBAC for self-paced (no-cohort) students, and draft-content hiding. +- `test/e2e-phase6b.js` — hierarchy for the 13 new modules across ROOT 05-10/12/Capstone, + prerequisite chains through the newly-inserted front-door modules, the optional-vs-required + (`is_required`) completion rule, and the capstone server-side unlock fix in both directions + (a direct bypass attempt rejected, a legitimately unlocked attempt still accepted). All e2e suites need `OWNER_EMAIL` / `OWNER_PASSWORD` set to a real bootstrapped instructor account (see "Running the full stack locally" above) — the old `ADMIN_PASSWORD` mechanism is gone. They're slow (real k3s boots, `e2e-phase3b.js` also runs a real API restart) and need the -full stack up. None of them substitute for the manual real-Lab-image checks (node-pty, terminal +full stack up. Run each suite against its own freshly-reset stack, not chained one after another +against the same Postgres — several routes (the admin dashboard's running-lab count, a course's +percent-complete) read GLOBAL state on purpose, and chaining suites produces false failures from +shared state, not real regressions (found and documented during Phase 6A's own regression pass). +None of them substitute for the manual real-Lab-image checks (node-pty, terminal WebSocket, `kubectl`, scenario validation) in `docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md` §8 — -those need a browser-equivalent client and aren't automated here. The one exception is Phase -6A's gold-standard module, which DOES get a real-Lab dress rehearsal — -`infra/scripts/drills/run-phase6a-course-rehearsal.sh`. +those need a browser-equivalent client and aren't automated here. Two exceptions get a real-Lab +dress rehearsal instead: Phase 6A's gold-standard module +(`infra/scripts/drills/run-phase6a-course-rehearsal.sh`) and Phase 6B's curriculum expansion, +including a real `kubectl expose` and a real instructor/classroom narrative +(`infra/scripts/drills/run-phase6b-root-course-rehearsal.sh`). ## Known limitations