feat(root01): author Computing & Linux Foundations, and the real 01->05 prerequisite chain

Adds all 6 ROOT 01 modules (how computers run software, the shell, filesystem navigation,
files & text, processes, users & permissions foundations), each with a real lesson,
demonstration/guided exercise, knowledge check where warranted, a hands-on Lab, and a
reflection — 35 activities, 29 new content files.

Adds 6 new hands-on Linux scenarios (linux-inspect-hardware, linux-shell-basics,
linux-filesystem-navigation, linux-files-and-text, linux-process-management,
linux-permissions-foundations) plus 2 MCQ scenarios, using the existing generic scenario/
validation engine (docs/ROOT_FOUNDATIONS_LAB_STRATEGY.md §1) — every one tested end-to-end via
the real scenario setup/validate API, including negative tests confirming they correctly FAIL
when not actually solved (e.g. the process-management scenario fails while the target process
is still running; the permissions scenario is tested against a real `su - labuser` denial).

Wires rootnode-linux-foundations-assessment (2 required + 4 random-draw scenarios) using the
existing Phase 3B assessment engine.

Adds a real, server-side ROOT 01->02->03->04->05 course prerequisite chain
(courses.prerequisites) — verified live on two students: one who completed ROOT 01 sees ROOT 02
unlock; a fresh student sees ROOT 02 correctly locked while 03/04/05 stay vacuously satisfied
(their own prerequisite courses are still empty). ROOT 06+ and the Capstone are unaffected.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-26 12:59:52 -05:00
co-authored by Claude Sonnet 5
parent 87526bcf7f
commit 15b616274b
37 changed files with 1549 additions and 0 deletions
@@ -0,0 +1,64 @@
# Processes
## What a process actually is
A **process** is a running instance of a program — its own code, its own private memory, and a
unique identifier the kernel tracks called a **PID** (Process ID). Run the same program twice
(open two terminals both running `bash`), and you get two separate processes with two different
PIDs, even though they're running identical code.
## Foreground vs. background
A command you run normally occupies your terminal until it finishes (**foreground**) — you
can't type anything else until it's done. Append `&` to run it in the **background** instead,
freeing your terminal immediately while the process keeps running. `sleep 300 &` starts a
300-second sleep in the background and hands your prompt right back.
## Inspecting processes: `ps` and `top`
- `ps aux` — a snapshot of every process currently running, including its PID, how much CPU/
memory it's using, and the exact command that started it.
- `top` (or `htop`, if installed) — the same idea, but live-updating and sorted by resource
usage, useful for "what's actually eating CPU/memory right now."
## Stopping a process: `kill` and signals
`kill <PID>` doesn't instantly destroy a process — it sends it a **signal**, a message the
process can (usually) choose how to respond to. The default signal, `SIGTERM` (15), politely
asks a process to shut down, giving it a chance to clean up first. `kill -9 <PID>` sends
`SIGKILL`, which the kernel enforces immediately with no chance for the process to react —
appropriate only when a process is truly stuck and ignoring `SIGTERM`. `kill -0 <PID>` is a
special case: it sends NO real signal at all, just checks whether the PID still exists —
exactly how you confirm "did that process actually stop" without side effects.
## Process hierarchy
Every process (except the very first one at boot) has a **parent** — the process that started
it. Your shell is the parent of every command you run from it. This matters later: a parent
process's own lifecycle can affect its children (this is part of why a container, which is
often just one process tree, behaves the way it does when its main process exits).
## Why this matters for containers and Kubernetes
A container is, underneath, mostly an isolated process (or small process tree) with its own
view of the filesystem and network — when people say "a container exited," they mean its main
process exited, exactly the same event you'll cause on purpose in this module's Lab. Kubernetes'
entire job, at its core, is deciding which machine a process (inside a container) runs on and
restarting it if it dies unexpectedly — none of that will feel intuitive without this module's
model of what a process actually is.
## Instructor Notes
**Lesson goal:** Build a real process mental model (PID, foreground/background, signals,
parent/child) that ROOT 04 (containers) and ROOT 06 (Kubernetes workloads) both directly build
on without re-explaining.
**Approximate timing:** 15 minutes read; 10 minutes live.
**Common misconception to watch for:** students often think `kill` always means "forcefully
destroy" — clarify that plain `kill` sends a polite, ignorable request (SIGTERM), and `-9` is
the forceful escalation, not the default.
**Discussion question:** "What do you think happens to a container if its main process exits on
its own, versus if something outside kills it?" (Don't need a complete answer yet — just plant
the question for later.)
@@ -0,0 +1,53 @@
# Demonstration: Watching a Process Actually Terminate
## OBSERVE
Start a long-running background process and note its PID:
```
$ sleep 300 &
[1] 1421
```
`1421` is the PID (yours will differ every time).
## HYPOTHESIS
Predict: will `kill -0 1421` print anything if the process is still running?
## INSPECT
```
$ kill -0 1421 && echo "still running"
still running
```
`kill -0` sends no real signal — silence (or, here, our own `echo`) means the process exists.
## CHANGE
Now actually terminate it:
```
$ kill 1421
```
## VERIFY
```
$ kill -0 1421 2>/dev/null && echo "still running" || echo "gone"
gone
```
The exact same check now reports the opposite result — real, observable proof the process is
gone, not an assumption.
## Instructor Notes
**Demo goal:** Make "checking before and after" a visible habit, not just narrated — students
should see the SAME command return a different real answer once state actually changed.
**Approximate timing:** 8 minutes live.
**Common mistake to watch for:** reaching for `kill -9` by default "to be safe" — reinforce that
plain `kill` (SIGTERM) is the correct first attempt almost always.
@@ -0,0 +1,38 @@
# Knowledge Check: Processes
---
**1. What does a PID identify?**
*Answer:* One specific running instance of a program — the kernel's unique identifier for that
process, distinct from every other running process, even other copies of the same program.
---
**2. What's the actual difference between `kill <pid>` and `kill -9 <pid>`?**
*Answer:* Plain `kill` sends SIGTERM, a polite request the process can choose to handle (clean
up, then exit). `kill -9` sends SIGKILL, which the kernel enforces immediately with no chance
for the process to react — an escalation for when SIGTERM is ignored, not the default choice.
---
**3. You want to confirm a process is still running WITHOUT affecting it in any way. What do
you use?**
*Answer:* `kill -0 <pid>` — sends no real signal, only checks whether the PID still exists.
---
**4. Why might understanding processes matter for understanding containers later?**
*Answer:* A container is, underneath, mostly an isolated process (or small process tree) — a
container "exiting" is really its main process exiting, the exact same event this module's Lab
causes on purpose.
## Instructor Notes
**Facilitation:** Question 2 is worth confirming explicitly — "kill -9 by default" is a common,
worth-correcting habit.
**Approximate timing:** 5 minutes.
@@ -0,0 +1,28 @@
# Guided Exercise: Choosing the Right Signal
This is a paper exercise — commit to an answer before checking it in the Lab.
## Your task
For each situation, decide: plain `kill` (SIGTERM) first, or is `kill -9` (SIGKILL) actually
justified?
1. A normal web server process you want to shut down for a routine restart.
2. A process that received SIGTERM two minutes ago and is still running, showing no signs of
responding.
3. A script you just started by mistake, that hasn't done anything harmful yet.
## A worked shape to check yourself against
1. Plain `kill` — give it the chance to shut down cleanly (close connections, flush data, etc).
2. `kill -9` is now justified — you already gave it the polite chance, and it didn't respond.
3. Plain `kill` first, still — "just started" doesn't change the default; SIGKILL is a last
resort, not a shortcut for convenience.
## Instructor Notes
**Facilitation:** Item 3 sometimes trips students who reach for `-9` out of impatience — worth
reinforcing that SIGTERM-first is the discipline REGARDLESS of urgency, since a process that
ignores SIGTERM is the actual signal that escalation is warranted.
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
@@ -0,0 +1,27 @@
# Reflection: You Just Stopped a Real Process, Not a Simulation
## Reflection questions
1. Before checking `kill -0` after your `kill`, how confident were you the process was
actually gone? What would you have done if it hadn't stopped?
2. Why do you think SIGTERM-before-SIGKILL matters more for a real production service than for
a throwaway script in a Lab?
## Real Ops takeaway
**"I ran the kill command" is not the same as "I confirmed the process is gone."** This module's
Lab required you to verify state, not just take an action and assume it worked — that
verification step is exactly what separates a confident operator from someone hoping for the
best. You'll repeat this exact pattern (act, then verify) constantly for the rest of ROOT_,
starting as soon as ROOT 04's containers and continuing all the way through Kubernetes
troubleshooting.
This module is complete. Continue to the next module.
## Instructor Notes
**Discussion questions for a live classroom:** question 2 is a good bridge to ROOT 06's later
discussion of graceful shutdown/`terminationGracePeriodSeconds` — plant the idea now without
naming Kubernetes specifics yet.
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.