diff --git a/modules/04-getting-the-ai-out-of-the-browser/README.md b/modules/04-getting-the-ai-out-of-the-browser/README.md index ee2b503..36bbf06 100644 --- a/modules/04-getting-the-ai-out-of-the-browser/README.md +++ b/modules/04-getting-the-ai-out-of-the-browser/README.md @@ -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. --- diff --git a/modules/07-worktrees-running-agents-in-parallel/README.md b/modules/07-worktrees-running-agents-in-parallel/README.md index 01c562d..685962b 100644 --- a/modules/07-worktrees-running-agents-in-parallel/README.md +++ b/modules/07-worktrees-running-agents-in-parallel/README.md @@ -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 /done | 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 /done | 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 diff --git a/modules/14-continuous-integration/README.md b/modules/14-continuous-integration/README.md index 3ccf2e1..7396a1c 100644 --- a/modules/14-continuous-integration/README.md +++ b/modules/14-continuous-integration/README.md @@ -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.