Adds PHASE6D_ROOT_FOUNDATIONS.md (the phase's own record: the generic-scenario-engine finding, the tested container-runtime decision, what was authored, the bugs found and fixed, and honest test/regression/rehearsal results) and ROOT_FOUNDATIONS_PILOT_READINESS.md (module-by-module READY/DEFERRED classification for ROOT 01-04 against the unchanged Phase 6C readiness standard — ROOT 01 READY, ROOT 02-04 DEFERRED by design). Adds ROOT_PROGRAM_DURATION_MODEL.md — real per-course instructional-hour totals computed from actual estimated_duration_minutes values (2,931 minutes / 48.9 hours across all authored content), not invented, with an explicit note on why instructor-led timing isn't a separate summable field yet. Updates ROOT_PROGRAM_ARCHITECTURE.md (ROOT 01's new READY status and the real 01-04 prerequisite chain), ROOT_PILOT_CURRICULUM.md (ROOT 01 added to the pilot, module-level detail), and ROOT_CURRICULUM_COVERAGE_MATRIX.md (updated module/scenario totals) to reflect what Phase 6D actually built, without restating Phase 6C's own Kubernetes-scenario tables. Co-Authored-By: Claude Sonnet 5 <[email protected]>
8.3 KiB
Phase 6D — ROOT_ Systems Foundations
Phase 6D's mission: build the foundational half of ROOT_ (ROOT 01-04) so a student reaches ROOT 05 already fluent in Linux/networking/Git and understanding why Kubernetes exists, using the existing course/module/activity model throughout.
Baseline verification
Confirmed at start: branch main, clean tree, HEAD = origin/main =
bd0a10fbfb31b05a6264d083f27d85a6f7d5d50c (the Phase 6C milestone).
The central architectural finding (Section 6/7)
backend/server.js's scenario engine (runCommand()) executes an arbitrary shell command
string from a scenario's setup_commands/validation.commands and checks its output — nothing
about it is Kubernetes-specific. This means the existing lab activity type and
scenarios/data/*.json format already fully support Linux and Git exercises with real,
deterministic validation. No shell_lab/git_lab/container_lab activity type was built —
the speculative multi-type model the authorization raised as a possibility was unnecessary.
See docs/ROOT_FOUNDATIONS_LAB_STRATEGY.md.
Container runtime decision, tested not assumed (Section 10)
Installed Docker Engine (docker.io + a static docker-compose v2 binary) inside the same
already-Privileged: true Lab container, alongside k3s. This does not weaken the security
boundary — the Lab Host's own Docker socket is never touched; the nested daemon has its own
socket inside the guest. Verified empirically against the real production resource caps (2
vCPU, 4096MB, 512 PIDs): k3s + dockerd + a running nginx:alpine container + a real image
build together used 884MB/4096MB and 160/512 PIDs, comfortable headroom in all dimensions.
Dockerfile and scripts/entrypoint.sh updated accordingly (dockerd starts non-blockingly
alongside k3s; a Docker daemon failure never breaks Kubernetes-only Labs).
ROOT 01 — Computing & Linux Foundations: fully authored, READY
6 modules, 35 activities, 29 new content files, 6 new hands-on scenario files
(linux-inspect-hardware, linux-shell-basics, linux-filesystem-navigation,
linux-files-and-text, linux-process-management, linux-permissions-foundations) plus 2 MCQ
scenarios, all using the existing generic scenario/validation engine — no new mechanism. Every
lab was tested end-to-end via the real scenario API (setup → solve → validate), including
negative tests confirming they correctly FAIL when not actually solved. A new
rootnode-linux-foundations-assessment (2 required + 4 random-draw scenarios) uses the existing
Phase 3B engine.
ROOT 02-04: architecture designed, content deferred
docs/ROOT_FOUNDATIONS_ARCHITECTURE.md designs all remaining 20 modules (6+6+8) in full —
outcomes, activities, and Real Ops connections for each. Given realistic effort constraints,
authoring, testing, and verifying ROOT 01 to genuine READY depth was prioritized over shallow
architecture-only coverage across all four courses (see
docs/ROOT_FOUNDATIONS_PILOT_READINESS.md for the full reasoning). ROOT 02-04 remain honestly
status = 'planned' with zero modules — not padded to look further along than they are.
Course prerequisite chain (Section 19)
courses.prerequisites now encodes ROOT 01→02→03→04→05 server-side, verified live: a fresh
student sees ROOT 02 correctly LOCKED (ROOT 01 is real, non-empty content) while ROOT 03/04/05
stay unlocked (vacuously satisfied — their own prerequisite courses are still empty). Completing
ROOT 01 unlocks ROOT 02 for that student. ROOT 06+ and the Capstone are completely unaffected
(re-confirmed via regression). No UI-only hiding — this is a real courses table constraint the
existing prerequisitesSatisfied() engine already enforced correctly before this phase; Phase
6D only populated real data into it.
Extension point for placement/override (Section 20)
Documented, not built: courses.prerequisites is a plain array a future placement assessment or
instructor override could satisfy without a schema change. No bypass button implemented.
Frontend (Sections 21-23)
No frontend change was needed or made. CourseView.jsx and CurriculumView.jsx already
generalized correctly to ROOT 01's activity/status shapes (lesson/demonstration/knowledge_check/
guided_exercise/lab/reflection/assessment, ready/planned status, is_required,
instructor_notes) — the same generic rendering already proven for ROOT 05-12 in Phase 6C.
Verified by inspection and by the real API responses e2e-phase6d.js and the rehearsal exercise.
New application/platform code and why necessary
Dockerfile+scripts/entrypoint.sh: the nested Docker Engine described above — a genuine platform gap (ROOT 04's course design needs it) that the existing content model couldn't satisfy on its own, tested and justified indocs/ROOT_FOUNDATIONS_LAB_STRATEGY.md.- No other application code. Every ROOT 01 requirement was satisfiable with the existing
course_modules/module_activities/assessment_definitions/scenario-engine model.
Bugs found and fixed
- An early draft of the real-Lab rehearsal's hardware-report step used a triple-nested
JS-template/shell/heredoc string that silently produced a malformed file. Caught by actually
running the rehearsal against a real Lab (not assumed to work); fixed by writing the script to
a file and
docker cp-ing it in rather than an inline heredoc. - Phase 6D's own content changes made two hardcoded assumptions in
e2e-phase6a.js/e2e-phase6b.jsstale: both suites assertedresumepointed at ROOT 05's gold module as "the first real content," which was true before this phase and is now ROOT 01. Both suites fixed and re-verified green against a fresh stack.
Testing (Section 26)
platform/test/e2e-phase6d.js — 20/20 passing (fresh stack): ROOT 01 hierarchy, ROOT 02-04
honestly empty, activity ordering, lab-cannot-be-manually-completed, full course completion via
lesson/lab/assessment, prerequisite chain (both the locked and unlocked directions, on two
separate students), existing ROOT 05+ and Capstone state unaffected, is_required rule
unaffected, self-paced no-instructor-deferral check, instructor-notes exclusion, RBAC, logout/
login persistence, cross-student isolation, version-bump stability, and content validation.
Real-Lab rehearsal (Section 27)
infra/scripts/drills/test-phase6d-foundations-rehearsal.js — 11/11 passing against a real
disposable Lab container (no mocking): login → enroll → resume → lesson/guided-exercise →
real Lab launch → real bash hardware inspection (nproc/free/df, written to a real file) →
real validation → real permission-denial-then-fix (su - labuser, genuinely denied, then
genuinely allowed after chmod 644) → remaining modules → assessment → course completion →
ROOT 02 unlock → logout/login persistence → instructor visibility of the resulting state.
Regression (Section 30)
Re-ran 6A (21/21), 6B (24/24), 6C (17/17) fresh, each against its own freshly-reset stack per
the established isolation convention — all green after the two resume-point staleness fixes
above. 2A/2B/3A/3B/5A/5B/5C were not re-run: git diff for this phase shows no changes to
platform/api/src/, gateway, or provisioner code (only the Lab image/entrypoint, migrations,
content, scenarios, tests, and docs), so their surface area was untouched. Frontend build: clean
(vite build, unchanged this phase). Lab image: rebuilt and standalone-smoke-tested directly
(see the container runtime section above) in addition to the full-stack rehearsal.
Content validation
infra/scripts/validate-course-content.js — 0 errors against 17 courses/modules with real
content, 201 activities, 106 scenario files (98 + 8 new this phase).
Known limitations / intentionally deferred
- ROOT 02, 03, 04 course content — architecture designed, not authored.
- ROOT 04's Docker/Compose hands-on labs specifically — platform extension done and verified, course content not yet written against it.
- Instructor-led vs. self-paced timing is not a separate database field (see
docs/ROOT_PROGRAM_DURATION_MODEL.md). - No placement testing or instructor-override UI (explicitly out of scope this phase).
Recommended Phase 6E scope
Author ROOT 02 (prioritize the "critical" Networking Mental Model module), then ROOT 03 (Git, using the already-verified local-repo strategy), then ROOT 04 (content only — the runtime is already built and tested).