Running-example consistency: paths, tasks.json, command collisions (#7,#10,#11) (#57)

Co-authored-by: claude <claude@jpaul.io>
Co-committed-by: claude <claude@jpaul.io>
This commit was merged in pull request #57.
This commit is contained in:
2026-06-22 15:35:51 -04:00
committed by Claude (agent)
parent 848ad14e3c
commit 06b9f8f308
14 changed files with 192 additions and 136 deletions
@@ -56,30 +56,30 @@ 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 added a `clear` command and committed it on its own branch:
git switch -c feature/clear
# ...agent A edits the usage line in cli.py to add `clear`...
git commit -am "Add clear command"
# Agent A added a `wipe` command and committed it on its own branch:
git switch -c feature/wipe
# ...agent A edits the usage line in cli.py to add `wipe`...
git commit -am "Add wipe command"
# You start Agent B on a fresh branch off main; it begins editing the SAME
# usage line to add `count`, and hasn't committed:
# usage line to add `remaining`, and hasn't committed:
git switch main
git switch -c feature/count
git switch -c feature/remaining
# ...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
git switch feature/wipe
# 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. Switching to `feature/clear` would overwrite Agent B's uncommitted edits
Git stops you — correctly. Switching to `feature/wipe` 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 B's
`count` command isn't done).
`remaining` 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).
@@ -95,17 +95,18 @@ repository, each with its own checked-out branch.** One repo, many checkouts.
```bash
cd ~/workflow-course/tasks-app # your existing repo from Module 2
git worktree add ../tasks-app-count -b feature/count
git worktree add ../tasks-app-remaining -b feature/remaining
```
That command creates a brand-new folder, `~/workflow-course/tasks-app-count`, containing a full
checkout of your project on a new branch `feature/count`. Your original folder is untouched, still
on its own branch. You now have two real directories you can `cd` into, edit, and run independently:
That command creates a brand-new folder, `~/workflow-course/tasks-app-remaining`, containing a full
checkout of your project on a new branch `feature/remaining`. Your original folder is untouched,
still on its own branch. You now have two real directories you can `cd` into, edit, and run
independently:
```
~/workflow-course/
tasks-app/ ← the "main" worktree, on (say) main
tasks-app-count/ ← a "linked" worktree, on feature/count
tasks-app/ ← the "main" worktree, on (say) main
tasks-app-remaining/ ← a "linked" worktree, on feature/remaining
```
Both are backed by **one** repository. There is a single `.git` — a single object store, a single
@@ -127,7 +128,7 @@ because there's only one store.
Think of the shared object store as the project's single, settled past — every commit, on every
branch, in one place. Each worktree is a different *present moment* checked out of that past: this
folder is "the project as of `feature/count`," that folder is "the project as of `main`." They all
folder is "the project as of `feature/remaining`," that folder is "the project as of `main`." They all
write to the same past (commits go to the shared store), but each lives in its own present (its own
files on disk).
@@ -149,9 +150,9 @@ git worktree prune # forget worktrees whose folders were
```bash
$ git worktree list
/home/you/workflow-course/tasks-app a1b2c3d [main]
/home/you/workflow-course/tasks-app-count d4e5f6a [feature/count]
/home/you/workflow-course/tasks-app-clear 7g8h9i0 [feature/clear]
/home/you/workflow-course/tasks-app a1b2c3d [main]
/home/you/workflow-course/tasks-app-remaining d4e5f6a [feature/remaining]
/home/you/workflow-course/tasks-app-wipe 7g8h9i0 [feature/wipe]
```
Three folders, one repo, three branches checked out simultaneously. No stashing, no switching, no
@@ -197,7 +198,7 @@ behave:
once — a feature here, a bugfix there, a doc update in a third. The constraint was never the
model; it was that they'd trip over one repo. Worktrees remove the constraint.
- **Each worktree is its own durable memory (Module 2).** A fresh agent dropped into
`tasks-app-count` reads `git status` / `git diff` / `git log` and gets *that branch's* ground
`tasks-app-remaining` reads `git status` / `git diff` / `git log` and gets *that branch's* ground
truth — not a blur of three agents' half-finished work. Per-agent isolation makes per-agent
"where were we?" actually answerable.
- **It keeps parallel AI output reviewable.** Each agent's work lands as its own branch with its own
@@ -214,8 +215,10 @@ to run two agents and watch them eat each other's homework.
**Lab language:** shell (Git commands), plus two AI edit sessions on the `tasks-app`.
In this lab you'll run **two AI sessions at the same time** on the same project — one adding a
`clear` command, one adding a `count` command — each in its own worktree, and watch them *not*
collide. Then you'll merge both back and clean up.
`wipe` command, one adding a `remaining` command — each in its own worktree, and watch them *not*
collide. Then you'll merge both back and clean up. (We use two commands your carried-forward
`tasks-app` doesn't have yet, so neither agent re-adds something that already exists — the lesson is
the parallel isolation, not the commands.)
**You'll need:**
@@ -226,7 +229,10 @@ collide. Then you'll merge both back and clean up.
- **Two** editor-integrated AI sessions you can run at once (Module 4) — two editor windows, or two
terminal AI sessions. If you only have a browser chat, you can still do the lab; just treat each
worktree folder as a separate copy-paste context.
- The starter scripts and prompts in this module's `lab/` folder.
- The starter scripts and prompts in this module's `lab/` folder. As established in Module 4, the
course's lab scripts live in the course repo under `modules/NN/lab/`, while `tasks-app` is a
separate folder — so **copy the scripts into `tasks-app` and run them by name** (`bash
setup-worktrees.sh`), using your real course path in place of `/path/to/`.
### Part A — Feel the collision (1 minute)
@@ -238,71 +244,75 @@ the edit an agent would make.) In your `tasks-app`:
```bash
cd ~/workflow-course/tasks-app
# 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 A's branch: add `wipe` to the usage line and commit it.
git switch -c feature/wipe
sed 's/done <index>/done <index> | wipe/' cli.py > cli.tmp && mv cli.tmp cli.py
git commit -am "Add wipe command (demo)"
# Agent B's branch, off main: start adding `count` to the SAME line — leave it uncommitted.
# Agent B's branch, off main: start adding `remaining` 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
git switch -c feature/remaining
sed 's/done <index>/done <index> | remaining/' 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
git switch feature/wipe
# 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 — 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
(The `sed` matches `done <index>`, which is still in your usage line no matter how many commands
you've added since Module 1, and inserts a new one right after it — so both branches edit the same
line.) Git refuses — moving the one working directory to `feature/wipe` would overwrite Agent B's
uncommitted edit with `feature/wipe`'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 # drop Agent B's uncommitted edit
git restore cli.py # drop Agent B's uncommitted edit
git switch main
git branch -D feature/clear feature/count # throw away the demo branches
git branch -D feature/wipe feature/remaining # throw away the demo branches
```
### Part B — Create two worktrees
From inside `tasks-app`, run the setup script (or run the commands by hand):
Copy the setup script into `tasks-app` (see *You'll need*), then run it from inside the repo (or run
the commands by hand):
```bash
bash modules/07-worktrees-running-agents-in-parallel/lab/setup-worktrees.sh
cp /path/to/modules/07-worktrees-running-agents-in-parallel/lab/setup-worktrees.sh .
bash setup-worktrees.sh
```
It runs:
```bash
git worktree add ../tasks-app-clear -b feature/clear
git worktree add ../tasks-app-count -b feature/count
git worktree add ../tasks-app-wipe -b feature/wipe
git worktree add ../tasks-app-remaining -b feature/remaining
git worktree list
```
You now have three folders backed by one repo. Confirm:
```bash
git worktree list # should show main + feature/clear + feature/count
git worktree list # should show main + feature/wipe + feature/remaining
```
### Part C — Run two AI sessions in parallel
This is the part to actually *do simultaneously*, not one then the other.
1. Open `~/workflow-course/tasks-app-clear` in one editor/AI session. Give it the prompt in
`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.*
1. Open `~/workflow-course/tasks-app-wipe` in one editor/AI session. Give it the prompt in
`lab/agent-a-prompt.md`*add a `wipe` command that removes all tasks.*
2. Open `~/workflow-course/tasks-app-remaining` in a **second** editor/AI session. Give it the prompt
in `lab/agent-b-prompt.md`*add a `remaining` 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 — 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:
use commands that **already exist**. (`wipe` and `remaining` 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 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
cd ~/workflow-course/tasks-app-wipe && python cli.py add "from worktree A" && python cli.py list
cd ~/workflow-course/tasks-app-remaining && python cli.py add "from worktree B" && python cli.py list
```
Each `list` shows only its own task — worktree A never sees "from worktree B" and vice versa. Each
@@ -313,8 +323,8 @@ This is the part to actually *do simultaneously*, not one then the other.
4. In each worktree, commit the agent's work on its own branch:
```bash
cd ~/workflow-course/tasks-app-clear && git add . && git commit -m "Add clear command"
cd ~/workflow-course/tasks-app-count && git add . && git commit -m "Add count command"
cd ~/workflow-course/tasks-app-wipe && git add . && git commit -m "Add wipe command"
cd ~/workflow-course/tasks-app-remaining && git add . && git commit -m "Add remaining command"
```
Two agents, two commits, two branches — neither ever saw the other's files.
@@ -322,12 +332,12 @@ This is the part to actually *do simultaneously*, not one then the other.
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
cd ~/workflow-course/tasks-app-wipe && python cli.py wipe # agent A's new command
cd ~/workflow-course/tasks-app-remaining && python cli.py remaining # 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.
`remaining` counts a single pending task — the one you added to worktree B 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
@@ -336,8 +346,8 @@ Bring both features home to `main` in your original worktree:
```bash
cd ~/workflow-course/tasks-app
git switch main
git merge feature/clear
git merge feature/count
git merge feature/wipe
git merge feature/remaining
```
Both commits are already in the shared object store, so there's nothing to fetch — the merges are
@@ -346,10 +356,12 @@ their `elif` branch in the same spot. That's expected, and it's a *merge-time* e
parallel-work collision — resolve it with the exact skill from Module 6, then `python cli.py list`
to confirm both commands work.
Now tear down the worktrees:
Now tear down the worktrees (copy the cleanup script into `tasks-app` the same way, then run it from
inside the repo):
```bash
bash modules/07-worktrees-running-agents-in-parallel/lab/cleanup-worktrees.sh
cp /path/to/modules/07-worktrees-running-agents-in-parallel/lab/cleanup-worktrees.sh .
bash cleanup-worktrees.sh
git worktree list # only the main worktree remains
```
@@ -366,7 +378,7 @@ Worktrees are sharp tools. The honest caveats:
(`fatal: 'main' is already checked out at ...`). This is a feature, not a bug — it's exactly what
stops two agents from writing the same branch — but it surprises people. One branch, one worktree.
- **Uncommitted work is *not* shared.** Only commits go to the shared store. The edits sitting
modified-but-uncommitted in `tasks-app-count` exist *only* in that folder. If you
modified-but-uncommitted in `tasks-app-remaining` exist *only* in that folder. If you
`git worktree remove` a dirty worktree, Git refuses unless you pass `--force` — and `--force`
throws that uncommitted work away for good. Commit before you remove.
- **Cleanup is a two-part chore.** Deleting a worktree folder with `rm -rf` does *not* tell Git it's