De-slop: remove every em-dash + banned words across all modules + capstone (#94)
Sync course wiki / sync-wiki (push) Successful in 4s

Co-authored-by: claude <claude@jpaul.io>
Co-committed-by: claude <claude@jpaul.io>
This commit was merged in pull request #94.
This commit is contained in:
2026-06-22 23:21:22 -04:00
committed by Claude (agent)
parent 513d7e7ac8
commit c098933f25
99 changed files with 1324 additions and 1315 deletions
@@ -1,6 +1,6 @@
# Module 6 Branches: Sandboxes for Experiments
# Module 6: Branches as Sandboxes for Experiments
> **A branch is a disposable copy of your project where the AI can try anything and `main` never
> **A branch is a disposable copy of your project where the AI can try anything, and `main` never
> finds out unless you decide it should.** This is what turns "let the agent attempt something bold"
> from a gamble into a one-line decision: keep it or throw it away.
@@ -8,19 +8,19 @@
## Prerequisites
- **Module 2 Version Control as a Safety Net.** You can `init`, `commit`, read `git diff`/`git
- **Module 2: Version Control as a Safety Net.** You can `init`, `commit`, read `git diff`/`git
log`/`git status`, and `git restore` an unwanted change. Branches build directly on commits: a
branch is just a label on the commit history you already understand.
- **Module 3 Version Control for Words.** You first met `git branch`, `git switch -c`, `git merge`,
and `git branch -d` there on a markdown doc, where a mistake costs nothing and the merge always
- **Module 3: Version Control for Words.** You first met `git branch`, `git switch -c`, `git merge`,
and `git branch -d` there, on a markdown doc, where a mistake costs nothing and the merge always
fast-forwarded. This module takes those same verbs to *code*, where branches actually diverge and
merges can conflict.
- **Module 4 Getting the AI Out of the Browser.** The AI now edits your real files directly from
your editor. That's exactly the capability that makes branches matter you're about to let it edit
- **Module 4: Getting the AI Out of the Browser.** The AI now edits your real files directly from
your editor. That's exactly the capability that makes branches matter; you're about to let it edit
files *fast and confidently*, and you want a wall around the blast radius.
- **Module 5 Commit the AI's Config, Not Just the Code.** Your committed instructions file travels
- **Module 5: Commit the AI's Config, Not Just the Code.** Your committed instructions file travels
with the branch automatically, so an agent working on a branch inherits the same setup. (You'll see
this for free in the lab nothing to do, just notice it.)
this for free in the lab; nothing to do, just notice it.)
Module 2's `git restore` undoes *uncommitted* changes back to your last checkpoint. This module is
the next size up: isolating *a whole line of committed work* so you can keep or discard it as a unit.
@@ -157,7 +157,7 @@ each, keep the winner, delete the loser. The branch is the unit of "maybe."
### Merge conflicts: when two changes collide
Most merges just work Git is good at combining changes that touch *different* lines. A **conflict**
Most merges just work; Git is good at combining changes that touch *different* lines. A **conflict**
happens only when two branches changed **the same lines** in different ways, and Git refuses to
guess which one you meant. It stops the merge and marks the collision *inside the file* so you can
decide:
@@ -172,8 +172,8 @@ decide:
Read it like this:
- `<<<<<<< HEAD` to `=======` is **your current branch's version** (the branch you're merging *into*
`main`, here).
- `<<<<<<< HEAD` to `=======` is **your current branch's version** (the branch you're merging *into*,
`main`, here).
- `=======` to `>>>>>>> experiment` is **the incoming branch's version**.
- Both markers and the divider are real text Git inserted into your file. Resolving means **editing
the file so it contains the version you want and deleting all three marker lines.**
@@ -196,19 +196,19 @@ things go sideways, `git merge --abort` rewinds to before the merge with no harm
Everything above is standard Git. Here's why it matters *more* in an AI-assisted workflow, not less:
- **The branch is the blast-radius container for an autonomous attempt.** An agent editing your files
directly (Module 4) is fast and confident including when it's confidently wrong across four
directly (Module 4) is fast and confident, including when it's confidently wrong across four
files. On `main`, cleaning that up is a chore. On a branch, you delete the branch. The riskier and
more autonomous the AI work, the more a branch earns its keep which is why this concept underpins
more autonomous the AI work, the more a branch earns its keep, which is why this concept underpins
everything in Unit 5, where agents run with far less supervision.
- **"Throw it away" is the feature, not the failure.** With copy-paste, a rejected AI attempt still
cost you the manual work of pasting it in and the manual work of ripping it back out. With a
branch, a rejected attempt costs *nothing* `git branch -D` and it's as if it never happened. That
branch, a rejected attempt costs *nothing*: `git branch -D` and it's as if it never happened. That
flips the economics: you can let the AI try things you'd never risk if undoing were expensive.
- **Compare, don't commit-and-hope.** Ask the AI for approach A on one branch and approach B on
another. Run both. Keep the winner, delete the loser. You're using branches as cheap A/B
experiments on implementation something that's painful without them and trivial with them.
experiments on implementation, something that's painful without them and trivial with them.
- **Conflicts are a great place to put the AI to work.** A merge conflict is a small, perfectly
bounded reasoning task: here are two versions of the same lines and the surrounding code produce
bounded reasoning task: here are two versions of the same lines and the surrounding code; produce
the correct combined version. The AI can see both sides and the intent. You still decide whether
its resolution is right (it can absolutely merge two changes into something that satisfies neither),
but "explain this conflict and propose a resolution" is one of the highest-hit-rate uses of an
@@ -222,20 +222,20 @@ Everything above is standard Git. Here's why it matters *more* in an AI-assisted
editor-integrated AI from Module 4.
You'll do three things: let the AI try a bold change on a branch, decide its fate, and then
deliberately create and resolve a merge conflict using the AI to help resolve it.
deliberately create and resolve a merge conflict, using the AI to help resolve it.
**You'll need:**
- The `tasks-app` Git repo from Module 2 (committed, clean working tree run `git status` and make
- The `tasks-app` Git repo from Module 2 (committed, clean working tree; run `git status` and make
sure it says "nothing to commit").
- Your editor-integrated AI from Module 4.
- Git (you've had it since Module 2).
> Throughout, "ask your AI" now means your **editor-integrated** agent (Module 4) editing the files
> directly no more copy-paste. After it edits, you still read `git diff` before committing. That
> directly, no more copy-paste. After it edits, you still read `git diff` before committing. That
> habit doesn't go away; the branch just decides how *much* damage a bad diff can do.
### Part A Branch it and let the AI go bold
### Part A: Branch it and let the AI go bold
1. Make sure you're in the repo, then **tell the agent to set up the branch.** Ask:
@@ -289,13 +289,13 @@ deliberately create and resolve a merge conflict — using the AI to help resolv
Your bold change exists only on the branch. `main` never saw it, and that's the whole point.
### Part B Decide its fate
### Part B: Decide its fate
**The decision is yours; the execution is the agent's.** Pick the path that matches reality. Do at
least one; ideally do **Path 2 (discard)** on this experiment so you feel how clean it is, then re-run
Part A and do **Path 1 (keep)** so you've done both.
**Path 1 Keep it (merge).** Tell the agent:
**Path 1: Keep it (merge).** Tell the agent:
> *"Merge `experiment/priorities` into `main`, then delete the branch."*
@@ -307,7 +307,7 @@ python cli.py list # the feature is now on main
git branch # experiment/priorities is gone
```
**Path 2 Throw it away (discard).** Tell the agent:
**Path 2: Throw it away (discard).** Tell the agent:
> *"Switch to `main` and discard the `experiment/priorities` branch entirely."*
@@ -323,16 +323,16 @@ Notice what you did *not* do in Path 2: no file-by-file `restore`, no manual und
diffs. The agent deleted a label and the entire experiment was gone. That's the economics shift: bold
AI attempts become free to reject.
### Part C Create a merge conflict and resolve it with the AI
### Part C: Create a merge conflict and resolve it with the AI
Merge conflicts have an outsized reputation for difficulty. You'll engineer a guaranteed one by having
**two branches change the same line in different ways**, then resolve it with the agent.
> **Starting state.** By now your `tasks-app` has accumulated commands from earlier modules, so your
> `usage:` line is longer than the bare `[add <title> | list | done <index>]` you started with and
> `usage:` line is longer than the bare `[add <title> | list | done <index>]` you started with, and
> that's fine. This lab works *regardless* of what's on that line, because the collision is just "two
> branches each appended a different new command to the same usage line." To make it reproduce even on
> a carried-forward app, we deliberately add two commands you **haven't** built yet `stats` and
> a carried-forward app, we deliberately add two commands you **haven't** built yet: `stats` and
> `purge`. (Any two brand-new commands would do; the point is the same line, edited two ways.) The
> marker examples below show the shape; your real markers will carry your fuller usage string.
@@ -376,7 +376,7 @@ Merge conflicts have an outsized reputation for difficulty. You'll engineer a gu
```
4. Open `cli.py` and find the conflict markers around the usage line (your usage string will be
longer it carries the commands from earlier modules but the collision is exactly this: both
longer (it carries the commands from earlier modules), but the collision is exactly this: both
branches appended a different new command to it):
```python
@@ -388,7 +388,7 @@ Merge conflicts have an outsized reputation for difficulty. You'll engineer a gu
```
(The command bodies for `stats` and `purge` touch different lines, so Git merged *those* cleanly
on its own the only collision is the usage string both branches edited.)
on its own; the only collision is the usage string both branches edited.)
5. **Resolve it with the AI.** This is exactly the bounded task the agent is good at. Ask:
@@ -401,13 +401,13 @@ Merge conflicts have an outsized reputation for difficulty. You'll engineer a gu
print("usage: python cli.py [add <title> | list | done <index> | stats | purge]")
```
**Verify its work this is the part the AI can get subtly wrong.** A conflict resolver can
**Verify its work; this is the part the AI can get subtly wrong.** A conflict resolver can
confidently drop one side, leave a stray marker, or "blend" the lines into something that runs but
means the wrong thing. Read the result and run it:
```bash
git diff # check ONLY what you intended changed; no markers remain
python cli.py # run with no args see the merged usage string
python cli.py # run with no args, see the merged usage string
python cli.py stats # both commands actually work
python cli.py purge
```
@@ -429,7 +429,7 @@ Merge conflicts have an outsized reputation for difficulty. You'll engineer a gu
> **Guaranteed-conflict generator.** AI edits are nondeterministic, so if the agent didn't touch the
> same line on both branches and you *didn't* get a conflict in step 3, run the helper script to
> manufacture one deterministically, then practice steps 46 on it. Copy it into your `tasks-app`
> first (the course's lab scripts live in the course repo, not in `tasks-app` see Module 4's
> first (the course's lab scripts live in the course repo, not in `tasks-app`; see Module 4's
> *You'll need*), then run it from inside the repo:
>
> ```bash
@@ -448,20 +448,20 @@ Merge conflicts have an outsized reputation for difficulty. You'll engineer a gu
The honest limits, so you don't over-trust the sandbox:
- **A branch isolates *files in the repo*, nothing else.** Switching branches rewrites your tracked
files it does **not** roll back a database the app wrote to, files Git is ignoring, running
files; it does **not** roll back a database the app wrote to, files Git is ignoring, running
processes, or anything outside version control. If your AI experiment ran a migration or wrote to
`tasks.json` (which the Module 2 `.gitignore` excludes), deleting the branch won't undo *that*. The
sandbox is the repo, not the world. (Real environment isolation is a later problem containers,
sandbox is the repo, not the world. (Real environment isolation is a later problem: containers,
Module 16.)
- **Branches are local until you push them.** Everything in this module lives on your laptop. A
branch isn't shared, backed up, or visible to anyone else until there's a remote that's
branch isn't shared, backed up, or visible to anyone else until there's a remote; that's
**Module 8**. Right now `git branch -D` deletes work that exists nowhere else, permanently. Treat
an unpushed branch as exactly as fragile as the rest of your local-only repo.
- **The AI can resolve a conflict into something plausible and wrong.** It sees both sides and the
intent, which makes it good at this but "good" isn't "trusted." A resolution that runs cleanly can
intent, which makes it good at this, but "good" isn't "trusted." A resolution that runs cleanly can
still mean the wrong thing (silently keeping the worse of two changes, or merging two behaviors
into one that satisfies neither). The `git diff` + run-it check in the lab isn't optional ceremony;
it's the actual safeguard. Reviewing AI output is its own discipline Module 10.
it's the actual safeguard. Reviewing AI output is its own discipline; that's Module 10.
- **Long-lived branches drift and conflict harder.** The longer a branch lives away from `main`, the
more `main` moves underneath it and the gnarlier the eventual merge. The defense is the same as
"commit often": branch small, merge soon, delete promptly. A branch that's been open for three
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
#
# make-conflict.sh manufacture a guaranteed merge conflict to practice on.
# make-conflict.sh: manufacture a guaranteed merge conflict to practice on.
#
# AI edits are nondeterministic, so the lab's organic conflict (two branches editing the same usage
# line in cli.py) doesn't ALWAYS land. This script guarantees one: it creates two branches that each
# append a different line to the same spot in README.md, then leaves you mid-merge with a real
# conflict in your working tree. The resolution mechanic is identical to the code case in the lab
# conflict in your working tree. The resolution mechanic is identical to the code case in the lab:
# read the <<<<<<< / ======= / >>>>>>> markers, edit to the version you want, remove the markers,
# then `git add` + `git commit`.
#