fix(M1-6): apply AI-drives-git reframe, lesson=theory, de-slop, + issue fixes

Phase 1 of the reframe. M1-3 stay manual-by-hand (browser chat); M4 is the pivot
to the AI agent (Claude Code as example); M5-6 are agent-driven.

- M1: de-slop (em-dashes), relocate the build-note out of the lab. Seam devices kept.
- M2: #78 tell learner how to paste cli.py into chat; #79 commit the delete so the
  tree ends clean. restore/cold-session devices kept.
- M3: #80 define ADR; #81 create-file-before-add; #82 ls before/after merge to prove
  branch isolation; #83 drop "prose"; M3 now owns the branch-basics intro.
- M4: #84 Claude Code as the worked example; #85 AI drives git (arithmetic->calculator);
  #86 /path/to -> ~/ai-workflow-course; #87 agent does the revert+verify.
- M5: #88 ask the agent which config files to commit, then let it stage/commit
  (CLAUDE.md example; repo still uses AGENTS.md).
- M6: #90 stop re-teaching branch basics; rescope to the AI experimenting on a branch;
  the engineered conflict is now AI-resolved, learner-verified.

Closes #78
Closes #79
Closes #80
Closes #81
Closes #82
Closes #84
Closes #85
Closes #87
Closes #88
Closes #90

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 21:41:14 -04:00
parent 2467f25901
commit 896c4606c6
8 changed files with 578 additions and 481 deletions
@@ -31,53 +31,35 @@ the next size up: isolating *a whole line of committed work* so you can keep or
By the end of this module you can:
1. Create a branch, switch between branches, and explain what a branch actually *is* (a movable
pointer, not a copy of your files).
2. Let an AI make a bold, multi-commit change on a branch while `main` stays untouched and runnable.
3. Decide the experiment's fate in one command: **merge** it into `main` to keep it, or **delete the
branch** to throw it away with zero trace.
4. Read a merge conflict the `<<<<<<<`/`=======`/`>>>>>>>` markers — and resolve it deliberately,
including handing the conflict to the AI to resolve.
5. Tell the difference between a fast-forward merge and a merge commit, and know which one you just
got.
1. Explain what a branch actually *is* (a movable pointer, not a copy of your files) and direct your
AI agent to create and switch between branches, verifying the result with `git branch`/`git status`.
2. Let the AI make a bold, multi-commit change on a branch while `main` stays untouched and runnable.
3. Decide the experiment's fate and have the agent carry it out: **merge** it into `main` to keep it,
or **delete the branch** to throw it away with zero trace. You make the call and check the result.
4. Read a merge conflict (the `<<<<<<<`/`=======`/`>>>>>>>` markers) and hand it to the AI to
resolve, then verify the resolution is right before the merge completes.
5. Tell the difference between a fast-forward merge and a merge commit, and know which one you got.
---
## Key concepts
### What a branch actually is
### What a branch actually is (quick recap)
You already drove this loop once — `git switch -c`, `git merge`, `git branch -d` on a doc in Module 3,
where the merge always fast-forwarded because nothing else had moved. Here the same verbs meet code
that diverges and conflicts, so it's worth pinning down what a branch really is before we lean on it.
You already drove the branch loop by hand in Module 3 (create, merge, delete) on a markdown doc,
where the merge always fast-forwarded because nothing else had moved. You won't re-learn those
commands here. From Module 4 on, the AI runs them for you; this module is about how the AI works
*inside* a branch and how you decide what to keep. So just one line of recap before we get there.
Strip the mystique and a branch is **a named, movable pointer to a commit.** That's the whole
definition. Your commit history is a chain of snapshots (Module 2); a branch is a sticky label that
points at one of them and *moves forward* every time you commit on it.
A branch is **a named, movable pointer to a commit.** Your commit history is a chain of snapshots
(Module 2); a branch is a sticky label that points at one of them and moves forward every time you
commit on it. `main` is the branch Git made for you in Module 2; every commit moved that label
forward. You were "on a branch" the whole time.
When you ran `git init -b main` in Module 2, Git made one branch for you automatically — named
`main` (the `-b main` is what guaranteed that name; in this course your repo is always on `main`).
Every commit you made moved the `main` label forward. You were "on a branch" the entire time
without thinking about it.
The thing that surprises people coming from an ops background: **creating a branch copies nothing.**
There's no second folder, no duplicated files, no disk cost worth mentioning. Git just writes a new
label pointing at the same commit you're standing on. That's why branches are *cheap enough to be
disposable* — and disposable is exactly the property we want.
```bash
git branch # list branches; the * marks the one you're on
git switch -c experiment # create a branch called "experiment" and switch to it
git switch main # switch back to main
git branch -d experiment # delete a branch you've already merged
git branch -D experiment # FORCE-delete a branch, merged or not (the "throw it away" button)
```
> **Naming note** (you saw the short version in Module 3). `git switch` (create/move between branches)
> and `git restore` (the Module 2 undo) were split out of the older, overloaded `git checkout` command.
> You'll still see `git checkout -b experiment` everywhere online — it does the same thing as
> `git switch -c experiment`. Both work; this module uses `switch`/`restore` because they say what they
> mean.
The property that makes branches the right tool here: **creating one copies nothing.** No second
folder, no duplicated files, no disk cost worth mentioning. Git writes a new label pointing at the
commit you're already on. That's why branches are cheap enough to be disposable, and disposable is
exactly what we want for an AI experiment you might throw away.
### The reframe: a branch is a sandbox you can blow away
@@ -87,10 +69,10 @@ You spin one up precisely *because* you're about to do something you might regre
clean way to make it never have happened.
In Module 2 the safety net was "commit, then `restore` if the AI makes a mess." That's perfect for a
single bad edit. But some experiments are bigger than one edit "rewrite the storage layer,"
"try a totally different CLI structure," "add a feature that touches four files." Those take *several
commits* to even evaluate, and you don't want that half-finished, possibly-broken work sitting on
`main`. A branch gives the whole experiment its own track:
single bad edit. But some experiments are bigger than one edit: "rewrite the storage layer," "try a
totally different CLI structure," "add a feature that touches four files." Those take several commits
to even evaluate, and you don't want that half-finished, possibly-broken work sitting on `main`. A
branch gives the whole experiment its own track:
```
main: A───B───C (always runnable; this is your "known good")
@@ -98,82 +80,80 @@ main: A───B───C (always runnable; this is y
experiment: D───E───F (the AI's bold attempt, however messy)
```
While you're on `experiment`, `main` is frozen at C runnable, shippable, untouched. The AI can
leave `experiment` in a smoking crater at F and `main` doesn't care. When you're done you make one
decision:
While you're on `experiment`, `main` is frozen at C: runnable, shippable, untouched. The AI can leave
`experiment` a broken mess at F and `main` doesn't care. When you're done you make one decision:
- **Keep it:** merge `experiment` into `main` (C gains D, E, F).
- **Kill it:** delete `experiment`. D, E, F evaporate. `main` is still exactly C, as if the
experiment never happened.
That "kill it, no trace" path is the one this module exists for. It's the difference between *"I have
to carefully undo everything the AI did"* and *"I delete the branch."*
That "kill it, no trace" path is the one this module exists for. It's the difference between "I have
to carefully undo everything the AI did" and "I delete the branch."
### Switching branches changes your files
Here's the part that feels like magic the first time. When you `git switch` to another branch, **Git
rewrites the files in your folder to match that branch.** Switch to `experiment` and the AI's
half-built feature appears in your editor. Switch back to `main` and it vanishes — your files are
back to commit C. Same folder, different contents, instantly.
One detail trips people up the first time. When you switch to another branch, **Git rewrites the
files in your folder to match that branch.** Switch to `experiment` and the AI's half-built feature
appears in your editor. Switch back to `main` and it's gone; your files are back to commit C. Same
folder, different contents, instantly.
This is why you can't switch with uncommitted changes lying around that would be clobbered: Git
stops you, because switching would silently throw work away. The fix is the Module 2 habit commit
(or stash) before you switch. On a branch, "commit often" pays off again: each commit is a safe
point to switch away from.
This is why you can't switch with uncommitted changes lying around that would be clobbered. Git stops
you, because switching would silently throw work away. The fix is the Module 2 habit: commit (or
stash) before you switch. On a branch, "commit often" pays off again, since each commit is a safe
point to switch away from. When the agent is driving, this is one of the things you verify after it
works: `git status` clean before a switch.
> **One folder, one branch at a time.** Switching swaps the *whole* folder between branches, which
> means you can only have one branch checked out at once. The moment you want *two* branches live
> simultaneously — say, two agents working in parallel without overwriting each other's files you've
> hit the limit of branches alone. That's exactly what **Module 7 (Worktrees)** solves: multiple
> working directories from one repo. Branches are the concept; worktrees are how you run several at
> once. Keep that in your back pocket.
> **One folder, one branch at a time.** Switching swaps the *whole* folder between branches, so you
> can only have one branch checked out at once. The moment you want *two* branches live at the same
> time (say, two agents working in parallel without overwriting each other's files) you've hit the
> limit of branches alone. That's what **Module 7 (Worktrees)** solves: multiple working directories
> from one repo. Branches are the concept; worktrees are how you run several at once.
### Merging: keeping the experiment
Merging takes the commits from one branch and brings them into another. You switch to the branch you
want to *receive* the work (usually `main`), then merge the other branch in:
Merging takes the commits from one branch and brings them into another. The receiving branch (usually
`main`) is the one you switch to, and the other branch merges into it. You don't type this; you tell
the agent "merge `experiment` into `main`," and it runs the equivalent of `git merge experiment`.
```bash
git switch main
git merge experiment
There are two outcomes, and it's worth recognizing which you got when you read the log:
- **Fast-forward.** If `main` hasn't moved since you branched (still at C), Git slides the `main`
label forward to F. The history stays a straight line. This is the common case for a solo
experiment.
- **Merge commit.** If `main` *did* move on (you committed to `main` while `experiment` was off doing
its thing), the two lines of history diverged. Git stitches them together with a new commit that
has two parents.
Git picks between these based on whether the branches diverged. You recognize them in the log: a
fast-forward is a straight line, a merge commit is a visible fork-and-join.
```console
$ git log --oneline --graph
* 9f3c1a2 Merge branch 'experiment'
|\
| * 4b8d0e1 Add task priorities (experiment)
* | 2a1f9c7 Fix list ordering on main
|/
* 7c0e3d4 Initial tasks app
```
There are two outcomes, and it's worth knowing which you got:
- **Fast-forward.** If `main` hasn't moved since you branched (it's still at C), Git doesn't need to
do anything clever — it just slides the `main` label forward to F. The history stays a straight
line. This is the common case for a solo experiment.
- **Merge commit.** If `main` *did* move on (someone — or you — committed to `main` while
`experiment` was off doing its thing), the two lines of history have diverged. Git stitches them
together with a new commit that has two parents. You'll be dropped into an editor to confirm the
merge message; save and close it.
You don't choose between these — Git picks based on whether the branches diverged. You just need to
recognize them in `git log --oneline --graph`, where a fast-forward is a straight line and a merge
commit is a visible fork-and-join.
After a successful merge, the branch has done its job. Delete it:
```bash
git branch -d experiment # -d refuses if it's NOT fully merged — a safety check
```
After a successful merge the branch has done its job, and `git branch -d experiment` deletes it. The
lowercase `-d` refuses if the branch isn't fully merged, which is a safety check. Again, the agent
runs this once you've decided; you confirm the branch is gone with `git branch`.
### Discarding: killing the experiment
This is the payoff. The AI tried something bold on the branch, you looked at it, and you don't want
it. You don't undo anything. You don't `restore` file by file. You switch away and delete the branch:
```bash
git switch main # your files snap back to known-good main
git branch -D experiment # -D force-deletes even though it was never merged
```
it. You don't undo anything. You don't `restore` file by file. You switch away and delete the branch
(`git switch main`, then `git branch -D experiment`, which force-deletes even though it was never
merged). The agent runs both on your say-so.
That's it. The experiment is gone. `main` never changed. `git log` on `main` shows no sign it ever
happened. **The whole bold attempt cost you one branch and one delete.**
This is the mental shift the module is selling: when discarding is this cheap, you stop being
precious about what you let the AI try. Risky refactor? Branch it. Want to compare two approaches?
A branch each, keep the winner, delete the loser. The branch is the unit of "maybe."
This is the mental shift the module is selling: when discarding is this cheap, you stop being precious
about what you let the AI try. Risky refactor? Branch it. Want to compare two approaches? A branch
each, keep the winner, delete the loser. The branch is the unit of "maybe."
### Merge conflicts: when two changes collide
@@ -198,19 +178,16 @@ Read it like this:
- 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.**
You're not picking a side mechanically — you're deciding what the line *should* say. Often that's one
side, sometimes it's a blend of both (here: a usage string that lists *both* `stats` and `purge`).
Then you tell Git the conflict is settled:
Resolving isn't picking a side mechanically. It's deciding what the line *should* say. Often that's
one side; sometimes it's a blend of both (here, a usage string that lists *both* `stats` and `purge`).
This is the kind of bounded reasoning task the AI is good at: it sees both versions and the
surrounding code, so you hand it the conflict and let it produce the combined version. Once the file
is correct and marker-free, telling Git the conflict is settled is two more commands the agent runs
(`git add cli.py` to mark the file resolved, then `git commit` to complete the merge).
```bash
# edit the file: remove the markers, leave the correct content
git add cli.py # marks this file's conflict as resolved
git commit # completes the merge (opens an editor for the merge message)
```
`git status` during a conflict is your map — it lists every file still "unmerged." When that list is
empty and you've `git add`-ed them all, you commit and the merge is done. If you panic mid-conflict,
`git merge --abort` rewinds you to before the merge, no harm done.
`git status` during a conflict is your map; it lists every file still "unmerged." Your job is the
verify: read the resolution, confirm it's what you meant, and check `git status` comes back clean. If
things go sideways, `git merge --abort` rewinds to before the merge with no harm done.
---
@@ -260,78 +237,96 @@ deliberately create and resolve a merge conflict — using the AI to help resolv
### Part A — Branch it and let the AI go bold
1. Confirm you're on `main` and clean, then create an experiment branch and switch to it:
1. Make sure you're in the repo, then **tell the agent to set up the branch.** Ask:
> *"We're on the `tasks-app` repo. Confirm we're on `main` with a clean working tree, then create
> a branch called `experiment/priorities` and switch to it."*
Then **verify** it did what you asked, by hand:
```bash
cd ~/ai-workflow-course/tasks-app
git switch main
git status # must be clean
git switch -c experiment/priorities
git branch # the * is now on experiment/priorities
git status # should be clean, on experiment/priorities
git branch # the * should be on experiment/priorities
```
2. Give the AI a deliberately *bold* task — the kind you'd hesitate to run straight on `main`:
You're not typing the branch commands; you're confirming the agent ran them correctly. This is the
pattern for the whole module: you direct, the agent does the git, you check.
2. Give the AI a deliberately *bold* task, the kind you'd hesitate to run straight on `main`:
> *"Add task priorities (low/medium/high) to this app. Store a priority on each task, let me set
> it when adding (`add "thing" --priority high`), show it in `list`, and sort `list` so high
> priority comes first. Change whatever files you need to."*
Let it edit `tasks.py` and `cli.py` freely. This is a multi-file change — exactly the kind that's
nerve-wracking on `main` and relaxed on a branch.
Let it edit `tasks.py` and `cli.py` freely. This is a multi-file change: nerve-wracking on `main`,
relaxed on a branch.
3. Review and commit the experiment **on the branch**:
3. Review the change, then have the agent commit it **on the branch**. First read the diff and run
the app yourself:
```bash
git diff # read what it actually changed
python cli.py add "ship module 6" --priority high
python cli.py add "water plants" --priority low
python cli.py list # see if priorities work and sort
git add .
git commit -m "Add task priorities (experiment)"
```
4. Now prove the isolation. Switch back to `main` and watch the feature **disappear**:
Once the diff looks right and the feature runs, tell the agent:
> *"Commit this on the branch with a message like 'Add task priorities (experiment)'."*
The agent decides what to stage and writes the commit. Confirm it landed with `git log --oneline`.
4. Now prove the isolation. Ask the agent to switch back to `main`, then watch the feature
**disappear**:
> *"Switch back to `main`."*
```bash
git switch main
python cli.py list # no priorities — main is exactly as you left it
python cli.py list # no priorities; main is exactly as you left it
```
Your bold change exists only on the branch. `main` never saw it. Sit with that for a second —
that's the whole point.
Your bold change exists only on the branch. `main` never saw it, and that's the whole point.
### Part B — Decide its fate
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.
**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):**
**Path 1 — Keep it (merge).** Tell the agent:
> *"Merge `experiment/priorities` into `main`, then delete the branch."*
Then verify the result yourself:
```bash
git switch main
git merge experiment/priorities # likely a fast-forward: main slides up to the branch
git log --oneline --graph # see the history; straight line = fast-forward
git log --oneline --graph # straight line = fast-forward merge
python cli.py list # the feature is now on main
git branch -d experiment/priorities # branch did its job; -d is the safe delete
git branch # experiment/priorities is gone
```
**Path 2 — Throw it away (discard):**
**Path 2 — Throw it away (discard).** Tell the agent:
> *"Switch to `main` and discard the `experiment/priorities` branch entirely."*
Then verify:
```bash
git switch main # files snap back to known-good main
git branch -D experiment/priorities # force-delete the unmerged branch
git log --oneline # no trace of the experiment on main
python cli.py list # main is untouched, exactly as before
git branch # the branch is gone
```
Notice what you did *not* do in Path 2: no file-by-file `restore`, no manual undo, no hunting through
diffs. You deleted a label and the entire experiment was gone. That's the economics shift bold AI
attempts become free to reject.
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
Now the skill everyone fears and nobody should. You'll engineer a guaranteed conflict by having
**two branches change the same line in different ways**, then resolve it.
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
@@ -341,47 +336,40 @@ Now the skill everyone fears and nobody should. You'll engineer a guaranteed con
> `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.
1. Make sure you're on a clean `main`. Create the first branch and have the AI add a `stats` command:
1. From a clean `main`, set up the first branch and the `stats` command in one instruction to the
agent:
> *"From `main`, create a branch `feature/stats`, add a `stats` command to `cli.py` that prints how
> many tasks are total, done, and pending, update the usage string to include it, then commit it
> with the message 'Add stats command'."*
Verify the agent edited the usage line and committed:
```bash
git switch main
git switch -c feature/stats
git diff main # the usage line changed + the command was added
git log --oneline # the commit is there, on feature/stats
```
Ask the AI: *"Add a `stats` command to `cli.py` that prints how many tasks are total, done, and
pending, and update the usage string to include it."* Then:
2. Now the second branch, which touches **the same usage line** a different way:
> *"Switch back to `main`, create a branch `feature/purge`, add a `purge` command to `cli.py` that
> removes all completed (done) tasks, update the usage string to include it, then commit it with
> the message 'Add purge command'."*
Verify the collision is set up:
```bash
git diff # confirm it edited the usage line + added the command
git add . && git commit -m "Add stats command"
git diff main # feature/purge edited the same usage line
```
2. Switch back to `main` and create a *different* branch that touches **the same usage line**:
Both branches changed the same `usage:` line, each adding a *different* command. Git won't be able
to auto-merge that line.
```bash
git switch main
git switch -c feature/purge
```
3. Now trigger the conflict. Tell the agent:
Ask the AI: *"Add a `purge` command to `cli.py` that removes all completed (done) tasks, and update
the usage string to include it."* Then:
> *"You're on `feature/purge`. Merge `feature/stats` into it."*
```bash
git diff # it also edited the usage line — this is the collision to come
git add . && git commit -m "Add purge command"
```
Both branches changed the same `usage:` line, each adding a *different* command to it. Git will
not be able to auto-merge that line.
3. Merge them and watch it conflict. Merge `feature/stats` into `feature/purge` (you're on
`feature/purge`):
```bash
git merge feature/stats
```
Git stops with a conflict and tells you which file is unmerged. Confirm:
Git stops with a conflict. Confirm the conflict state yourself:
```bash
git status # cli.py listed under "Unmerged paths"
@@ -402,7 +390,7 @@ Now the skill everyone fears and nobody should. You'll engineer a guaranteed con
(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.)
5. **Resolve it with the AI.** With your editor-integrated agent, this is its sweet spot. Ask:
5. **Resolve it with the AI.** This is exactly the bounded task the agent is good at. Ask:
> *"`cli.py` has a merge conflict on the usage line. I want the final version to list BOTH the
> `stats` and `purge` commands. Resolve the conflict and remove the markers."*
@@ -424,17 +412,19 @@ Now the skill everyone fears and nobody should. You'll engineer a guaranteed con
python cli.py purge
```
6. Tell Git the conflict is settled and complete the merge:
6. Once you've verified the resolution, have the agent finish the merge:
> *"The resolution looks right. Stage `cli.py` and complete the merge."*
Then confirm the merge landed as a merge commit:
```bash
git add cli.py
git commit # opens an editor for the merge message; save and close
git log --oneline --graph # see the fork-and-join: this is a merge commit
git log --oneline --graph # the fork-and-join: this is a merge commit
```
You just resolved a real merge conflict. The marker syntax is identical no matter the file or the
project — once you can read those three lines, conflicts stop being scary and become a five-minute
chore.
You just resolved a real merge conflict: you directed it, the agent did the plumbing, and you
verified the result. The marker syntax is identical no matter the file or the project. Once you can
read those three lines and check the resolution, a conflict is a short, routine task.
> **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
@@ -443,12 +433,13 @@ Now the skill everyone fears and nobody should. You'll engineer a guaranteed con
> *You'll need*), then run it from inside the repo:
>
> ```bash
> cp /path/to/modules/06-branches-sandboxes-for-experiments/lab/make-conflict.sh .
> cp ~/ai-workflow-course/the-workflow-course/modules/06-branches-sandboxes-for-experiments/lab/make-conflict.sh .
> bash make-conflict.sh
> ```
>
> It creates two branches that both edit the same line of `README.md`, leaving you mid-conflict with
> on-screen instructions. The resolution mechanic is identical to the code case above.
> on-screen instructions. From there, hand it to the agent the same way (step 5), then verify. The
> resolution mechanic is identical to the code case above.
---
@@ -485,15 +476,15 @@ The honest limits, so you don't over-trust the sandbox:
**You're done when:**
- You created a branch, let the AI make a multi-file change on it, and confirmed `main` was untouched
by switching back and seeing the change vanish.
- You have **discarded** an experiment with `git branch -D` and confirmed `main` shows no trace, and
you have **merged** one in and seen it land on `main`.
- You directed the agent to branch, let the AI make a multi-file change on it, and confirmed `main`
was untouched by switching back and seeing the change vanish.
- You have **discarded** an experiment (the agent ran `git branch -D`) and confirmed `main` shows no
trace, and you have **merged** one in and seen it land on `main`.
- You can explain, in one sentence, why creating a branch costs essentially nothing (it's a movable
pointer, not a copy).
- You deliberately created a merge conflict, read the `<<<<<<<`/`=======`/`>>>>>>>` markers, resolved
it (with the AI's help) to a marker-free file that runs, and completed the merge with `git add` +
`git commit`.
- You deliberately created a merge conflict, read the `<<<<<<<`/`=======`/`>>>>>>>` markers, had the
AI resolve it to a marker-free file that runs, verified the result, and let the agent complete the
merge.
- You can name the limit: a branch isolates tracked files, not your database, ignored files, or the
outside world.
@@ -10,7 +10,7 @@
# then `git add` + `git commit`.
#
# Copy it into your tasks-app repo, then run it from inside the repo:
# cp /path/to/modules/06-branches-sandboxes-for-experiments/lab/make-conflict.sh .
# cp ~/ai-workflow-course/the-workflow-course/modules/06-branches-sandboxes-for-experiments/lab/make-conflict.sh .
# bash make-conflict.sh
#
# It is non-destructive to your real work: it only touches README.md on two throwaway practice
@@ -73,11 +73,11 @@ echo "================================================================"
echo
echo " Next steps (the skill you're practicing):"
echo " 1. git status # see $FILE under 'Unmerged paths'"
echo " 2. open $FILE and find the <<<<<<< / ======= / >>>>>>> markers"
echo " 3. edit it to the version you want; delete all three marker lines"
echo " (or ask your editor-integrated AI to resolve it, then verify)"
echo " 4. git add $FILE"
echo " 5. git commit # completes the merge"
echo " 2. ask your agent to resolve the conflict in $FILE and complete the merge"
echo " (\"resolve the conflict markers in $FILE and finish the merge\")"
echo " 3. verify: open $FILE, confirm no <<<<<<< / ======= / >>>>>>> markers remain"
echo " 4. git log --oneline --graph # confirm the merge commit landed"
echo " (to do it by hand instead: edit out the markers, then git add $FILE && git commit)"
echo
echo " Chicken out? Undo the whole thing with: git merge --abort"
echo