Fix broken git demos/labs in Modules 4, 7, 14 (#2,#3,#4,#12) #55

Merged
claude merged 1 commits from fix/p1-broken-git-demos into main 2026-06-22 14:52:31 -04:00
3 changed files with 98 additions and 39 deletions
@@ -331,10 +331,24 @@ copy-paste loop back in Module 1, now done right.
It should add tasks, delete one by index, and confirm the right task remains. If it fails, don't
hand-fix it — tell the AI what broke and let it iterate (step 4 of the loop), then re-run.
8. **Commit the reviewed change — this is your new checkpoint.** It passed your own eyes and it
passes the check, so lock it in:
```bash
git add .
git commit -m "Add delete command (made via editor/CLI agent)"
git log --oneline
```
You just shipped a reviewed, multi-file change made by an AI editing your files directly — and the
copy-paste loop never entered into it. This commit is now the clean state `git restore .` falls
back to in the next part.
### Part D — Practice the revert (do this even though it works)
8. You only trust an undo you've used. Prove the net is under you: ask the tool for a deliberately
throwaway change —
9. You only trust an undo you've used. Your tree is clean — you just committed in Part C, which is
exactly the safe setup the one rule demands. Prove the net is under you: ask the tool for a
deliberately throwaway change —
> *"Rename every variable in `tasks.py` to single letters."*
@@ -348,18 +362,18 @@ copy-paste loop back in Module 1, now done right.
That's the Module 2 safety net catching a Module 4 mistake. Internalize how cheap that was.
### Part E — Commit the good change
### Part E — Confirm you're back at your good state
9. Now commit the `delete` feature you kept in Part C (Part D's mess is already gone):
10. Nothing left to commit the `delete` feature went in back in Part C, and Part D's throwaway is
already gone. Confirm the reviewed multi-file commit is your latest and the tree is clean:
```bash
git add .
git commit -m "Add delete command (made via editor/CLI agent)"
git log --oneline
```
```bash
git log --oneline # "Add delete command…" is the latest commit
git status # clean — the throwaway left no trace
```
You just shipped a reviewed, multi-file change made by an AI editing your files directly — and the
copy-paste loop never entered into it.
That's the whole loop closed: a reviewed, multi-file change the AI made across both files is
committed, and the mess you made on purpose vanished without touching it.
---
@@ -56,22 +56,31 @@ That's fine when *you* are the only one standing on the floor. It falls apart th
two things happening at once. Watch it break:
```bash
# Agent A is mid-change on a feature branch — uncommitted edits in cli.py
# Agent A added a `clear` command and committed it on its own branch:
git switch -c feature/clear
# ...agent A edits cli.py, hasn't committed...
# ...agent A edits the usage line in cli.py to add `clear`...
git commit -am "Add clear command"
# You want Agent B to start a different job, so you try to switch:
# You start Agent B on a fresh branch off main; it begins editing the SAME
# usage line to add `count`, and hasn't committed:
git switch main
git switch -c feature/count
# ...agent B edits cli.py, hasn't committed...
# You try to hop the working directory back to Agent A's branch to check on it:
git switch feature/clear
# error: Your local changes to the following files would be overwritten by checkout:
# cli.py
# Please commit your changes or stash them before you switch branches.
```
Git stops you — correctly. But now you're stuck choosing between bad options:
Git stops you — correctly. Switching to `feature/clear` would overwrite Agent B's uncommitted edits
to `cli.py` with Agent A's committed version of those same lines, so Git refuses rather than silently
destroy the work. But now you're stuck choosing between bad options:
- **Commit half-finished work** just to get it out of the way (pollutes history, and Agent A's job
isn't done).
- **Stash it** (now Agent A's context lives in a stash you have to remember to pop, and Agent A — a
- **Commit half-finished work** just to get it out of the way (pollutes history, and Agent B's
`count` command isn't done).
- **Stash it** (now Agent B's context lives in a stash you have to remember to pop, and Agent B — a
long-running session that thinks its files are right there — is now editing files that silently
changed under it).
- **Run both agents on the same branch in the same folder** — and watch them overwrite each other's
@@ -220,23 +229,40 @@ collide. Then you'll merge both back and clean up.
### Part A — Feel the collision (1 minute)
Before fixing it, reproduce the bottleneck from "Where branches alone run out." In your `tasks-app`:
Before fixing it, reproduce the bottleneck from "Where branches alone run out." The wall only appears
when both branches touch the **same line** of `cli.py` — one committed, one not — so we make each
branch edit the usage line. (The `sed … > tmp && mv` is just a portable, copy-pasteable stand-in for
the edit an agent would make.) In your `tasks-app`:
```bash
cd ~/workflow-course/tasks-app
git switch -c feature/scratch
# make a fake uncommitted edit so the working dir is dirty:
echo "# scratch" >> cli.py
git switch -c feature/other
# Agent A's branch: add `clear` to the usage line and commit it.
git switch -c feature/clear
sed 's/done <index>/done <index> | clear/' cli.py > cli.tmp && mv cli.tmp cli.py
git commit -am "Add clear command (demo)"
# Agent B's branch, off main: start adding `count` to the SAME line — leave it uncommitted.
git switch main
git switch -c feature/count
sed 's/done <index>/done <index> | count/' cli.py > cli.tmp && mv cli.tmp cli.py
# Try to hop the working directory back to Agent A's branch:
git switch feature/clear
# error: Your local changes to the following files would be overwritten by checkout:
# cli.py
# Please commit your changes or stash them before you switch branches.
```
Git refuses — it won't move you to another branch with uncommitted changes in the way. *That* is the
wall. Clean up before continuing:
Git refuses — moving the one working directory to `feature/clear` would overwrite Agent B's
uncommitted edit with `feature/clear`'s committed version of that line. *That* is the wall: one
directory can't hold two agents' in-progress work at once. These two branches existed only to feel
the collision, so clean them up before continuing:
```bash
git restore cli.py
git restore cli.py # drop Agent B's uncommitted edit
git switch main
git branch -D feature/scratch feature/other
git branch -D feature/clear feature/count # throw away the demo branches
```
### Part B — Create two worktrees
@@ -269,16 +295,19 @@ This is the part to actually *do simultaneously*, not one then the other.
`lab/agent-a-prompt.md`*add a `clear` command that removes all tasks.*
2. Open `~/workflow-course/tasks-app-count` in a **second** editor/AI session. Give it the prompt in
`lab/agent-b-prompt.md`*add a `count` command that prints the number of pending tasks.*
3. Let both work at the same time. While they run, prove the isolation from a third terminal:
3. Let both work at the same time. While they run, prove the isolation from a third terminal — but
use commands that **already exist**. (`clear` and `count` don't yet; the agents are still writing
them.) Give each worktree its own task and list it:
```bash
cd ~/workflow-course/tasks-app-clear && python cli.py clear # agent A's feature
cd ~/workflow-course/tasks-app-count && python cli.py count # agent B's feature
cd ~/workflow-course/tasks-app-clear && python cli.py add "from worktree A" && python cli.py list
cd ~/workflow-course/tasks-app-count && python cli.py add "from worktree B" && python cli.py list
```
Each app runs its own command in its own folder. Note that each worktree has its **own**
`tasks.json` (it's gitignored runtime state, not shared history) so the two running apps don't
even share data. Total isolation.
Each `list` shows only its own task — worktree A never sees "from worktree B" and vice versa. Each
worktree has its **own** `tasks.json` (gitignored runtime state, not shared history), so the two
running apps don't even share data. Separate files, separate state, while both agents work. Total
isolation.
4. In each worktree, commit the agent's work on its own branch:
@@ -289,6 +318,16 @@ This is the part to actually *do simultaneously*, not one then the other.
Two agents, two commits, two branches — neither ever saw the other's files.
5. *Now* the new commands exist — run each in its own worktree to watch it work:
```bash
cd ~/workflow-course/tasks-app-clear && python cli.py clear # agent A's new command
cd ~/workflow-course/tasks-app-count && python cli.py count # agent B's new command
```
`count` reports only worktree B's task — the one you added in step 3 — because B's `tasks.json` is
the only state it can see. The isolation, one last time.
### Part D — Merge back and clean up
Bring both features home to `main` in your original worktree:
@@ -354,8 +393,8 @@ Worktrees are sharp tools. The honest caveats:
**You're done when:**
- `git worktree list` showed three entries at once, and you ran a different command of the
`tasks-app` from two different worktree folders.
- `git worktree list` showed three entries at once, and you ran the `tasks-app` from two different
worktree folders — adding a different task in each and watching each keep its own `tasks.json`.
- You ran two AI sessions in parallel — each in its own worktree on its own branch — and confirmed
neither touched the other's files (different folders, different `tasks.json`, different branch).
- You merged both feature branches back into `main` (resolving a conflict if one appeared) and the
+10 -4
View File
@@ -274,15 +274,21 @@ and watch CI stop it.
`test_pending_excludes_completed_tasks` failed, with the assertion and the actual-vs-expected
values. CI caught in seconds what a skim would have waved through.
9. Reproduce and fix:
9. Reproduce and fix. The bad change is already committed *and pushed*, so `git restore` is no help
here — it only discards *uncommitted* edits, and there are none. The team-safe undo for something
already on shared history is `git revert` (Module 12): it writes a **new** commit that inverts the
bad one, instead of rewriting history other people may have pulled.
```bash
pytest -q # fails locally too — same command, same failure
git restore tasks.py # throw away the bad change (Module 2's safety net)
git commit -am "Revert: pending() must exclude completed tasks"
git push # CI goes green again
git revert HEAD # new commit that undoes "Simplify pending()" (Module 12)
git push # CI re-runs on the fixed code and goes green again
```
`git revert HEAD` opens an editor with a prefilled message (`Revert "Simplify pending()"`) — save
and close it. The revert restores the correct `pending()`, the push triggers CI on the fixed code,
and the run goes green.
10. *(Optional, to feel the linter tier.)* Add an obviously unused import to `cli.py`
(`import os` at the top, unused), commit, and push. Watch the **Lint** step fail *before* the
tests even run — the cheap check failing fast. Remove it and push again.