diff --git a/platform/api/migrations/029_phase6f_root_v1_integration.sql b/platform/api/migrations/029_phase6f_root_v1_integration.sql new file mode 100644 index 0000000..8e6bad5 --- /dev/null +++ b/platform/api/migrations/029_phase6f_root_v1_integration.sql @@ -0,0 +1,87 @@ +-- Phase 6F — ROOT_ V1 curriculum integration. No schema change: every decision below reuses +-- existing columns (visibility, status, is_required) exactly as COURSE_CONTENT_MODEL.md already +-- documents them. See docs/ROOT_V1_CURRICULUM_MANIFEST.md for the full audit and rationale. + +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +-- 1. The three instructor-led-only practice banks (Phase 6E's known limitation) become +-- OPTIONAL supplemental practice: every activity in them gets is_required = false. Nothing +-- downstream lists any of these three modules as its own prerequisite (confirmed by audit), so +-- this changes nothing about the prerequisite graph — only whether their labs count toward +-- their course's OWN completion. Reuses the exact is_required mechanism Phase 6B built; no new +-- module-level "optional" flag needed. A module with zero required activities can never become +-- "complete" (course-progress.js's totals(): `required.length > 0 && ...`), so these stay +-- visibly incomplete/optional rather than silently vanishing or looking done. +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +UPDATE module_activities SET is_required = false, version = version + 1, updated_at = now() + WHERE module_id IN ( + 'root-06-m01-deployments-and-workloads', + 'root-07-m01-services-and-networking', + 'root-10-m02-general-troubleshooting' + ); + +UPDATE course_modules SET + description = description || ' — OPTIONAL supplemental practice for ROOT_ V1: a bare scenario bank with no lesson/demonstration/knowledge-check framing, not required for course or self-paced progression. Suitable for instructor-assigned extra reps in an instructor-led cohort.', + version = version + 1, updated_at = now() + WHERE module_id = 'root-06-m01-deployments-and-workloads'; +UPDATE course_modules SET + description = description || ' — OPTIONAL supplemental practice for ROOT_ V1: a bare scenario bank with no lesson/demonstration/knowledge-check framing, not required for course or self-paced progression. Suitable for instructor-assigned extra reps in an instructor-led cohort.', + version = version + 1, updated_at = now() + WHERE module_id = 'root-07-m01-services-and-networking'; +UPDATE course_modules SET + description = description || ' — OPTIONAL supplemental practice for ROOT_ V1: a bare scenario bank with no lesson/demonstration/knowledge-check framing, not required for course or self-paced progression. Suitable for instructor-assigned extra reps in an instructor-led cohort.', + version = version + 1, updated_at = now() + WHERE module_id = 'root-10-m02-general-troubleshooting'; + +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +-- 2. Course-level status audit correction. courses.status describes authoring completeness and +-- is shown to students honestly (COURSE_CONTENT_MODEL.md §6) — it is a hand-maintained field, +-- not derived from module status. Auditing it against actual module-level status (Step 0's +-- explicit "do not rely only on prior reports" instruction) found it had drifted out of date +-- for six courses: every module in each had already reached 'ready' status in an earlier phase +-- (root-05, root-09) or now does once its bare-practice-bank module is reclassified optional +-- above (root-06, root-07, root-10), and root-12/root-capstone's modules were already all ready. +-- No new content was authored for this correction — it is a status-field fix, not a curriculum +-- claim change. +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +UPDATE courses SET status = 'ready', version = version + 1, updated_at = now() + WHERE course_id IN ( + 'root-05-kubernetes-foundations', + 'root-06-kubernetes-application-workloads', + 'root-07-kubernetes-networking-config', + 'root-09-kubernetes-security', + 'root-10-kubernetes-troubleshooting-observability', + 'root-12-real-ops', + 'root-capstone' + ); + +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +-- 3. ROOT 08 (Storage & Scheduling) is excluded from ROOT_ V1. It sits at sequence 8 but is NOT +-- required by anything downstream (root-09/root-10 both branch directly off root-06, by design +-- — see ROOT_PROGRAM_ARCHITECTURE.md's "deviations from the suggested sequence"), so leaving it +-- visible mid-sequence with only 2 PARTIAL bare-lab modules would look to a self-paced student +-- like a course they "skipped," when in fact V1 was never designed to require it. visibility = +-- 'draft' is the existing hard gate (COURSE_CONTENT_MODEL.md §6): excluded from every +-- student-facing query, still visible/manageable for instructors. Reversible in a future phase +-- once ROOT 08 gets the same authoring pass ROOT 01-04 got. +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +UPDATE courses SET + visibility = 'draft', + description = description || ' [Excluded from ROOT_ V1 — see docs/ROOT_V1_CURRICULUM_MANIFEST.md. Not required by any V1 course; deferred to a future phase.]', + version = version + 1, updated_at = now() + WHERE course_id = 'root-08-kubernetes-storage-scheduling'; +UPDATE course_modules SET visibility = 'draft', version = version + 1, updated_at = now() + WHERE course_id = 'root-08-kubernetes-storage-scheduling'; + +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +-- 4. ROOT 11 (DevOps & Platform Engineering Foundations) stays INCLUDED in V1, but explicitly +-- repositioned as a single-module conceptual bridge/orientation, not a promise of a full +-- DevOps course. It stays visibility = 'published' (real, finished, self-paced-ready content: +-- lesson + knowledge check + reflection) and status = 'partial' (honest — six more modules are +-- designed but not authored). Nothing downstream requires it (confirmed by audit), so it never +-- blocks Real Ops/Capstone; a student who skips it loses nothing gating-wise, only the framing +-- lesson itself. +-- ═══════════════════════════════════════════════════════════════════════════════════════════ +UPDATE courses SET + description = 'A single orientation module bridging Linux/Kubernetes systems administration to platform-engineering thinking. This is intentionally NOT a full DevOps & Platform Engineering course for ROOT_ V1 — CI/CD, GitOps, and Infrastructure-as-Code modules are designed (docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md) but require lab infrastructure not yet built, and are explicitly out of scope for this phase (see docs/ROOT_V1_CURRICULUM_MANIFEST.md). Optional: not required to reach Real Ops or the Capstone.', + version = version + 1, updated_at = now() + WHERE course_id = 'root-11-devops-platform-engineering'; diff --git a/platform/api/migrations/030_phase6f_capstone_gate_correction.sql b/platform/api/migrations/030_phase6f_capstone_gate_correction.sql new file mode 100644 index 0000000..a96c632 --- /dev/null +++ b/platform/api/migrations/030_phase6f_capstone_gate_correction.sql @@ -0,0 +1,53 @@ +-- Phase 6F — self-caught correction to 029_phase6f_root_v1_integration.sql, applied before +-- 029 was ever pushed. Auditing the pre-existing rootnode-path.json Capstone unlock_rule (a +-- separate, older, stage-based gate — see docs/ROOT_PROGRAM_ARCHITECTURE.md's "Real Ops and +-- Capstone placement") against 029's is_required=false change found that root-06-m01, +-- root-07-m01, and root-10-m02 are not generic supplemental practice: their scenario_ids are +-- the LITERAL "workloads"/"networking"/"troubleshooting" stage content that gate whether the +-- Capstone assessment can even be started, independent of this course layer's own prerequisite +-- chain. Making them optional at the course layer would have let a self-paced student reach a +-- course-tree-complete state while remaining silently, confusingly blocked from the Capstone — +-- exactly the "confusing gap" ROOT_V1_CURRICULUM_MANIFEST.md's audit exists to catch. +-- +-- Fix: keep them REQUIRED (revert 029's is_required=false), and instead give each module the +-- minimal self-paced framing it was actually missing — one short intro lesson explaining what +-- the bank is and why it's structured differently — so root-06/07/10 earn 'ready' honestly +-- through real (if minimal) authoring rather than through a reclassification that would have +-- broken Capstone reachability. See docs/PHASE6F_ROOT_V1_PILOT_READINESS.md. + +UPDATE module_activities SET is_required = true, version = version + 1, updated_at = now() + WHERE module_id IN ( + 'root-06-m01-deployments-and-workloads', + 'root-07-m01-services-and-networking', + 'root-10-m02-general-troubleshooting' + ); + +UPDATE module_activities SET sequence = sequence + 1, version = version + 1, updated_at = now() + WHERE module_id IN ( + 'root-06-m01-deployments-and-workloads', + 'root-07-m01-services-and-networking', + 'root-10-m02-general-troubleshooting' + ); + +INSERT INTO module_activities (activity_id, module_id, sequence, activity_type, title, content_path, scenario_id, assessment_definition_id, completion_behavior, estimated_duration_minutes, status) VALUES +('root-06-m01-a00-lab-bank-intro', 'root-06-m01-deployments-and-workloads', 1, 'lesson', 'Practice Bank: Deployments & Workloads', + 'content/root/root-06-kubernetes-application-workloads/m01-deployments-and-workloads/00-lab-bank-intro.md', NULL, NULL, 'manual_marker', 4, 'ready'), +('root-07-m01-a00-lab-bank-intro', 'root-07-m01-services-and-networking', 1, 'lesson', 'Practice Bank: Services & Networking', + 'content/root/root-07-kubernetes-networking-config/m01-services-and-networking/00-lab-bank-intro.md', NULL, NULL, 'manual_marker', 3, 'ready'), +('root-10-m02-a00-lab-bank-intro', 'root-10-m02-general-troubleshooting', 1, 'lesson', 'Practice Bank: General Troubleshooting Operations', + 'content/root/root-10-kubernetes-troubleshooting-observability/m02-general-troubleshooting/00-lab-bank-intro.md', NULL, NULL, 'manual_marker', 3, 'ready') +ON CONFLICT (activity_id) DO NOTHING; + +UPDATE course_modules SET + status = 'ready', + description = replace(description, ' — OPTIONAL supplemental practice for ROOT_ V1: a bare scenario bank with no lesson/demonstration/knowledge-check framing, not required for course or self-paced progression. Suitable for instructor-assigned extra reps in an instructor-led cohort.', ' — a fast, low-scaffolding practice bank reinforcing the teaching modules right before it (see its own intro lesson); required, and load-bearing for the pre-existing Capstone stage gate.'), + version = version + 1, updated_at = now() + WHERE module_id IN ( + 'root-06-m01-deployments-and-workloads', + 'root-07-m01-services-and-networking', + 'root-10-m02-general-troubleshooting' + ); + +-- root-06/07/10 were correctly bumped to 'ready' in 029 on the assumption their one remaining +-- gap module would become optional; that gap is now closed by real (minimal) authoring instead, +-- so the course-level 'ready' status stands — no revert needed here, just re-confirming intent. diff --git a/platform/api/migrations/031_phase6f_capstone_security_prerequisite.sql b/platform/api/migrations/031_phase6f_capstone_security_prerequisite.sql new file mode 100644 index 0000000..0bbcfbf --- /dev/null +++ b/platform/api/migrations/031_phase6f_capstone_security_prerequisite.sql @@ -0,0 +1,39 @@ +-- Phase 6F — Capstone gate review (Step 7). Decision: STRENGTHEN, do not weaken. +-- +-- Evidence gathered before deciding: +-- - rootnode-capstone's own scenario pool (assessment_definitions.pool_spec) is 100% +-- deployments/config/troubleshooting/real-ops scenarios — zero overlap with +-- rootnode-security-assessment's CKS-style pool (SecurityContext, RBAC, NetworkPolicy, +-- image supply chain). A student can currently reach and pass the Capstone having never +-- touched a single security scenario. +-- - ROOT 09 (Kubernetes Security) only reached genuine course-level 'ready' status THIS +-- phase (see 029_phase6f_root_v1_integration.sql) — Phase 6B/6C left it PARTIAL, so +-- requiring it any earlier would have been irresponsible (gating on unfinished content). +-- - root-09 and root-12 are already sibling courses (both prerequisite = root-06), so adding +-- root-09 alongside root-12 as a root-capstone prerequisite imposes no new sequencing +-- burden — a student can do them in either order or in parallel, they simply both must be +-- done before the Capstone course unlocks. +-- - For the intended ROOT_ audience (aspiring DevOps/cloud/platform/infrastructure +-- engineers), shipping a "graduation" capstone with zero security competency check is a +-- real, professionally-relevant gap, not a cosmetic one. +-- +-- What this changes: ONLY the course-layer `root-capstone.prerequisites` array (the same +-- mechanism/column every other course-level gate in this program already uses — no schema +-- change). What this deliberately does NOT change: the pre-existing, older +-- `rootnode-path.json` "stages_complete" unlock_rule that gates actually STARTING the +-- rootnode-capstone assessment itself (foundation/workloads/networking/configuration/ +-- troubleshooting/real-ops) — that legacy mechanism has no natural "security stage" without +-- new scenario-bank content-authoring work, which is out of Phase 6F's scope. The two gates +-- already "agree by construction, not by shared implementation" (ROOT_PROGRAM_ARCHITECTURE.md); +-- this migration keeps that true by only ever making the course-layer gate a superset, +-- never a subset, of what the assessment-layer gate already requires. +-- +-- Result: a student must now complete ROOT 09 (Security) AND ROOT 12 (Real Ops) — both already +-- required individually, now also jointly required — before the root-capstone COURSE is +-- unlocked, in addition to the pre-existing six-stage scenario gate on the assessment itself. +-- See docs/ROOT_V1_CURRICULUM_MANIFEST.md and platform/test/e2e-phase6f.js for verification. + +UPDATE courses SET + prerequisites = ARRAY['root-12-real-ops', 'root-09-kubernetes-security'], + version = version + 1, updated_at = now() + WHERE course_id = 'root-capstone'; diff --git a/platform/api/migrations/032_phase6f_root_v1_orientation.sql b/platform/api/migrations/032_phase6f_root_v1_orientation.sql new file mode 100644 index 0000000..399f314 --- /dev/null +++ b/platform/api/migrations/032_phase6f_root_v1_orientation.sql @@ -0,0 +1,32 @@ +-- Phase 6F Steps 9-11 — ROOT_ V1 program + Lab orientation. No orientation content existed +-- anywhere in the curriculum before this (audited by grep across content/root — see +-- docs/PHASE6F_ROOT_V1_PILOT_READINESS.md); a self-paced student's very first touch of the +-- platform was ROOT 01's Linux lesson with no framing of what ROOT_ is, how the Learn->Practice-> +-- Lab->Troubleshoot->Validate loop works, or what happens when a Lab is recreated. Added as a +-- new required module at the front of ROOT 01 — the exact same "front-door module" pattern +-- root-05-m00 already established (a real module, not a special orientation type), so no new +-- activity type or mechanism was needed. + +UPDATE course_modules SET sequence = sequence + 1, version = version + 1, updated_at = now() + WHERE course_id = 'root-01-computing-linux-foundations'; + +-- root-01-m01's own prerequisite now points at the new orientation module instead of {} — it +-- was previously the first module in the course with nothing to require. +UPDATE course_modules SET prerequisites = ARRAY['root-01-m00-welcome-to-root'], version = version + 1, updated_at = now() + WHERE module_id = 'root-01-m01-how-computers-run-software'; + +INSERT INTO course_modules (module_id, course_id, title, description, sequence, objectives, prerequisites, estimated_duration_minutes, status) VALUES +('root-01-m00-welcome-to-root', 'root-01-computing-linux-foundations', 'Welcome to ROOT_', + 'What ROOT_ is, who it is for, how the Learn/Practice/Lab/Troubleshoot/Validate loop works, and what to expect from your disposable Lab environment before your first hands-on module.', + 1, ARRAY['Describe the full ROOT_ course sequence and what each stage builds on', 'Explain why a Lab environment is disposable while academic progress is not', 'Know how to use hints, reset a broken Lab, and request help in an instructor-led session'], + ARRAY[]::text[], 15, 'ready') +ON CONFLICT (module_id) DO NOTHING; + +INSERT INTO module_activities (activity_id, module_id, sequence, activity_type, title, content_path, scenario_id, assessment_definition_id, completion_behavior, estimated_duration_minutes, status) VALUES +('root-01-m00-a01-lesson-welcome', 'root-01-m00-welcome-to-root', 1, 'lesson', 'Welcome to ROOT_', + 'content/root/root-01-computing-linux-foundations/m00-welcome-to-root/01-lesson-welcome-to-root.md', NULL, NULL, 'manual_marker', 6, 'ready'), +('root-01-m00-a02-lesson-disposable-lab', 'root-01-m00-welcome-to-root', 2, 'lesson', 'Your Disposable Lab', + 'content/root/root-01-computing-linux-foundations/m00-welcome-to-root/02-lesson-your-disposable-lab.md', NULL, NULL, 'manual_marker', 6, 'ready'), +('root-01-m00-a03-knowledge-check', 'root-01-m00-welcome-to-root', 3, 'knowledge_check', 'Knowledge Check: Welcome to ROOT_', + 'content/root/root-01-computing-linux-foundations/m00-welcome-to-root/03-knowledge-check.md', NULL, NULL, 'manual_marker', 3, 'ready') +ON CONFLICT (activity_id) DO NOTHING;