fix(M7-27+capstone): apply AI-drives-git reframe, lesson=theory, de-slop course-wide

Phase 2 sweep — all modules are post-pivot, so the learner directs the AI agent
(Claude Code as the worked example) to do the git/setup work and verifies, instead
of typing commands by hand; no re-teaching basics. Lesson sections are theory with
example output; all execution lives in the labs. De-slopped ("prose" etc. gone
course-wide, em-dash density thinned). /path/to placeholders -> ~/ai-workflow-course.

Every deliberate teaching device verified intact: M10 ai-change.patch trap,
M12 bad-clear-snippet, M13/M27 planted pending_count bug, M15 secret+typosquat+MD5,
M18 BREAK=1, M21 absent-.gitignore, M22 poisoned skill, M24 no-op patch, M25 --simulate.
Labs compile/parse (py/sh/yaml/json); no junk.

Closes #83
Closes #86
Closes #89

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:58:17 -04:00
parent a29823f4b3
commit f925fd9645
38 changed files with 1735 additions and 1424 deletions
@@ -1,6 +1,6 @@
# Module 11 — Collaboration: Humans and Agents on One Repo
> **You now have every piece issues, branches, PRs, review. This module wires them into one loop,
> **You now have every piece: issues, branches, PRs, review. This module wires them into one loop,
> and points out that half your "teammates" might not be human.** Once the loop runs the same way no
> matter who's pulling the work, an agent is just another contributor who needs a branch.
@@ -20,7 +20,7 @@ This is the synthesis module for Unit 2's collaboration arc. It assumes the whol
- **Module 10** — pull/merge requests and the skill of reviewing a diff you didn't write.
Each of those taught one move. This module is the assembled motion. If you're missing one, the loop
still works, but a step will feel like a black box go back and fill it in.
still works, but a step will feel like a black box, so go back and fill it in.
---
@@ -54,8 +54,8 @@ issue → branch → implementation → pull request → review → me
(M9) (M6) (inner loop, M2) (M10) (M10) (this module)
```
Everything you learned was a single station on this track. The reason to assemble them now rather
than keep treating issues, branches, and PRs as separate skills is that the *handoffs between
Everything you learned was a single station on this track. The reason to assemble them now, rather
than keep treating issues, branches, and PRs as separate skills, is that the *handoffs between
stations* are where collaboration actually happens, and where it breaks. The issue says what to do.
The branch isolates the attempt. The PR makes the attempt reviewable. The review is the judgment.
The merge is the commitment. Closing the issue is the receipt. Skip a handoff and you get the
@@ -63,7 +63,7 @@ failure modes every team knows: work nobody asked for, changes that land straigh
review, "done" issues for work that was never actually done.
The loop is worth internalizing as a loop because **it's the same loop regardless of who's doing the
work** and increasingly, some of the workers are agents. Hold that thought; it's the whole point of
work**, and increasingly some of the workers are agents. Hold that thought; it's the whole point of
the module, and we'll come back to it.
### The loop, step by step
@@ -71,17 +71,18 @@ the module, and we'll come back to it.
**1 — The issue (Module 9) is the contract.** Before any code, there's a statement of intent: a
title, a description of the desired behavior, maybe acceptance criteria. It has a number (`#42`) that
the rest of the loop will reference. The issue exists so that "what we're doing and why" lives
somewhere durable and shared not in one person's head or one chat session that'll evaporate
somewhere durable and shared, not in one person's head or one chat session that'll evaporate
(Module 1, Seam 2). Assign it to whoever's taking it: a person, or an agent.
**2 — The branch (Module 6) is the workspace.** You never implement on `main`. You cut a branch
named for the work — convention is something traceable like `42-clear-done-command` (the issue
named for the work. Convention is something traceable like `42-clear-done-command` (the issue
number plus a slug). The name matters more than it looks: months later, `git branch` and the host's
branch list become a map of "what's in flight," and the issue number ties each branch back to its
contract.
```bash
git switch -c 42-clear-done-command # branch off main and switch to it
# Switched to a new branch '42-clear-done-command'
```
**3 — Implementation is the inner loop (Module 2).** This is where the actual editing happens —
@@ -91,6 +92,7 @@ untouched until the loop says otherwise.
```bash
git push -u origin 42-clear-done-command # publish the branch so others (and the host) can see it
# branch '42-clear-done-command' set up to track 'origin/42-clear-done-command'.
```
**4 — The pull request (Module 10) makes it reviewable.** Opening a PR says "this branch is ready
@@ -99,12 +101,12 @@ reviewable unit. Crucially, **this is where you link back to the issue** (next s
can close itself.
**5 — Review (Module 10) is the judgment gate.** Someone who isn't the author reads the diff for
correctness *and plausibility* the skill Module 10 is built around. They approve, request changes,
correctness *and plausibility*, the skill Module 10 is built around. They approve, request changes,
or comment. For AI-generated diffs this gate is doing more work than it used to: the code compiles,
reads cleanly, and is still wrong in a way only review catches.
**6 — Merge is the commitment.** Approved, the PR merges into `main`. Hosts offer a couple of merge
styles a squash or a merge commit; your team picks one and the effect is the same: the branch's work
styles, a squash or a merge commit; your team picks one and the effect is the same: the branch's work
is now part of the shared trunk. (You'll also see a *rebase-merge* option; it rewrites history and is
out of scope here.) Delete the branch after; its job is done and its name lives on in the merge.
@@ -114,8 +116,8 @@ issue automatically. The receipt is written without anyone touching the issue. T
### Linking the PR to the issue (the auto-close)
The mechanic that makes step 7 free: put a **closing keyword** in the PR description. Most hosts
GitHub, GitLab, Gitea/Forgejo, Bitbucket recognize a common set:
The mechanic that makes step 7 free: put a **closing keyword** in the PR description. Most hosts
(GitHub, GitLab, Gitea/Forgejo, Bitbucket) recognize a common set:
```
Closes #42
@@ -127,11 +129,11 @@ host closes the referenced issue and cross-links the two so each shows the other
body buys you a self-closing loop and a permanent trail from "why we did this" (issue) to "what we
did" (PR/diff) to "when it landed" (merge).
A plain mention without a keyword just `#42` *links* the two but does **not** close on merge.
A plain mention without a keyword, just `#42`, *links* the two but does **not** close on merge.
That's useful too (for "related to" references), but know the difference: the keyword is load-bearing.
> **The trail is the point.** Six months later, someone possibly an agent reading the repo as
> durable memory (Module 2) asks "why does `clear-done` exist?" The answer is one click away:
> **The trail is the point.** Six months later, someone (possibly an agent reading the repo as
> durable memory, Module 2) asks "why does `clear-done` exist?" The answer is one click away:
> issue → PR → diff → merge. You built that trail for free by linking one line.
### Branch vs. fork: it comes down to push access
@@ -157,7 +159,7 @@ simple: **can you push to the repo?**
```
For this audience, working mostly on repos you control, **branches are the default and forks are the
exception** you reach for a fork when contributing to something you don't own. The relevance to AI
exception**: you reach for a fork when contributing to something you don't own. The relevance to AI
work: an agent you run on your own repo branches like any teammate. An agent contributing to a
project it doesn't own forks like any outside contributor. The rule doesn't change for machines.
@@ -167,10 +169,10 @@ project it doesn't own forks like any outside contributor. The rule doesn't chan
*enforced* rule, and that enforcement is the other half of collaboration nobody mentions until it
bites.
**Roles.** Hosts assign access in tiers typically read (clone, comment), then write/develop (push
**Roles.** Hosts assign access in tiers, typically read (clone, comment), then write/develop (push
branches, open PRs), then maintain/admin (manage settings, force-merge, change protections). A
contributor only needs *write* to do the whole loop above; admin is for the people running the repo.
Give out the least that lets someone do their job the same least-privilege instinct you already
Give out the least that lets someone do their job, the same least-privilege instinct you already
have for production systems.
**Protected branches.** This is the enforcement mechanism. You mark `main` (and any other shared
@@ -183,38 +185,38 @@ can layer rules on top:
Turning these on converts "we agreed not to push to `main`" into "the server won't let you." For a
solo learner this can feel like bureaucracy, but it's exactly the guardrail that makes it safe to add
contributors you trust *less than fully* including machine ones. (Required **status checks**
"CI must pass before merge" are the same protected-branch feature, but they need CI to exist first;
contributors you trust *less than fully*, including machine ones. (Required **status checks**,
"CI must pass before merge", are the same protected-branch feature, but they need CI to exist first;
that's Module 14. We'll come back and switch it on there.)
### The contributor who isn't human
Here's the synthesis the whole unit was building toward. Re-read the loop issue, branch,
implementation, PR, review, merge and notice that **nothing in it specifies that the contributor is
Here's the synthesis the whole unit was building toward. Re-read the loop (issue, branch,
implementation, PR, review, merge) and notice that **nothing in it specifies that the contributor is
a person.** That's not an accident; it's the most useful property of the whole system right now.
- **An agent is a contributor with a branch.** You hand an agent an issue (Module 9 already framed
assignees as a mix of humans and agents). It cuts a branch, implements, and opens a PR exactly
assignees as a mix of humans and agents). It cuts a branch, implements, and opens a PR, exactly
the loop above. A human reviews that PR on the same gate used for any teammate (Module 10). The
agent never touches `main`; the protected-branch rules and the review gate apply to it identically.
This is *why* the loop is worth assembling as a loop: it's the harness that lets you accept work
from a contributor whose judgment you don't fully trust yet.
- **Two agents in parallel are just two contributors needing branches.** The moment you run more than
one agent at once, you have the classic collaboration problem two workers who must not edit the
one agent at once, you have the classic collaboration problem: two workers who must not edit the
same files in the same working directory. That's not a new problem, and it already has an answer:
**worktrees (Module 7).** Each agent gets its own working directory and its own branch; they work
simultaneously, each opens its own PR, and you review and merge them independently. Worktrees
earned their module precisely so this case would already be solved by the time you got here.
- **The merge stays human (for now).** The agent can do every step *up to* merge. The merge the
commitment to shared `main` is where a human stays in the loop, because review is judgment and
- **The merge stays human (for now).** The agent can do every step *up to* merge. The merge, the
commitment to shared `main`, is where a human stays in the loop, because review is judgment and
judgment is the thing you haven't delegated yet. Unit 5 is about carefully, conditionally moving
that line; this module is where you should be able to *picture* an agent doing the first five steps
while you do the sixth.
The reframe to carry forward: **collaboration tooling was never really about humans.** It's about
coordinating *contributors* isolating their work, making it reviewable, controlling who can commit
coordinating *contributors*: isolating their work, making it reviewable, controlling who can commit
it to the trunk. Those guarantees are exactly what you need to safely let an agent contribute, which
is why the team layer you just learned doubles as the agent-safety layer you'll lean on for the rest
of the course.
@@ -223,26 +225,26 @@ of the course.
## The AI angle
A generic "intro to team git" lesson ends at "branch, PR, review, merge congrats, you can work on a
A generic "intro to team git" lesson ends at "branch, PR, review, merge, congrats, you can work on a
team." This module's reason to exist is that **the team you're coordinating now includes agents, and
the loop is what makes that safe.**
- **The loop is the harness for untrusted contributors and an agent is one.** Branch isolation,
the PR boundary, mandatory review, protected `main` every one of these was designed to let work
- **The loop is the harness for untrusted contributors, and an agent is one.** Branch isolation,
the PR boundary, mandatory review, protected `main`: every one of these was designed to let work
flow from someone whose every change you don't personally vouch for. That's the exact profile of an
agent. You don't need new tooling to put an agent to work; you need the tooling you just learned,
pointed at a new kind of contributor.
- **Volume goes up; the gate has to hold.** A human contributor opens a PR a day. An agent can open
five before lunch. The review gate (Module 10) and the protected-branch rules are what keep that
volume from landing unreviewed on `main`. The faster your contributors, the more the gate earns its
keep same lesson as Module 1, one layer up.
keep, the same lesson as Module 1, one layer up.
- **Parallel agents are a solved problem, on purpose.** Two agents at once is just two contributors
needing isolation worktrees (Module 7) and separate branches. You already have the answer; this
needing isolation: worktrees (Module 7) and separate branches. You already have the answer; this
module is where you see *why* you were given it.
- **The auto-closing trail is memory for the next session.** Issue → PR → diff → merge is exactly the
durable, on-disk-and-on-host record a fresh agent reads to reconstruct "why does this exist?"
(Module 2's durable-memory reframe, now spanning the whole loop). Linking the PR to the issue isn't
bookkeeping; it's writing the project's memory in a form the next contributor human or machine
bookkeeping; it's writing the project's memory in a form the next contributor, human or machine,
can follow.
You're not learning collaboration *and then* learning to work with agents. They're the same skill.
@@ -251,27 +253,29 @@ You're not learning collaboration *and then* learning to work with agents. They'
## Hands-on lab
**Lab language:** shell (git commands) plus your host's web UI for the issue, PR, review, and merge
steps. You'll implement the feature with your AI the way Module 4 taught — agent editing the files
directly, you reviewing the diff.
**Lab language:** shell plus your host's web UI for the issue, PR, review, and merge steps. From
Module 4 on you direct the AI to do the git work and verify the result; the only commands you type by
hand here are read-only checks like `git branch` and `git show`. You'll implement the feature with
Claude Code (sub your own agent) the way Module 4 taught: the agent edits the files directly, you
review the diff.
The goal is to run the **entire outer loop once**, on the `tasks-app`, and watch the issue close
itself on merge. One small feature, all seven stations.
**The feature:** add a `clear-done` command to the CLI that removes every completed task. It's a
deliberately small, two-file change (logic in `tasks.py`, wiring in `cli.py`) small enough that the
deliberately small, two-file change (logic in `tasks.py`, wiring in `cli.py`), small enough that the
loop, not the code, is what you're practicing.
**You'll need:**
- Your `tasks-app` repo from earlier modules, with a remote on your git host (Module 8) that supports
issues and PRs.
- Your `tasks-app` repo from earlier modules (`~/ai-workflow-course/tasks-app`), with a remote on your
git host (Module 8) that supports issues and PRs.
- Push access to that repo (it's yours, so you have it).
- Your editor-integrated AI tool (Module 4).
- Claude Code (sub your own agent), your editor-integrated AI from Module 4.
- Your host's CLI (`gh` for GitHub, `glab` for GitLab, `tea` for Gitea/Forgejo). The web UI covers the
whole human-driven loop (Parts AD), so there the CLI is just convenience. Part E is the exception:
for an *agent* to open the PR itself it has to reach the forge, which needs the CLI installed and
authenticated or you take the no-CLI fallback that section spells out.
authenticated, or you take the no-CLI fallback that section spells out.
Starter artifacts are in this module's `lab/`: `issue.md` (the issue to file) and `pr-body.md` (the
PR description, including the load-bearing closing keyword).
@@ -281,43 +285,55 @@ PR description, including the load-bearing closing keyword).
Before the loop, make `main` enforce what you've been doing by hand. In your host's web UI, open the
repo's branch-protection settings and protect `main` with **"require a pull request before merging."**
```bash
# Confirm the rule bites — this push should now be REFUSED by the host:
git switch main
echo "# direct edit" >> README.md
git commit -am "try to push straight to main"
git push # expect: remote rejects the push to a protected branch
git reset --hard HEAD~1 # undo the local commit; we'll add the feature the right way, via a PR
```
Now prove the rule bites. Working in `~/ai-workflow-course/tasks-app`, tell Claude Code to make a
throwaway edit on `main` and push it straight up:
(That `git reset --hard HEAD~1` is a sharp, history-rewriting command from a later module — it drops
your most recent commit *and* its changes. It's safe here only because that commit was a throwaway to
test the guardrail; its full treatment and its real dangers are **Module 12**.)
> "On the `main` branch, append a comment line to `README.md`, commit it, and push directly to the
> remote. This is a deliberate test of branch protection."
If the push went through, protection isn't on — fix that before continuing. Feeling the server say
*no* is the point: "never commit to `main`" is now a rule, not a resolution.
Watch the push come back **rejected**: the host refuses a direct push to a protected branch. That
refusal is the whole point of Part A. Then have the agent undo the throwaway commit:
> "Good, the host rejected it. Drop that last commit and its changes so we're back to a clean `main`,
> then we'll do this the right way through a PR."
The agent reaches for `git reset --hard HEAD~1` here. That's a sharp, history-rewriting command from a
later module: it drops your most recent commit *and* its changes. It's safe only because that commit
was a throwaway to test the guardrail. Its full treatment and its real dangers are **Module 12**.
If the push went through instead of bouncing, protection isn't on; fix that before continuing. Feeling
the server say *no* is the point: "never commit to `main`" is now a rule, not a resolution.
### Part B — Issue → branch
1. **File the issue.** Create a new issue from `lab/issue.md` (title and body). Note its number say
1. **File the issue.** Create a new issue from `lab/issue.md` (title and body). Note its number; say
it's `#42`. This is the contract.
2. **Branch for it**, naming the branch after the issue:
2. **Branch for it**, naming the branch after the issue. Tell Claude Code to sync `main` and cut the
branch:
> "Sync `main` with the remote, then create and switch to a branch named `42-clear-done-command`
> (use my issue number)."
Verify it landed before moving on:
```bash
git switch main && git pull # start from current main
git switch -c 42-clear-done-command # use YOUR issue number
git branch # the new 42-clear-done-command branch, marked current with *
git status # "On branch 42-clear-done-command", working tree clean
```
The branch-naming convention (issue number plus a short slug) is the thing to get right here, not
the keystrokes.
### Part C — Implementation (with AI)
3. Point your editor-integrated AI at the repo and ask for the feature:
3. Point Claude Code at `~/ai-workflow-course/tasks-app` and ask for the feature:
> "Add a `clear-done` command. In `tasks.py`, add a `TaskList` method that removes all completed
> tasks. In `cli.py`, wire up a `clear-done` command that calls it, saves, and prints how many
> were removed. Match the existing style."
4. **Review the diff before you trust it** the Module 2 habit, the Module 10 skill:
4. **Review the diff before you trust it** (the Module 2 habit, the Module 10 skill):
```bash
git diff
@@ -337,12 +353,17 @@ If the push went through, protection isn't on — fix that before continuing. Fe
Read the index off `list` rather than assuming it: `done` is positional, and your `tasks-app` has
been carrying tasks since Module 1, so "trash" won't reliably land at index 1.
5. Commit and push the branch:
5. **Have the agent commit and push.** Tell Claude Code to stage just the two changed files, commit
with a message that closes the issue, and publish the branch:
> "Commit `tasks.py` and `cli.py` with a message like `Add clear-done command (closes #42)` (use my
> issue number and the closing keyword), then push the branch to the remote."
Verify before you trust it: the commit staged **only** those two files, and the subject carries the
closing keyword.
```bash
git add tasks.py cli.py
git commit -m "Add clear-done command (closes #42)"
git push -u origin 42-clear-done-command
git show --stat HEAD # only tasks.py and cli.py listed; subject ends "(closes #42)"
```
### Part D — PR → review → merge → auto-close
@@ -363,12 +384,18 @@ If the push went through, protection isn't on — fix that before continuing. Fe
approval). Delete the branch when prompted.
9. **Watch the issue close itself.** Open issue `#42`. It should now be **closed**, with a link to
the PR that closed it. You didn't touch the issue the merge did. That click is the whole loop
the PR that closed it. You didn't touch the issue; the merge did. That click is the whole loop
landing.
Now have Claude Code bring the merged work down and tidy up:
> "Switch to `main`, pull the merged work, and delete the now-merged local branch
> `42-clear-done-command`."
Verify the branch is gone:
```bash
git switch main && git pull # bring the merged work down locally
git branch -d 42-clear-done-command # tidy up the local branch
git branch # 42-clear-done-command no longer listed; you're on main
```
### Part E — Now make the contributor an agent
@@ -379,7 +406,7 @@ method already exists, so this is wiring only).
**First, a reality check the rest of the lab let you skip.** Two of those steps cross the forge
boundary: the agent has to *read* issue #43 from the forge and *open* a PR back into it. Your Module 4
editor agent only edits files and runs local commands and `git push` publishes a branch, it does
editor agent only edits files and runs local commands, and `git push` publishes a branch, it does
**not** open a PR. The web UI you've been clicking can't be handed to the agent. So before you prompt,
give the agent a way to reach the forge. Pick one path:
@@ -391,20 +418,20 @@ give the agent a way to reach the forge. Pick one path:
> referencing the issue with a closing keyword, push the branch, and open a PR into `main` whose
> description closes #43."
- **No-CLI fallback (you open the PR).** Have the agent do everything local branch, implement,
commit, push and *you* open the PR in the web UI, reusing `lab/pr-body.md` and keeping the
- **No-CLI fallback (you open the PR).** Have the agent do everything local (branch, implement,
commit, push) and *you* open the PR in the web UI, reusing `lab/pr-body.md` and keeping the
`Closes #43` line. Prompt it the same way, but stop it at the push:
> "Take issue #43. Create a branch named `43-pending-command`, implement the feature, commit
> referencing the issue with a closing keyword, and push the branch. I'll open the PR."
Wiring an agent *directly* into the forge so it reads issues and opens PRs with no human hand-off
and no CLI to shell out to is what an MCP forge integration buys you in **Module 20**. Here you're
Wiring an agent *directly* into the forge, so it reads issues and opens PRs with no human hand-off
and no CLI to shell out to, is what an MCP forge integration buys you in **Module 20**. Here you're
feeling the exact seam that module closes.
Either way, let the agent drive to the open-PR state. Then **you** are the human at the gate: review
the diff, and merge (or request changes) yourself. You've just watched the exact loop run with a
non-human contributor and felt precisely where you, the human, stayed in it. If you want the
non-human contributor, and felt precisely where you, the human, stayed in it. If you want the
parallel-agents case, file two issues and run two agents in separate worktrees (Module 7), each on its
own branch.
@@ -414,33 +441,33 @@ own branch.
- **Auto-close only fires on merge to the *default* branch.** Closing keywords close the issue when
the PR lands on `main` (or whatever your default is). Merge into a non-default branch and the issue
stays open by design. Keep the keyword in the *PR description* (or a commit message); a closing
stays open, by design. Keep the keyword in the *PR description* (or a commit message); a closing
keyword buried in a mid-thread comment behaves differently across hosts.
- **The exact keyword set is host-specific.** `Closes/Fixes/Resolves` are the safe, widely-supported
trio, but the full list and the cross-repo syntax (`owner/repo#42`, needed when a fork's PR closes
an upstream issue) vary by host. When in doubt, mention-link and close the issue by hand the trail
an upstream issue) vary by host. When in doubt, mention-link and close the issue by hand; the trail
still exists.
- **Auto-closed is not the same as actually done.** Merging closes the issue *mechanically*. It says
nothing about whether the work was correct that judgment was the review (Module 10), and if review
nothing about whether the work was correct; that judgment was the review (Module 10), and if review
was a rubber stamp, you just auto-closed an issue for broken work. The loop automates the
bookkeeping, never the thinking.
- **Protected branches protect against accidents, not admins.** Most hosts let admins bypass
protection (sometimes silently). And an account with push access including a *bot* account you set
up for an agent is an attack surface and a blast radius: its token can push branches and, if
protection (sometimes silently). And an account with push access, including a *bot* account you set
up for an agent, is an attack surface and a blast radius: its token can push branches and, if
over-permissioned, merge them. Scope machine accounts to the least they need; this is the front edge
of a problem Unit 4 takes head-on.
- **Forks add real friction beyond the extra clone.** Keeping a fork in sync with a fast-moving
upstream is ongoing work, and PRs *from* forks are deliberately limited by hosts (for example, they
often can't access the upstream repo's CI secrets relevant once you reach Module 14). For repos
often can't access the upstream repo's CI secrets, relevant once you reach Module 14). For repos
you own, prefer branches; reach for forks only when you genuinely lack push access.
- **The loop diagram is the happy path.** Real PRs get change requests, need updating when `main`
moves underneath them, or hit a merge conflict (Module 6) when two contributors touched the same
lines exactly
lines, exactly
the parallel-agent scenario worktrees mitigate but don't eliminate. The stations are fixed; the
number of trips around them isn't.
- **Squash-merge collapses authorship.** If your team squashes, the agent's (or your) individual
commits become one commit on `main`, and the per-commit trail lives only on the now-deleted branch /
closed PR. That's usually a fine trade for a clean history just know the granular history moved
closed PR. That's usually a fine trade for a clean history; just know the granular history moved
from `main` to the PR record.
---
@@ -449,7 +476,7 @@ own branch.
**You're done when:**
- You ran the full loop on `tasks-app` at least once and watched an issue close itself on merge
- You ran the full loop on `tasks-app` at least once and watched an issue close itself on merge,
with `main` protected so the PR was mandatory, not optional.
- You can draw the seven-station loop (issue → branch → implementation → PR → review → merge → closed)
from memory and say which earlier module owns each station.
@@ -461,7 +488,7 @@ own branch.
- You can explain why the same tooling that coordinates human teammates is what makes accepting an
agent's work safe.
When the loop feels like one motion rather than six separate tools and when "give the agent a
branch and review its PR" feels obvious rather than novel you're ready for Module 12, where we make
When the loop feels like one motion rather than six separate tools, and when "give the agent a
branch and review its PR" feels obvious rather than novel, you're ready for Module 12, where we make
the *recovery* half of this safety net its own discipline: reverting a bad PR after it's already
merged.