feat(course): build out all 27 modules, capstone, scaffold, and conventions

Scaffold the course repo and author the full curriculum in dependency-chain
order, following the settled build decisions in handoff.md.

- Scaffold: course README, vendor-neutral AGENTS.md (dogfoods Module 5),
  _TEMPLATE.md (the fixed 9-section module shape), root .gitignore, ship config.
- Modules 1-2: reference exemplars (locked for tone/depth/lab style).
- Modules 3-27: full lessons + runnable labs, each following the template,
  respecting the chain, vendor/model-agnostic, with "feel the pain" labs.
- Module 8 hosting comparison web-researched and date-stamped (as of 2026-06-22),
  not written from memory; expansion-zone modules carry Verify-before-publish.
- Capstone: the full loop end to end on the running tasks-app example.

Lab code syntax-checked (Python/shell/YAML); every module has the 7 core
template sections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TfzV5QvtPDz8LJS3Pu5VLT
This commit is contained in:
2026-06-22 12:18:30 -04:00
parent 4bd586bbd0
commit fbec36cb67
117 changed files with 15131 additions and 1 deletions
@@ -0,0 +1,22 @@
# Agent prompt — issue #42, branch `feature/42-count`
Run this in the `tasks-app-42-count` worktree. This agent's work is genuinely parallel with #43
(docs) — different files — and deliberately collides with #44 (clear) at `cli.py`'s dispatch chain.
---
You are working in this worktree only. Do not touch any other folder.
**Task:** Add a `count` command to `cli.py` that prints the number of *pending* (not-done) tasks.
- Add a new `elif command == "count":` branch to the dispatch in `main()` in `cli.py`.
- Use the existing `TaskList.pending()` method from `tasks.py` — do not change `tasks.py`.
- Print just the integer, e.g. `3`.
**Acceptance criteria:**
- `python cli.py count` prints the number of pending tasks and exits 0.
- No other files change. (`README.md`, `CHANGELOG.md`, and `tasks.py` are owned by other agents —
stay out of them.)
When done, stop. The human commits, pushes, and opens the PR.
@@ -0,0 +1,26 @@
# Agent prompt — issue #43, branch `feature/43-docs`
Run this in the `tasks-app-43-docs` worktree. This agent owns documentation only — different files
from every other agent in the fleet, so it merges cleanly no matter what the others do. This is what
a *genuinely* parallel split looks like: disjoint files, no shared interface.
---
You are working in this worktree only. Do not touch any other folder, and do not edit `cli.py` or
`tasks.py` — code is owned by other agents.
**Task:** Document the `tasks-app` and start a changelog.
- In `README.md`, add a "Commands" section documenting the existing commands: `add <title>`, `list`,
and `done <index>`. Show an example invocation for each.
- Create `CHANGELOG.md` with a "Keep a Changelog"style `## [Unreleased]` section and an `### Added`
list. (Other agents are adding commands in parallel; leave a placeholder line noting that new
commands are landing — the human will reconcile the exact list at merge.)
**Acceptance criteria:**
- `README.md` documents the three existing commands accurately.
- `CHANGELOG.md` exists and is valid markdown.
- No code files change.
When done, stop. The human commits, pushes, and opens the PR.
@@ -0,0 +1,24 @@
# Agent prompt — issue #44, branch `feature/44-clear`
Run this in the `tasks-app-44-clear` worktree. **This agent deliberately collides with #42.** Both
add a new `elif` to the same dispatch chain in `cli.py` — same file, same region. That's the
agent-vs-agent merge conflict the lab wants you to predict in Part A and resolve in Part C. It is not
a mistake in the lab; it is the lesson. Two agents on the same file is a *joint*, not a seam.
---
You are working in this worktree only. Do not touch any other folder.
**Task:** Add a `clear` command to `cli.py` that removes all tasks.
- Add a new `elif command == "clear":` branch to the dispatch in `main()` in `cli.py`.
- It should empty the task list and save, then print `cleared`.
- Reuse the existing `load()` / `save()` helpers. Do not change `tasks.py`.
**Acceptance criteria:**
- `python cli.py clear` removes all tasks and prints `cleared`.
- `python cli.py list` afterward shows `(no tasks yet)`.
When done, stop. The human commits, pushes, and opens the PR — and should expect a conflict against
`feature/42-count` at merge.
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Module 26 lab — tear down the fleet after the work has merged.
#
# Removes each worktree and prunes stale records. Refuses to remove a worktree with uncommitted
# work (Git's safety) — commit or merge first. Run from inside your tasks-app repo.
set -euo pipefail
FLEET=(
"../tasks-app-42-count"
"../tasks-app-43-docs"
"../tasks-app-44-clear"
)
git rev-parse --git-dir >/dev/null 2>&1 || { echo "not a git repo" >&2; exit 1; }
for path in "${FLEET[@]}"; do
if [ -d "$path" ]; then
echo "remove: $path"
git worktree remove "$path" # fails if dirty — that's intentional; commit first
fi
done
git worktree prune
echo
echo "Fleet torn down. Remaining worktrees:"
git worktree list
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Module 26 lab — fan work out across a fleet of worktrees.
#
# Creates one worktree per issue, each on its own issue-named branch. main is left untouched
# and reserved as the integration point. Run from inside your tasks-app repo.
#
# This is Module 7's `git worktree add` repeated with a naming convention. The convention is the
# point: branch name carries the issue number, folder name mirrors the branch.
set -euo pipefail
# Each entry: "<worktree-folder> <branch>". Edit to match your coordination plan.
FLEET=(
"../tasks-app-42-count feature/42-count"
"../tasks-app-43-docs feature/43-docs"
"../tasks-app-44-clear feature/44-clear"
)
if [ ! -d .git ] && ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "error: run this from inside your tasks-app git repo." >&2
exit 1
fi
for entry in "${FLEET[@]}"; do
# shellcheck disable=SC2086
set -- $entry
path="$1"; branch="$2"
if git worktree list --porcelain | grep -q "branch refs/heads/$branch"; then
echo "skip: $branch already checked out in a worktree"
continue
fi
echo "fan-out: $path (branch $branch)"
git worktree add "$path" -b "$branch"
done
echo
echo "Fleet is up. main is reserved for integration — no agent works there."
git worktree list
@@ -0,0 +1,45 @@
# Coordination plan — Module 26 lab
This is the artifact orchestration runs on. With one agent, the plan lived in your head. With a
fleet, it has to live here — because your head doesn't scale and it forgets (Module 2).
Fill the **Status** column as you go, and answer the questions at the bottom. The plan is the
deliverable, not the code.
---
## The fleet
| Issue | Branch | Worktree | Files owned | Depends on | Status |
|-------|--------|----------|-------------|------------|--------|
| #42 count | `feature/42-count` | `tasks-app-42-count` | `cli.py` (dispatch + new fn) | — | queued |
| #43 docs | `feature/43-docs` | `tasks-app-43-docs` | `README.md`, `CHANGELOG.md` | — | queued |
| #44 clear | `feature/44-clear` | `tasks-app-44-clear` | `cli.py` (dispatch + new fn) | — | queued |
`main` is reserved as the integration point. No agent works in the main worktree.
---
## Part A — Predict the conflicts BEFORE you launch
Read the "Files owned" column. Which pairs are genuinely parallel, and which will collide at merge?
Write your prediction here, then watch it come true in Part C.
- Genuinely parallel (disjoint files, no shared interface): _______________________
- Will collide at merge (and on which file/line): _______________________________
- If you wanted to avoid the collision, what would you change? (serialize one? scope it to a
different file?) _____________________________________________________________
---
## Part D — Score the orchestration honestly
- **Did parallel beat sequential?** Agent wall-clock (overlapping) + your serial review time +
conflict resolution, vs. "I'd have done these three myself, in order."
Answer: ______________________________________________________________________
- **Which split was worth it and which wasn't?**
Answer: ______________________________________________________________________
- **Where was the bottleneck?** (Almost certainly your review queue, not the agents. Name it.)
Answer: ______________________________________________________________________
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Module 26 lab — fleet dashboard.
#
# Prints every worktree, its branch, and how much work is in flight (uncommitted changes +
# commits ahead of main). Your "where is every agent?" view in one command. Run from anywhere
# inside the repo.
set -euo pipefail
git rev-parse --git-dir >/dev/null 2>&1 || { echo "not a git repo" >&2; exit 1; }
printf "%-32s %-22s %-10s %s\n" "WORKTREE" "BRANCH" "AHEAD" "DIRTY?"
printf "%-32s %-22s %-10s %s\n" "--------" "------" "-----" "------"
git worktree list --porcelain | awk '/^worktree /{wt=$2} /^branch /{print wt"\t"$2}' \
| while IFS=$'\t' read -r wt ref; do
branch="${ref#refs/heads/}"
name="$(basename "$wt")"
# commits on this branch not yet in main
ahead="$(git -C "$wt" rev-list --count main.."$branch" 2>/dev/null || echo "?")"
# any uncommitted changes in this worktree?
if [ -n "$(git -C "$wt" status --porcelain 2>/dev/null)" ]; then dirty="yes"; else dirty="no"; fi
printf "%-32s %-22s %-10s %s\n" "$name" "$branch" "$ahead" "$dirty"
done