Adds PHASE6E_ROOT_FOUNDATIONS_COMPLETION.md (the phase's own record: what was authored, the dash-vs-bash scenario bug found and fixed, the pre-existing lock-enforcement gap found and fixed, the regression fallout that fix revealed in earlier suites and why each was a stale test assumption rather than a platform bug, and honest test/rehearsal/regression results). Updates ROOT_FOUNDATIONS_PILOT_READINESS.md (full re-audit: all 4 ROOT Foundations courses now READY, not inherited blindly from Phase 6D), ROOT_PROGRAM_DURATION_MODEL.md (real ROOT 02-04 duration data: 24.7 combined hours, landing inside Phase 6D's own estimated range; 67.9 hours across the full authored program), ROOT_PILOT_CURRICULUM.md (ROOT 02-04 move from NOT IN PILOT to IN PILOT with full module-level detail; the pilot is now genuinely self-paced-capable for the entire foundational half, not just instructor-led), ROOT_PROGRAM_ARCHITECTURE.md and ROOT_CURRICULUM_COVERAGE_MATRIX.md (course/module/scenario totals), and ROOT_FOUNDATIONS_ARCHITECTURE.md (marked as-built, not just as-designed). Co-Authored-By: Claude Sonnet 5 <[email protected]>
172 lines
11 KiB
Markdown
172 lines
11 KiB
Markdown
# Phase 6E — ROOT Foundations Completion
|
|
|
|
Phase 6E's mission: finish what Phase 6D deliberately left for a future phase — ROOT 02 (Linux
|
|
Administration & Networking), ROOT 03 (Git & Collaborative Development), and ROOT 04
|
|
(Containers & Docker) — completing the real ROOT 01→02→03→04→05 foundational path.
|
|
|
|
## Baseline verification
|
|
|
|
Confirmed at start: branch `main`, clean tree, HEAD = `origin/main` = Phase 6D's
|
|
`b6516a74db2d5a5d53d2f1f76c5f2d74e4ed35eb`.
|
|
|
|
## ROOT 02, 03, 04 — architecture, content, labs
|
|
|
|
All three courses were authored to the exact same gold-module standard already proven in ROOT
|
|
01: real lessons/demonstrations/knowledge-checks/guided-exercises/reflections, real hands-on labs
|
|
using the existing generic scenario/validation engine, and a wired course assessment. See
|
|
`docs/ROOT_FOUNDATIONS_PILOT_READINESS.md` for the full module-by-module evidence and
|
|
`docs/ROOT_PILOT_CURRICULUM.md` for the pilot manifest detail.
|
|
|
|
**ROOT 02** (6 modules, 37 activities): the Networking Mental Model module (M3) received
|
|
deliberately extra depth per its "critical module" designation — no lab by design (conceptual
|
|
mastery via knowledge check and guided exercise), with every later module building on it
|
|
explicitly. Real Labs: a nginx-inspection task that never touches the platform's own reverse
|
|
proxy, a controllable start/stop service lifecycle, disk-usage forensics, `ip`/`ss`/`getent`
|
|
diagnostics, and a genuine DNS-resolves-but-port-is-wrong diagnose-and-fix.
|
|
|
|
**ROOT 03** (6 modules, 31 activities): uses only real LOCAL Git repositories — a real merge
|
|
conflict caused on purpose and resolved by the student, and a real local bare repository
|
|
standing in for a remote (clone/commit/push, verified independently on the "remote" side). No
|
|
Gitea account, no network access, no RootNode credential of any kind ever reaches a student Lab.
|
|
|
|
**ROOT 04** (8 modules, 43 activities): uses the nested Docker Engine Phase 6D built and tested.
|
|
Real Labs: `docker run`/`exec`/`inspect` fluency, a genuine "works internally, unreachable from
|
|
host" port-publishing diagnosis and fix, a real destroy/recreate volume-persistence proof, a
|
|
real Dockerfile build, and a real two-service Compose application.
|
|
|
|
## The central architectural finding, confirmed again
|
|
|
|
Every new scenario this phase (Linux, Git, Docker) uses the exact same generic scenario/
|
|
validation engine ROOT 01 proved — `runCommand()` executes an arbitrary shell command and checks
|
|
its output, nothing Kubernetes-specific. No new activity type, no new mechanism.
|
|
|
|
## A real, reproducible dash-vs-bash bug, found and fixed
|
|
|
|
`&>file` (bash's combined-redirect shorthand) silently misparses under `/bin/sh` (dash, this
|
|
image's actual shell, confirmed via `readlink -f /bin/sh`), which is what
|
|
`backend/server.js`'s `runCommand()` uses — a scenario's `setup_commands` using `&>` captured
|
|
the WRONG process's PID via `$!`, breaking a later `kill $(cat pid)` step with "No such
|
|
process." Found while testing `linux-service-lifecycle` and `linux-dns-http-diagnosis` against
|
|
the real API (not assumed). Fixed by using the POSIX-portable `>file 2>&1` form in both affected
|
|
scenarios' `setup_commands`/hints.
|
|
|
|
## A real, pre-existing security gap, found and fixed
|
|
|
|
Testing the real ROOT 01→02→03→04→05 chain via direct API (Step 11's explicit requirement)
|
|
revealed that `POST /api/student/activities/:activityId/complete`
|
|
(`platform/api/src/course-routes.js`) checked only that the activity exists and the student
|
|
holds an active PROGRAM enrollment — **never** whether the activity's own course/module was
|
|
actually unlocked for that student. A direct POST could mark any `manual_marker` activity
|
|
complete regardless of the prerequisite chain. This is the exact class of gap Phase 6B already
|
|
fixed once for the capstone's own start route, just never closed for this more general route.
|
|
|
|
**Proven to predate Phase 6E**, not introduced by it: reproduced against an unrelated ROOT 05/06
|
|
pair before touching any Phase 6E content, confirming the gap was real and pre-existing, just
|
|
never triggered before because ROOT 01-04 (and therefore any REAL prerequisite gate before ROOT
|
|
05) didn't exist with real content until this phase and Phase 6D.
|
|
|
|
**Fix**: the route now calls the same `buildCourseTree()`/lock computation every other route
|
|
already trusts, resolves the activity's own course and module within that tree, and returns
|
|
`403 { error: 'locked' }` if either is locked — reusing the exact mechanism, not a second,
|
|
duplicated check. Verified live: both a ROOT 02 bypass attempt (behind the new chain) and a
|
|
ROOT 06 bypass attempt (a completely unrelated, pre-existing course pair) are now correctly
|
|
rejected, while legitimate unlocked completions still succeed.
|
|
|
|
## Regression fallout from a genuine, correct fix — and what it revealed
|
|
|
|
Enforcing this lock for real broke several EARLIER e2e suites' own historical shortcuts: tests
|
|
written under the old (buggy) permissive behavior had accumulated small ordering gaps —
|
|
completing a course's modules out of the order its own real prerequisite chain requires, or
|
|
skipping a scenario the ALL_STAGES/FOUNDATION lists never included because the corresponding
|
|
activity was added in a later phase. None of these were platform bugs; each was a stale test
|
|
assumption the missing enforcement had been silently covering for. Found and fixed, with the
|
|
underlying scenario gap named explicitly in each case:
|
|
|
|
- `e2e-phase6a.js`/`e2e-phase6b.js`/`e2e-phase6c.js`/`e2e-phase6d.js`: needed the real ROOT
|
|
01-04 completion sequence injected before touching ROOT 05+ content, since ROOT 05's real
|
|
prerequisite (ROOT 04) is no longer vacuously satisfied. Extracted into a new shared
|
|
`platform/test/foundations-helper.js` (`completeRootFoundations01Through04`,
|
|
`completeThroughRoot06`, `completeThroughRoot10`) rather than duplicating ~30-80 lines four
|
|
times.
|
|
- `e2e-phase6b.js`'s `bypass` student flow: needed `deployment-rollback`, `job-task`,
|
|
`cronjob-task`, and `container-logging-mcq` scenarios completed explicitly — all four exist in
|
|
the real curriculum but were added in phases after the original `ALL_STAGES` list was written,
|
|
so completing "every stage scenario" via that stale list never actually finished
|
|
`root-06`/`root-10` the COURSE, only their originally-scoped subset.
|
|
- `e2e-phase6a.js`'s and `e2e-phase6d.js`'s own ROOT 01/05 completion tests needed each module
|
|
completed fully (lesson→demo→kc→guided→**lab→reflection**) before moving to the next, since
|
|
ROOT 01's (and ROOT 05's) own modules carry real intra-course prerequisites — completing every
|
|
module's front half first and circling back for labs/reflections later is exactly the ordering
|
|
the newly-enforced lock now correctly rejects.
|
|
- Two now-obsolete Phase-6D-era assertions in `e2e-phase6d.js` ("ROOT 02-04 stay PLANNED," "ROOT
|
|
03-05 stay vacuously unlocked") were updated to assert the opposite, with a comment explaining
|
|
Phase 6E intentionally changed that fact.
|
|
|
|
## Testing
|
|
|
|
`platform/test/e2e-phase6e.js` — 23/23 passing (fresh stack): full ROOT 02/03/04 hierarchy,
|
|
content, and assessment-pool verification; the complete ROOT 01→02→03→04→05 chain proven on one
|
|
student (each course's completion unlocking exactly the next, nothing further); the direct-API
|
|
lock bypass proven blocked on both a Phase 6E course pair and a pre-existing, unrelated pair;
|
|
existing ROOT 06+/Capstone/`is_required` behavior confirmed unaffected; self-paced no-instructor-
|
|
deferral scan; instructor-notes exclusion and RBAC; logout/login persistence; cross-student
|
|
isolation; content-version stability; content validation.
|
|
|
|
## Real-Lab rehearsal
|
|
|
|
`infra/scripts/drills/test-phase6e-foundations-rehearsal.js` — 13/13 passing against a real,
|
|
freshly-built stack: real Platform API/Gateway/Provisioner/disposable Lab, real shell, real
|
|
nested Docker, real Git, real k3s (left completely untouched, still healthy at the end). Walks
|
|
one genuinely representative real-Lab task per new course (a real nginx-inspection Lab for ROOT
|
|
02, a real merge-conflict resolution for ROOT 03, a real container-networking diagnose-and-fix
|
|
for ROOT 04) plus the full ROOT 01-04 chain to a genuine ROOT 05 unlock, with an instructor
|
|
narrative confirming the resulting learning state is visible.
|
|
|
|
## Regression
|
|
|
|
Full fresh-stack reruns after every fix: 6A (23/23), 6B (26/26), 6C (17/17), 6D (20/20), 6E
|
|
(23/23) — all green. 2A/2B/3A/3B/5A/5B/5C not re-run: `git diff` for this phase touches only
|
|
`platform/api/src/course-routes.js` (the one real application-code change) plus content/
|
|
migrations/tests/docs — confirmed by grep that none of those earlier suites reference the
|
|
`/api/student/activities/:id/complete` route this phase's fix touched.
|
|
|
|
## Frontend, Lab image, nested Docker, k3s
|
|
|
|
No frontend change was needed — `CourseView.jsx`/`CurriculumView.jsx` already generalize
|
|
correctly to every ROOT 02-04 activity/status shape (proven by the rehearsal and e2e suite, not
|
|
assumed). Frontend build: clean, unchanged this phase. Lab image: rebuilt with the dash-fix and
|
|
new scenario files, standalone-smoke-tested and exercised extensively via the rehearsal (real
|
|
nested Docker + real k3s side by side, k3s health reconfirmed untouched at the rehearsal's end).
|
|
|
|
## New application code and why necessary
|
|
|
|
`platform/api/src/course-routes.js` — the lock-enforcement fix described above. This is the
|
|
ONLY application code changed this phase; every course/content requirement was satisfiable with
|
|
the existing `course_modules`/`module_activities`/`assessment_definitions`/scenario-engine
|
|
model.
|
|
|
|
## Content validation
|
|
|
|
`infra/scripts/validate-course-content.js` — 0 errors, all courses/modules with real content,
|
|
the full activity/scenario set including this phase's 17 new scenario files (6 Linux task
|
|
scenarios, 2 Linux MCQs, 3 Git task scenarios, 2 Git MCQs, 5 Docker task scenarios, 2 Docker
|
|
MCQs — see the migrations for the exact counts per course).
|
|
|
|
## Known limitations
|
|
|
|
- Instructor-led vs. self-paced timing remains prose in Instructor Notes, not a separate
|
|
database field (unchanged from Phase 6D).
|
|
- Three practice-bank modules from earlier phases (root-06-m01, root-07-m01, root-10-m02) remain
|
|
instructor-led-only this pilot — unrelated to Phase 6E's own scope, unchanged.
|
|
- No placement testing or instructor-override UI (explicitly out of scope, as in every prior
|
|
phase) — the extension point (`courses.prerequisites` as a plain array) remains documented in
|
|
`docs/ROOT_FOUNDATIONS_ARCHITECTURE.md`.
|
|
|
|
## Recommended Phase 6F scope
|
|
|
|
With the entire ROOT_ foundational-to-capstone path now genuinely READY, natural next
|
|
directions: author ROOT 08 (Storage & Scheduling) to close the last PARTIAL Kubernetes course;
|
|
build the future lab infrastructure ROOT 11 modules 2-7 need (a Git+ArgoCD environment for
|
|
GitOps, a CI runner for CI/CD) and author that content; or invest in self-paced teaching layers
|
|
for the three remaining instructor-led-only practice-bank modules.
|