seed: curriculum content

This commit is contained in:
2026-05-07 14:32:44 +00:00
parent 9258534803
commit ec76f4f56b
100 changed files with 2846 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
---
type: module
title: "Welcome"
description: "Your first 12 days. Crash course in the terminal and Git, then a paired battle."
---
# Welcome
You've landed. The next 12 days are about getting you off the ground:
the shell, Git, and how things work here. Sixteen blocks, one battle,
then you're cleared for Python.
This module gives you the bare minimum tools to write code, save your
work, and ship it. Every command you learn here will reappear, in some
form, every day for the rest of the program.
## What You'll Do
- Walk the file system from a blank prompt
- Make, move, and read files
- Write your first shell script
- Configure Git and make your first commits
- Push your work to GitHub and open your first pull request
- Pair with another cadet to close out the module
## Pace
Roughly 1012 hours of focused work. Some cadets finish in a day, some
take two. Either is fine. Do not skip blocks — the checkpoint catches
gaps.

View File

@@ -0,0 +1,84 @@
---
type: story
title: "Welcome"
xp: 25
duration: 20
difficulty: 1
---
# Welcome
> **[INCOMING — Mission Control, Earth]**
>
> Cadet.
>
> Welcome to the program.
>
> You have just enrolled in twelve months of training. By the end, if
> you finish, you will be a working developer — not a graduate of
> something, but a person who can sit in front of a blank file and
> produce software that does what it's supposed to do.
>
> We will not teach you the way you've been taught before. There will
> be no lectures. No instructor at a board. No videos to play at 2x.
> You will read, you will type, and you will build. Most of the
> learning happens between your ears while your fingers move.
>
> Before you touch the terminal — and you will, in the very next block
> — we want you to know where you are and what's about to happen.
> Read this to the end.
>
> [TRANSMISSION CONTINUES]
## Where You Are
You have entered Stage I: **Solar System**. Your launchpad. Over the
next four weeks, you will move from "I have never written code" to
"I can write small programs that solve real problems."
Past Solar System, four more stages await:
- **Milky Way** — deeper Python, data structures, your first real projects.
- **Andromeda** — web, databases, applications other people can use.
- **Deep Space** — specialize. Backend, frontend, data, or DevOps.
- **New Horizon** — your capstone, and the bridge to a job.
Don't worry about anything past Solar System right now. The path
exists. Trust it. Walk one stage at a time.
## How Content Works
Every module in every stage is built from three kinds of blocks:
- **Stories** like this one. They give you context — the *why* before
the *do*. They are short. Skim them and you'll pay for it later.
- **Challenges** are where you build. Each one drops you a starter pack
and a mission. You write code, submit, and an automated grader tells
you pass or iterate.
- **Battles** put you in a pair. One of you executes, the other
reviews. You learn twice — once doing, once watching.
Every block earns XP when you complete it. XP is not a leaderboard.
It's a measure of how much you have actually done. Skipping ahead does
not work — checkpoints catch it, and they catch it in front of your
peers.
## What This Module Holds
The next block is `hello-world` — your first line of code. After that,
twelve more challenges teach you the shell and Git, the tools every
cadet on Earth uses every day. One paired battle closes the module.
One to two days of work, depending on your pace. Then Python begins.
> **[CLOSING — Mission Control]**
>
> One more thing before we let you go.
>
> Everyone who finishes the program got stuck where you are now. Every
> single one. The terminal you're about to open scared them too. They
> kept going. So will you.
>
> Your training begins on the next block.
>
> [END TRANSMISSION]

View File

@@ -0,0 +1,35 @@
---
type: challenge
title: "Hello, World"
xp: 25
duration: 15
difficulty: 1
---
# Hello, World
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, first signal. Write a script that creates `hello.txt`
> containing exactly:
>
> ```
> Hello, World
> ```
>
> Two commands:
>
> - `echo` prints text
> - `>` redirects what would print into a file
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, write the command(s) that create `hello.txt`
with the content `Hello, World`.
## Objectives
- `hello.txt` exists in the working directory
- File contents match `Hello, World` exactly

View File

@@ -0,0 +1,6 @@
#!/bin/bash
# Hello, World — your first script.
# Write the line "Hello, World" into a file called hello.txt
# in the current directory.
# Your code here.

View File

@@ -0,0 +1,16 @@
#!/bin/bash
bash solution.sh > /dev/null 2>&1
if [ -f hello.txt ]; then
echo "ok 1 - hello.txt is created"
else
echo "not ok 1 - hello.txt is created"
fi
ACTUAL=$(cat hello.txt 2>/dev/null)
ACTUAL="${ACTUAL%$'\n'}"
if [ "$ACTUAL" = "Hello, World" ]; then
echo "ok 2 - hello.txt contains 'Hello, World'"
else
echo "not ok 2 - hello.txt contains 'Hello, World'"
fi

View File

@@ -0,0 +1,46 @@
---
type: battle
title: "First Repo"
xp: 200
duration: 30
difficulty: 1
---
# First Repo
> **[INCOMING — Mission Control, Earth]**
>
> Cadets, your first paired exercise. One of you will set up a fresh
> Git project; the other will verify it. Then swap.
>
> Pair up. Decide who goes first as **defender** and who goes first
> as **attacker**.
>
> [END TRANSMISSION]
## The Task
The **defender** writes a single shell script — `setup.sh` — that,
when run inside an empty workspace:
1. Initializes a fresh Git repo on branch `main`.
2. Sets `user.name` and `user.email` on the repo.
3. Creates a file called `hello-world.txt` containing exactly:
```
Hello, World
```
4. Stages and commits it with the message `add hello-world`.
## Battle Rules
- **Defender**: writes `setup.sh` in a clean workspace. Does not show
the file until done.
- **Attacker**: receives the script, runs it in a clean folder,
inspects the resulting repo, and submits a review.
- **Then swap**: roles flip; the new defender writes their own
`setup.sh` (no copy-pasting).
- Both pass the battle if both scripts pass review.
## Tools
`git init`, `git config`, `echo`, `>`, `git add`, `git commit -m`.

View File

@@ -0,0 +1,38 @@
# First Repo — Extended Notes
*Not rendered yet. For instructors and a future "learn more" surface.*
## Purpose
The first paired exercise of the program. Cadets have just finished
the bash basics; this introduces them to the battle pattern (defender
writes, attacker reviews, then swap) using a tiny but real Git
workflow: init, commit, log.
## Skills Demonstrated
- Composing learned commands (`git init`, `git config`, `git add`,
`git commit`) into a single script
- Reading another cadet's script to verify behavior
- Filling out the review checklist constructively
## Common Pitfalls
- **Skipping `git config`** — the commit either fails or uses a
fallback identity that's not the cadet's. Reviewer should run
`git config --local user.name` to confirm.
- **Wrong file content** — `echo "hello world"` (lowercase) doesn't
match `Hello, World`. Reviewer must `cat hello-world.txt` and check
byte-for-byte.
- **Wrong commit message** — `add hello world` (no dash) is *not*
`add hello-world`. Reviewer should run `git log --format=%s`.
- **Multiple commits** — sometimes a cadet stages files twice and
ends up with two commits. The spec asks for exactly one.
## Discussion Prompts (post-battle)
After both rounds, pair discusses for 23 minutes:
1. Whose script was easier to read? Why?
2. What did you check first when reviewing? Why?
3. If this script ran on a server, what could go wrong?

View File

@@ -0,0 +1,27 @@
[
{
"title": "Repo Setup",
"icon": "ph:git-branch-duotone",
"items": [
".git/ exists after running setup.sh",
"user.name is set on the repo",
"user.email is set and looks like an email"
]
},
{
"title": "File Content",
"icon": "ph:file-text-duotone",
"items": [
"hello-world.txt exists at the repo root",
"File contains exactly the line 'Hello, World'"
]
},
{
"title": "Commit",
"icon": "ph:git-commit-duotone",
"items": [
"Exactly one commit in git log",
"Commit message is exactly 'add hello-world'"
]
}
]

View File

@@ -0,0 +1,40 @@
---
type: challenge
title: "The Maker"
xp: 50
duration: 25
difficulty: 2
---
# The Maker
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, you've walked the field. Now build something.
>
> When your script runs, a stale file `cargo-old.txt` is sitting in
> your working directory. Build a cargo bay with three rooms — `food/`,
> `water/`, `tools/` — each holding an empty `manifest.txt`. Then
> delete the stale file.
>
> Three commands:
>
> - `mkdir` — make a directory (use `-p` to make nested ones at once)
> - `touch` — create an empty file
> - `rm` — delete (be careful — there is no trash)
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, write the commands to:
1. Create `cargo/food/`, `cargo/water/`, `cargo/tools/`
2. Create an empty `manifest.txt` in each room
3. Remove `cargo-old.txt`
## Objectives
- All three room directories exist
- Each contains an empty `manifest.txt`
- `cargo-old.txt` no longer exists

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# The Maker — build a cargo bay.
#
# When this script runs, a stale file `cargo-old.txt` is in your
# working directory. Your script must:
#
# 1. Create directories: cargo/food, cargo/water, cargo/tools
# 2. Create an empty file `manifest.txt` in each of those three rooms
# 3. Remove cargo-old.txt
#
# Tools: mkdir (use -p for nested), touch, rm
# Your code here.

View File

@@ -0,0 +1,17 @@
#!/bin/bash
echo "stale entry" > cargo-old.txt
bash solution.sh > /dev/null 2>&1
N=0
check() {
N=$((N+1))
if eval "$1"; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
}
check '[ -d cargo/food ]' "cargo/food exists"
check '[ -d cargo/water ]' "cargo/water exists"
check '[ -d cargo/tools ]' "cargo/tools exists"
check '[ -f cargo/food/manifest.txt ] && [ ! -s cargo/food/manifest.txt ]' "cargo/food/manifest.txt exists and is empty"
check '[ -f cargo/water/manifest.txt ] && [ ! -s cargo/water/manifest.txt ]' "cargo/water/manifest.txt exists and is empty"
check '[ -f cargo/tools/manifest.txt ] && [ ! -s cargo/tools/manifest.txt ]' "cargo/tools/manifest.txt exists and is empty"
check '[ ! -f cargo-old.txt ]' "cargo-old.txt removed"

View File

@@ -0,0 +1,39 @@
---
type: challenge
title: "The Mover"
xp: 50
duration: 25
difficulty: 2
---
# The Mover
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, your cargo bay is built. Now stock it.
>
> When your script runs, an `inbox/` of fresh supplies has arrived.
> Each item is labeled by category — `food-`, `water-`, or `tool-`.
> Sort them into the right rooms. Then make a backup of the food
> manifest.
>
> Two commands:
>
> - `mv` — move (or rename)
> - `cp` — copy
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, write the commands to:
1. Move each `food-*` file into `cargo/food/`
2. Move each `water-*` file into `cargo/water/`
3. Move each `tool-*` file into `cargo/tools/`
4. Copy `cargo/food/manifest.txt` to `cargo/food/manifest.backup`
## Objectives
- All `food-*`, `water-*`, `tool-*` files moved to the correct rooms
- `cargo/food/manifest.backup` exists

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# The Mover — sort the supplies.
#
# When this script runs:
# - cargo/food/, cargo/water/, cargo/tools/ already exist
# - cargo/food/manifest.txt exists (empty)
# - inbox/ contains files with prefixes:
# food-apple.txt, food-bread.txt
# water-bottle.txt, water-canteen.txt
# tool-wrench.txt, tool-hammer.txt
#
# Your script must:
# 1. Move all food-*.txt files into cargo/food/
# 2. Move all water-*.txt files into cargo/water/
# 3. Move all tool-*.txt files into cargo/tools/
# 4. Copy cargo/food/manifest.txt to cargo/food/manifest.backup
#
# Tools: mv, cp
# Your code here.

View File

@@ -0,0 +1,25 @@
#!/bin/bash
mkdir -p cargo/food cargo/water cargo/tools inbox
touch cargo/food/manifest.txt cargo/water/manifest.txt cargo/tools/manifest.txt
echo "1 apple" > inbox/food-apple.txt
echo "1 loaf" > inbox/food-bread.txt
echo "500ml" > inbox/water-bottle.txt
echo "1L" > inbox/water-canteen.txt
echo "10mm" > inbox/tool-wrench.txt
echo "claw" > inbox/tool-hammer.txt
bash solution.sh > /dev/null 2>&1
N=0
check() {
N=$((N+1))
if eval "$1"; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
}
check '[ -f cargo/food/food-apple.txt ]' "food-apple in cargo/food"
check '[ -f cargo/food/food-bread.txt ]' "food-bread in cargo/food"
check '[ -f cargo/water/water-bottle.txt ]' "water-bottle in cargo/water"
check '[ -f cargo/water/water-canteen.txt ]' "water-canteen in cargo/water"
check '[ -f cargo/tools/tool-wrench.txt ]' "tool-wrench in cargo/tools"
check '[ -f cargo/tools/tool-hammer.txt ]' "tool-hammer in cargo/tools"
check '[ -f cargo/food/manifest.backup ]' "cargo/food/manifest.backup exists"

View File

@@ -0,0 +1,39 @@
---
type: challenge
title: "The Reader"
xp: 50
duration: 30
difficulty: 2
---
# The Reader
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, an old mission log was recovered. We need three things from it.
>
> When your script runs, `mission-log.txt` is sitting in the working
> directory. Pull these out:
>
> 1. The first 5 lines → save to `first-five.txt`
> 2. The last 3 lines → save to `last-three.txt`
> 3. The total line count → save to `line-count.txt`
>
> Three commands:
>
> - `head -n N file` — first N lines
> - `tail -n N file` — last N lines
> - `wc -l file` — count lines
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, produce the three output files using `head`,
`tail`, `wc`, and redirect.
## Objectives
- `first-five.txt` matches the first 5 lines of the log
- `last-three.txt` matches the last 3 lines of the log
- `line-count.txt` contains the line count

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# The Reader — pull stats from a log.
#
# When this script runs, mission-log.txt is in your working directory.
# Your script must produce three files:
#
# - first-five.txt — the first 5 lines of mission-log.txt
# - last-three.txt — the last 3 lines of mission-log.txt
# - line-count.txt — the line count (output of `wc -l`)
#
# Tools: head, tail, wc, redirect (>)
# Your code here.

View File

@@ -0,0 +1,39 @@
#!/bin/bash
cat > mission-log.txt <<'LOG'
[1969-07-20 13:32] Apollo touchdown confirmed.
[1969-07-20 13:33] Surface stable.
[1969-07-20 13:34] Cabin sealed.
[1969-07-20 13:35] EVA prep started.
[1969-07-20 13:40] Hatch open check.
[1969-07-20 13:42] Suit pressurization complete.
[1969-07-20 13:50] Ladder deployed.
[1969-07-20 13:55] First step recorded.
[1969-07-20 14:00] Sample collection begun.
[1969-07-20 14:10] All systems nominal.
[1969-07-20 14:20] Communication test passed.
[1969-07-20 14:30] Second sample box sealed.
[1969-07-20 14:40] Solar panel deployed.
[1969-07-20 14:50] Flag planted.
[1969-07-20 15:00] Phone call inbound.
[1969-07-20 15:10] Phone call complete.
[1969-07-20 15:20] Sample box stowed.
[1969-07-20 15:30] EVA wrap-up started.
[1969-07-20 15:35] Hatch closed.
[1969-07-20 15:40] Cabin re-pressurized.
LOG
bash solution.sh > /dev/null 2>&1
N=0
report() {
N=$((N+1))
if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi
}
[ -f first-five.txt ]; report $? "first-five.txt exists"
diff -q <(head -n 5 mission-log.txt) first-five.txt > /dev/null 2>&1; report $? "first-five.txt matches head -n 5"
[ -f last-three.txt ]; report $? "last-three.txt exists"
diff -q <(tail -n 3 mission-log.txt) last-three.txt > /dev/null 2>&1; report $? "last-three.txt matches tail -n 3"
[ -f line-count.txt ]; report $? "line-count.txt exists"
COUNT=$(awk '{print $1}' line-count.txt 2>/dev/null | tr -d ' ')
[ "$COUNT" = "20" ]; report $? "line-count.txt contains 20"

View File

@@ -0,0 +1,48 @@
---
type: challenge
title: "The Editor"
xp: 50
duration: 25
difficulty: 2
---
# The Editor
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, single-line `echo` works for short messages. Real files
> need multiple lines. Two options:
>
> - Multiple `echo` lines, each with its own redirect (use `>>` after
> the first one to append)
> - A *heredoc* — write a whole block in one go:
>
> ```bash
> cat > journal.md <<EOF
> line one
> line two
> EOF
> ```
>
> Write a script that creates `journal.md` containing exactly:
>
> ```
> # Cadet Log — Day 1
>
> Today I learned to navigate the shell.
> Tomorrow I will write code.
> ```
>
> Mind the empty line between the header and the body.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, produce `journal.md` with the exact content
above.
## Objectives
- `journal.md` exists
- Contents match the template exactly (4 lines + the blank line)

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# The Editor — write a multi-line journal entry.
#
# Your script must create a file `journal.md` containing exactly:
#
# # Cadet Log — Day 1
#
# Today I learned to navigate the shell.
# Tomorrow I will write code.
#
# Note the empty line between the header and the body.
#
# A heredoc writes multiple lines at once:
#
# cat > journal.md <<EOF
# ...lines here...
# EOF
# Your code here.

View File

@@ -0,0 +1,19 @@
#!/bin/bash
bash solution.sh > /dev/null 2>&1
EXPECTED=$(cat <<'EXP'
# Cadet Log — Day 1
Today I learned to navigate the shell.
Tomorrow I will write code.
EXP
)
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -f journal.md ]; report $? "journal.md exists"
ACTUAL=$(cat journal.md 2>/dev/null)
ACTUAL="${ACTUAL%$'\n'}"
[ "$ACTUAL" = "$EXPECTED" ]; report $? "journal.md matches the template exactly"

View File

@@ -0,0 +1,36 @@
---
type: challenge
title: "The Searcher"
xp: 75
duration: 30
difficulty: 3
---
# The Searcher
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, an archive arrived. Somewhere in it, a single log file
> mentions the word `BREACH`. We need it found.
>
> Two commands:
>
> - `find <path> -name "<pattern>"` — locate files by name
> - `grep -rl "<text>" <path>` — search file contents recursively,
> list only the matching file paths
>
> Your script must produce two files:
>
> 1. `logs.txt` — every `.log` file under `archive/`
> 2. `breach.txt` — the path to the file containing `BREACH`
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, write the two commands.
## Objectives
- `logs.txt` lists every `.log` file under `archive/`
- `breach.txt` contains the path to the file mentioning `BREACH`

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# The Searcher — find things in an archive.
#
# When this script runs, an `archive/` directory tree exists with
# many files. Somewhere in it, a single .log file mentions BREACH.
#
# Your script must produce two files:
# - logs.txt — every `.log` file path under archive/, one per line
# - breach.txt — the path to the file containing the word BREACH
#
# Tools: find, grep -rl
# Your code here.

View File

@@ -0,0 +1,22 @@
#!/bin/bash
mkdir -p archive/sectors/a archive/sectors/b archive/sectors/c archive/data archive/old
echo "[03:30] Sector A nominal." > archive/sectors/a/sector-a.log
echo "[03:32] Sector B nominal." > archive/sectors/b/sector-b.log
printf '[03:30] Sector C nominal.\n[03:42] BREACH detected at perimeter.\n' > archive/sectors/c/sector-c.log
echo "[00:00] Logger online." > archive/data/raw.log
echo "Notes" > archive/data/notes.txt
echo "[01:00] Legacy." > archive/old/old.log
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -f logs.txt ]; report $? "logs.txt exists"
EXPECTED=$(find archive -name "*.log" | sort)
ACTUAL=$(sort logs.txt 2>/dev/null)
[ "$EXPECTED" = "$ACTUAL" ]; report $? "logs.txt lists all .log files"
[ -f breach.txt ]; report $? "breach.txt exists"
grep -q "sector-c.log" breach.txt 2>/dev/null; report $? "breach.txt references sector-c.log"

View File

@@ -0,0 +1,45 @@
---
type: challenge
title: "The Scripter"
xp: 100
duration: 35
difficulty: 3
---
# The Scripter
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, time to combine commands. Bash lets you embed the output of
> one command inside another using `$(...)` — *command substitution*:
>
> ```bash
> echo "Today is $(date '+%Y-%m-%d')"
> # → Today is 2026-05-04
> ```
>
> When your script runs, a directory `supplies/` exists with several
> files. Produce `report.txt` containing exactly two lines:
>
> ```
> Date: <today in YYYY-MM-DD format>
> Files in supplies: <count of entries in supplies/>
> ```
>
> Two helpers:
>
> - `date '+%Y-%m-%d'` — today in the right format
> - `ls supplies | wc -l | tr -d ' '` — entry count, whitespace stripped
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, use `echo` + `$(...)` substitution to
produce the two-line report.
## Objectives
- `report.txt` exists
- Line 1 matches `Date: <today>`
- Line 2 matches `Files in supplies: <count>`

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# The Scripter — combine commands into a script.
#
# When this script runs, a directory `supplies/` exists with several
# files. Your script must produce `report.txt` containing exactly two
# lines:
#
# Date: <today, in YYYY-MM-DD format>
# Files in supplies: <number of entries in supplies/>
#
# Use command substitution `$(...)` to embed command output inside
# strings:
#
# $(date '+%Y-%m-%d')
# $(ls supplies | wc -l | tr -d ' ')
# Your code here.

View File

@@ -0,0 +1,21 @@
#!/bin/bash
mkdir -p supplies
touch supplies/oxygen-tank.txt \
supplies/ration-pack.txt \
supplies/medkit.txt \
supplies/repair-kit.txt \
supplies/comms-unit.txt
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -f report.txt ]; report $? "report.txt exists"
TODAY=$(date '+%Y-%m-%d')
LINE1=$(sed -n '1p' report.txt 2>/dev/null)
LINE2=$(sed -n '2p' report.txt 2>/dev/null)
[ "$LINE1" = "Date: $TODAY" ]; report $? "line 1 is 'Date: $TODAY'"
[ "$LINE2" = "Files in supplies: 5" ]; report $? "line 2 is 'Files in supplies: 5'"

View File

@@ -0,0 +1,89 @@
---
type: story
title: "How to Learn Here"
xp: 25
duration: 25
difficulty: 1
---
# How to Learn Here
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, take a breath. You've spent the morning in the shell.
> Before we take you to Git, we need you to know how learning actually
> works in this program.
>
> Cadets who finish the program share three habits. Cadets who quit
> share three different ones. We're going to tell you which is which.
>
> [TRANSMISSION CONTINUES]
## Rule 1 — Understand Every Line
You will use AI here. Claude, ChatGPT, our onboard NAV-7 — whatever
you reach for. We don't forbid it. We *expect* it. The world you're
training to enter assumes AI fluency.
But there is one rule that does not bend:
**You must be able to explain every line of code you submit.**
If AI writes a function and you can't say what each line does and why,
you have not learned. You've outsourced thinking. The checkpoint will
catch the gap. The first real project will catch it harder. Use AI as
a tutor and a sparring partner — never as a ghostwriter.
## Rule 2 — Help the Cadet Next to You
This is a peer-learning environment. There are no professors here.
There are people who landed last month, last week, and yesterday — and
right now, you.
Two things follow from that:
- **When you understand something, teach it.** Explaining a concept to
someone else cements it in your own head better than reading it twice.
- **When you don't understand something, ask.** Your peers solved this
exact problem hours, days, or weeks ago. Their explanation will
usually beat ours.
The cohort is the curriculum. Treat it that way and the program runs
on rails. Ignore it and you'll fall behind alone.
## Rule 3 — Struggle First, for Fifteen Minutes
When you get stuck — and you will, every day — the instinct is to ask
right away. Resist it.
For fifteen minutes, struggle. Re-read the briefing. Read the error
message slowly. Try one thing. Try a second thing. Notice what you
expected versus what actually happened.
Most of your real learning happens in those fifteen minutes. Skip them
and you don't just miss the answer — you miss the *thinking*. After
fifteen, ask. Not before.
## Asking a Good Question
When you do ask — peer, AI, or staff — three things make a good
question:
1. **Context.** What were you trying to do?
2. **What you tried.** What approach did you take? What error did you
see?
3. **The exact error.** Copy it. Don't summarize it.
A good question gets answered in ten minutes. A bad question gets
ignored for an hour, or worse, gets the wrong answer because nobody
understood what you meant.
> **[CLOSING — Mission Control]**
>
> Two days in, you'll have habits. Make sure they're the right ones.
>
> Six Git challenges follow. Then a paired battle. Then Python.
>
> Keep going.
>
> [END TRANSMISSION]

View File

@@ -0,0 +1,38 @@
---
type: challenge
title: "The Identity"
xp: 50
duration: 15
difficulty: 2
---
# The Identity
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, before Git records any of your work, it needs to know who's
> making the changes. Every commit you'll ever make will carry your
> name and email.
>
> When your script runs, a fresh Git repository already exists in the
> current directory. Set the local identity:
>
> ```bash
> git config user.name "Your Name"
> git config user.email "you@example.com"
> ```
>
> Without `--global`, these settings only apply to this one repo —
> exactly what we want for a contained challenge.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, set `user.name` and `user.email` on the
current Git repo.
## Objectives
- `user.name` is set on the repo (non-empty)
- `user.email` is set on the repo and looks like an email

View File

@@ -0,0 +1,12 @@
#!/bin/bash
# The Identity — tell Git who you are.
#
# When this script runs, a fresh Git repo is already initialized in
# the current directory. Your script must set both:
#
# - user.name (any non-empty string)
# - user.email (must contain @ and a dot)
#
# Tools: git config <key> "<value>"
# Your code here.

View File

@@ -0,0 +1,13 @@
#!/bin/bash
git init -q -b main
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
NAME=$(git config --local --get user.name 2>/dev/null)
EMAIL=$(git config --local --get user.email 2>/dev/null)
[ -n "$NAME" ]; report $? "user.name is set on the repo"
[[ "$EMAIL" == *@*.* ]]; report $? "user.email is set and looks like an email"

View File

@@ -0,0 +1,43 @@
---
type: challenge
title: "The Initiator"
xp: 50
duration: 20
difficulty: 2
---
# The Initiator
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, ground zero. Take an empty directory and turn it into a
> Git project.
>
> Two commands today:
>
> - `git init` — turns the current directory into a Git repository
> (creates a hidden `.git/` folder)
> - `git status` — shows what Git sees: branch, tracked, untracked
>
> Your script must:
>
> 1. Run `git init` (use `-b main` to set the default branch)
> 2. Capture `git status` to `status-before.txt`
> 3. Create `intro.md` containing `Hello, Git`
> 4. Capture `git status` to `status-after.txt`
>
> Notice the second status sees the new file as *untracked* — that
> distinction is the heart of how Git works.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, write the four steps above.
## Objectives
- `.git/` exists
- `intro.md` contains `Hello, Git`
- `status-before.txt` looks like git status output
- `status-after.txt` mentions `intro.md`

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# The Initiator — turn an empty directory into a Git repo.
#
# Your script must:
# 1. Initialize a Git repo (use main as the default branch)
# 2. Capture `git status` to status-before.txt
# 3. Create intro.md containing exactly "Hello, Git"
# 4. Capture `git status` to status-after.txt
#
# Notice how the second status sees intro.md as untracked.
#
# Tools: git init, git status, echo
# Your code here.

View File

@@ -0,0 +1,13 @@
#!/bin/bash
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -d .git ]; report $? ".git/ exists"
[ -f intro.md ]; report $? "intro.md exists"
grep -q "Hello, Git" intro.md 2>/dev/null; report $? "intro.md contains 'Hello, Git'"
[ -f status-before.txt ]; report $? "status-before.txt exists"
grep -qi "branch" status-before.txt 2>/dev/null; report $? "status-before.txt looks like git status output"
[ -f status-after.txt ]; report $? "status-after.txt exists"
grep -q "intro.md" status-after.txt 2>/dev/null; report $? "status-after.txt mentions intro.md"

View File

@@ -0,0 +1,41 @@
---
type: challenge
title: "The Saver"
xp: 75
duration: 25
difficulty: 2
---
# The Saver
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, snapshots. Git's whole purpose is to capture *commits* —
> snapshots of your work — that you can return to later.
>
> The flow per commit:
>
> 1. Create or modify a file
> 2. `git add <file>` — stage it
> 3. `git commit -m "<message>"` — lock it in
>
> When your script runs, a clean Git repo with identity already set
> sits in the working directory. Make three commits, in this exact
> order:
>
> 1. Create `a.txt` with `alpha`. Commit message: `add alpha`
> 2. Create `b.txt` with `beta`. Commit message: `add beta`
> 3. Create `c.txt` with `gamma`. Commit message: `add gamma`
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, run six commands per the flow above (one
echo + add + commit per file).
## Objectives
- `a.txt`, `b.txt`, `c.txt` exist with `alpha`, `beta`, `gamma`
- `git log` shows exactly 3 commits
- Commit messages: `add alpha`, `add beta`, `add gamma` in order

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# The Saver — make three commits.
#
# When this script runs, a fresh Git repo with identity already set
# is in your working directory. Make three commits, in this exact
# order:
#
# 1. Create a.txt containing "alpha". Commit message: "add alpha"
# 2. Create b.txt containing "beta". Commit message: "add beta"
# 3. Create c.txt containing "gamma". Commit message: "add gamma"
#
# Tools: echo, git add, git commit -m
# Your code here.

View File

@@ -0,0 +1,20 @@
#!/bin/bash
git init -q -b main
git config user.name "Setup"
git config user.email "setup@learnroom.local"
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
{ [ -f a.txt ] && grep -q alpha a.txt 2>/dev/null; }; report $? "a.txt exists with 'alpha'"
{ [ -f b.txt ] && grep -q beta b.txt 2>/dev/null; }; report $? "b.txt exists with 'beta'"
{ [ -f c.txt ] && grep -q gamma c.txt 2>/dev/null; }; report $? "c.txt exists with 'gamma'"
COUNT=$(git log --oneline 2>/dev/null | wc -l | tr -d ' ')
[ "$COUNT" = "3" ]; report $? "exactly 3 commits in git log"
MSGS=$(git log --reverse --format='%s' 2>/dev/null)
EXPECTED=$'add alpha\nadd beta\nadd gamma'
[ "$MSGS" = "$EXPECTED" ]; report $? "commit messages: 'add alpha', 'add beta', 'add gamma' in order"

View File

@@ -0,0 +1,40 @@
---
type: challenge
title: "The Historian"
xp: 75
duration: 25
difficulty: 3
---
# The Historian
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, the repo in your working directory contains five commits
> from a previous mission. We need a historian's eye on it.
>
> Two commands today:
>
> - `git log --oneline` — compact one-line-per-commit history
> - `git show <hash>` — show what a commit changed
>
> Your script must produce two files:
>
> 1. `log.txt` — full one-line log
> 2. `breach-commit.txt` — short hash of the commit whose message
> mentions `BREACH`
>
> Hint: pipe the log through `grep BREACH | awk '{print $1}'` to
> extract the short hash from the matching line.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`, capture the full log and the breach commit's
short hash.
## Objectives
- `log.txt` lists all 5 commits, one per line
- `breach-commit.txt` contains the short hash of the BREACH commit

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# The Historian — read git history.
#
# When this script runs, the current Git repo already has 5 commits
# from a prior mission. One of them mentions BREACH.
#
# Your script must produce two files:
# - log.txt — `git log --oneline` output (one line per commit)
# - breach-commit.txt — the short hash of the BREACH commit
#
# Hint: `git log --oneline | grep BREACH | awk '{print $1}'`
# pipes the matching log line and prints the leftmost column (the
# short hash).
# Your code here.

View File

@@ -0,0 +1,24 @@
#!/bin/bash
git init -q -b main
git config user.name "Setup"
git config user.email "setup@learnroom.local"
echo "manifest" > manifest.txt; git add manifest.txt; git commit -q -m "add manifest"
echo "sensor a online" > sensor-a.txt; git add sensor-a.txt; git commit -q -m "add sensor A"
echo "breach details" > breach.txt; git add breach.txt; git commit -q -m "investigate BREACH at sector C"
echo "sensor b online" > sensor-b.txt; git add sensor-b.txt; git commit -q -m "add sensor B"
echo "resolution applied" > resolution.txt; git add resolution.txt; git commit -q -m "apply resolution"
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -f log.txt ]; report $? "log.txt exists"
LINES=$(wc -l < log.txt | tr -d ' ')
[ "$LINES" = "5" ]; report $? "log.txt has 5 lines (one per commit)"
[ -f breach-commit.txt ]; report $? "breach-commit.txt exists"
ACTUAL=$(tr -d '[:space:]' < breach-commit.txt 2>/dev/null)
EXPECTED=$(git log --grep BREACH --format=%h)
[ "$ACTUAL" = "$EXPECTED" ]; report $? "breach-commit.txt contains the BREACH commit's short hash"

View File

@@ -0,0 +1,45 @@
---
type: challenge
title: "The Cloner"
xp: 75
duration: 25
difficulty: 3
---
# The Cloner
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, real work doesn't start from scratch. You join an existing
> project — clone it, make changes, commit.
>
> When your script runs, `remote.git` is a *bare repository* (think
> of it as a project living on a server) sitting in your working
> directory. Clone it into a folder called `project`:
>
> ```bash
> git clone remote.git project
> ```
>
> Then inside it, set your identity (Git won't let you commit
> without one), create `log-entry.txt` containing exactly
> `Cadet log entry — checked in.`, and commit with message
> `add cadet log entry`.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`:
1. Clone `remote.git` into `project`
2. `cd project`
3. Set `user.name` and `user.email`
4. Create `log-entry.txt` with the exact line above
5. Add and commit with the exact message
## Objectives
- `project/.git` exists
- `project/log-entry.txt` matches the exact content
- `project/` has at least 2 commits in its log

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# The Cloner — clone a remote and add a commit.
#
# When this script runs, a bare repository `remote.git` is in your
# working directory. Your script must:
#
# 1. Clone remote.git into a folder called `project`
# 2. Inside project/, set user.name and user.email
# 3. Create log-entry.txt containing exactly:
# Cadet log entry — checked in.
# 4. Stage and commit it with message: "add cadet log entry"
#
# Tools: git clone, cd, git config, echo, git add, git commit -m
# Your code here.

View File

@@ -0,0 +1,29 @@
#!/bin/bash
TMP=$(mktemp -d)
(
cd "$TMP"
git init -q -b main
git config user.name "Setup"
git config user.email "setup@learnroom.local"
echo "# Mission Project" > README.md
echo "Initial mission setup." > mission.txt
git add .
git commit -q -m "initial mission setup"
)
git clone -q --bare "$TMP" remote.git
rm -rf "$TMP"
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
[ -d project/.git ]; report $? "project/.git exists (clone happened)"
[ -f project/log-entry.txt ]; report $? "project/log-entry.txt exists"
ACTUAL=$(cat project/log-entry.txt 2>/dev/null)
ACTUAL="${ACTUAL%$'\n'}"
[ "$ACTUAL" = "Cadet log entry — checked in." ]; report $? "log-entry.txt contains the exact line"
COUNT=$(git -C project log --oneline 2>/dev/null | wc -l | tr -d ' ')
[ "$COUNT" -ge 2 ] 2>/dev/null
report $? "project has at least 2 commits"

View File

@@ -0,0 +1,43 @@
---
type: challenge
title: "The Pusher"
xp: 100
duration: 30
difficulty: 3
---
# The Pusher
> **[INCOMING — Mission Control, Earth]**
>
> Cadet, your commits live only on your machine until you *push* them
> somewhere shared. That's how teams work.
>
> When your script runs, you'll find:
>
> - `local-repo/` — a working repo with two commits already, identity set
> - `remote.git/` — an empty bare repo (your shared destination)
>
> Two commands:
>
> - `git remote add <name> <path>` — register a remote and give it a
> short name (by convention, `origin`)
> - `git push -u <remote> <branch>` — send your branch's commits to that
> remote (`-u` sets it as default for next time)
>
> Wire `local-repo` to `remote.git` and push.
>
> [END TRANSMISSION]
## Your Task
In `starter/starter.sh`:
1. `cd local-repo`
2. Add `../remote.git` as a remote named `origin`
3. Push `main` to `origin` with `-u`
## Objectives
- `local-repo` has a remote `origin` configured
- `remote.git` has the same commits on `main` as `local-repo`

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# The Pusher — send your commits to a remote.
#
# When this script runs:
# - local-repo/ is a working repo with commits + identity already set
# - remote.git/ is a bare empty repo waiting for your push
#
# Your script must:
# 1. Inside local-repo, register remote.git as a remote named `origin`
# pointing at ../remote.git
# 2. Push the main branch to origin (use -u to set upstream)
#
# Tools: cd, git remote add, git push
# Your code here.

View File

@@ -0,0 +1,27 @@
#!/bin/bash
mkdir -p local-repo
(
cd local-repo
git init -q -b main
git config user.name "Cadet Vega"
git config user.email "cadet.vega@learnroom.local"
echo "# Mission" > README.md
git add README.md; git commit -q -m "initial commit"
echo "Day 1 log" > log.txt
git add log.txt; git commit -q -m "add log"
)
git init -q --bare remote.git
bash solution.sh > /dev/null 2>&1
N=0
report() { N=$((N+1)); if [ "$1" = "0" ]; then echo "ok $N - $2"; else echo "not ok $N - $2"; fi; }
ORIGIN_URL=$(git -C local-repo remote get-url origin 2>/dev/null)
[ -n "$ORIGIN_URL" ]; report $? "'origin' remote configured on local-repo"
LOCAL_COUNT=$(git -C local-repo log --oneline main 2>/dev/null | wc -l | tr -d ' ')
REMOTE_COUNT=$(git -C remote.git log --oneline main 2>/dev/null | wc -l | tr -d ' ')
[ -n "$REMOTE_COUNT" ] && [ "$REMOTE_COUNT" != "0" ]; report $? "remote.git has commits on main"
[ "$LOCAL_COUNT" = "$REMOTE_COUNT" ]; report $? "remote.git has same number of commits as local-repo"

View File

@@ -0,0 +1,63 @@
---
type: battle
title: "Team Build"
xp: 200
duration: 90
difficulty: 3
---
# Team Build
> **[INCOMING — Mission Control, Earth]**
>
> Cadets, this is your final task before Python. You and a partner will
> automate a mission setup together — one writing, the other
> reviewing. Then swap.
>
> Pair up. Decide who goes first as **defender** and who goes first as
> **attacker**.
>
> [END TRANSMISSION]
## The Task
The **defender** writes a single shell script — `launch.sh` — that,
when run inside an empty workspace, does the following in order:
1. Creates a folder called `mission/` and enters it.
2. Initializes a fresh Git repo on branch `main`.
3. Sets `user.name` and `user.email` on the repo (use your own).
4. Creates `manifest.txt` containing exactly:
```
Mission Apollo
```
5. Creates `crew.txt` containing exactly:
```
Cadet A, Cadet B
```
6. Creates `coords.txt` containing exactly:
```
lat: 0.0, lon: 0.0
```
7. Commits each file separately, in this exact order, with messages:
- `add manifest`
- `add crew`
- `add coords`
8. Prints `MISSION READY` to stdout when done.
## Battle Rules
- **Defender**: writes `launch.sh` from scratch in a clean workspace.
Does not show the file until done. Time limit: 30 minutes.
- **Attacker**: receives the script, runs it in a clean folder,
inspects the resulting `mission/`, and submits a review through the
platform's review form.
- **Then swap**: roles flip. The new defender writes their own
`launch.sh` (no copy-pasting). 30 more minutes.
- Both pass the battle if both scripts pass review.
## Tools You'll Use
Everything you've learned this module:
`mkdir`, `cd`, `echo`, `>`, `git init`, `git config`, `git add`,
`git commit`, and a shebang to make the script run.

View File

@@ -0,0 +1,70 @@
# Team Build — Extended Notes
*Not rendered in the platform yet. Reference for instructors and for
future "learn more" surfaces.*
## Purpose
The capstone of `1.welcome`. By this point a cadet has used every
tool they need: `mkdir`, `echo`, `git init`, `git config`, `git add`,
`git commit`, and shell scripting with `chmod +x`. This battle forces
them to combine all of it into a single artifact — a working
automation script — and to read someone else's script for the first
time.
## Skills Demonstrated
- Composing learned commands into a sequential program
- Writing a script with a correct shebang and execute bit
- Producing exact output from automation
- Reading another cadet's code well enough to score it
- Giving and receiving honest feedback under a time limit
## Common Pitfalls
- **One commit covering all three files** — defenders forget that the
spec says *each file commits separately*. Three commits, not one.
- **File contents with extra whitespace** — `echo` adds a trailing
newline by default, which is fine; trailing spaces on the line are
not. Reviewers should `cat -A` to spot invisible characters.
- **Missing or wrong identity** — `git commit` will succeed even
without `user.name` set if a fallback is configured globally;
reviewers should check `git -C mission config user.name` directly.
- **Hardcoded absolute paths** — `cd /Users/...` breaks the moment
the script runs on someone else's machine. Use relative paths.
- **`MISSION READY` printed inside another message** — the spec says
"exactly". `echo "Done. MISSION READY now."` should fail review.
- **Forgetting `chmod +x`** — script runs fine via `bash launch.sh`
but fails when executed directly. Reviewers should test both.
## Discussion Prompts (post-battle)
Ask the pair to discuss for 5 minutes after both rounds:
1. Whose script was easier to read? Why?
2. Was anything in the spec ambiguous? How would you tighten it?
3. What would you do differently if you wrote this from scratch
today?
4. Did you find a bug your partner missed?
## Alternative Approaches
A cadet who's seen heredocs might write:
```bash
cat > manifest.txt <<EOF
Mission Apollo
EOF
```
Both `echo "..." > file` and heredoc are acceptable. The spec
doesn't require a specific style — only that the resulting file
content match exactly.
## Why This Is the Last Block of Welcome
This is the bridge between mechanical skill (typing commands) and
real engineering (composing them, reading others' code, judging
quality). Cadets who pass this battle have proven they can act as
both author and reviewer — the foundational loop of every
collaborative codebase. Python is built on this foundation.

View File

@@ -0,0 +1,38 @@
[
{
"title": "Script Quality",
"icon": "ph:code-duotone",
"items": [
"Script starts with #!/bin/bash shebang",
"Script is executable (chmod +x applied)",
"Script runs without errors",
"Prints exactly 'MISSION READY' at the end"
]
},
{
"title": "Repo State",
"icon": "ph:git-branch-duotone",
"items": [
"mission/.git/ exists after running the script",
"user.name is set on the mission repo",
"user.email is set and looks like an email"
]
},
{
"title": "Files",
"icon": "ph:files-duotone",
"items": [
"manifest.txt contains exactly 'Mission Apollo'",
"crew.txt contains exactly 'Cadet A, Cadet B'",
"coords.txt contains exactly 'lat: 0.0, lon: 0.0'"
]
},
{
"title": "Commits",
"icon": "ph:list-duotone",
"items": [
"Three separate commits exist (not one combined commit)",
"Commit messages, in order: 'add manifest', 'add crew', 'add coords'"
]
}
]