-
Notifications
You must be signed in to change notification settings - Fork 0
11 collaboration humans and agents
📖 This page is generated from
modules/11-collaboration-humans-and-agents/README.md. Edit the source, not the wiki; edits here are overwritten on the next sync. Run the hands-on labs from the repo, linked inline.
⬅ Previous: Module 10: Reviewing Code You Didn't Write
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.
This is the synthesis module for Unit 2's collaboration arc. It assumes the whole chain up to here:
-
Module 2: commits as checkpoints, and
git diff/git logas the record everyone reads. -
Module 6: branches as isolated sandboxes; you make changes off
main, not on it. - Module 7: worktrees, so more than one branch (and more than one agent) can be live at once without stepping on each other.
- Module 8: a remote on a git host (GitHub the default; a self-hosted forge if you took that track), so there's a shared copy to collaborate around.
- Module 9: issues: the task layer that says what needs doing and who (human or agent) owns it.
- 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, so go back and fill it in.
By the end of this module you can:
- Run the full collaboration loop end to end (issue → branch → implementation → PR → review → merge → issue auto-closed) and explain why each step exists.
- Link a PR to an issue so the merge closes the issue automatically, and explain when that does and doesn't fire.
- Decide correctly between a branch and a fork based on whether you have push access.
- Reason about who's allowed to push: roles, protected branches, and why "never commit to
main" stops being a personal habit and becomes an enforced rule. - Treat an agent as a contributor (give it a branch, route an issue to it, review its PR on the same gate you'd use for a human) and know where a human has to stay in the loop.
Module 2 gave you the inner loop: edit, git diff, commit, repeat. That loop lives on your disk
and is yours alone. It's how you (or your agent) make progress in a working session.
This module is the outer loop, the one the team sees:
issue → branch → implementation → pull request → review → merge → issue closed
(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
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
failure modes every team knows: work nobody asked for, changes that land straight on main with no
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 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
(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
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.
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:
you, or an agent, making commits on the branch. Nothing here is new; it's the edit/diff/commit
rhythm you already have. The branch keeps it isolated, so however bold the change, main is
untouched until the loop says otherwise.
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
to be considered for main." It bundles the diff, a description, and a discussion thread into one
reviewable unit. Crucially, this is where you link back to the issue (next section) so the loop
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, 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
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.
7. The issue closes, ideally by itself. If you linked the PR correctly, merging closes the issue automatically. The receipt is written without anyone touching the issue. That's the satisfying click of the whole loop landing, and it's the concrete thing the lab makes you feel.
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
Closes, Fixes, and Resolves (and their variants close/closed, fix/fixed,
resolve/resolved) all work on the major hosts. When the PR merges into the default branch, the
host closes the referenced issue and cross-links the two so each shows the other. One line in the PR
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.
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-doneexist?" The answer is one click away: issue → PR → diff → merge. You built that trail for free by linking one line.
There are two ways a contributor gets their work in front of the team, and the deciding question is simple: can you push to the repo?
-
You have push (write) access → branch in the repo. This is the normal case for a team working
on a shared repo, and everything above assumes it. Your branch lives alongside everyone else's on
the same remote; PRs go branch →
mainwithin one repo. -
You don't have push access → fork, then PR from the fork. This is the open-source contribution
model and the "outside contributor" case. You clone the repo into your own copy (a fork), push
branches there, and open a PR across repos from
your-fork:branchintoupstream:main. The maintainers review and merge; you never needed write access to their repo.
# Forked-contributor flow (no push access to upstream):
# 1. Fork upstream/repo -> you-now-own you/repo (one click on the host)
# 2. git clone https://host/you/repo
# 3. git switch -c my-fix ; ...commit...
# 4. git push -u origin my-fix # origin = your fork, which you CAN push to
# 5. Open a PR from you/repo:my-fix -> upstream/repo:mainFor 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 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.
"Never commit directly to main" started as a personal discipline. On a shared repo it becomes an
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 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 have for production systems.
Protected branches. This is the enforcement mechanism. You mark main (and any other shared
branch) as protected, and the host then refuses direct pushes to it. The only way in is a PR. You
can layer rules on top:
- Require a pull request: no direct pushes, full stop. The loop is mandatory, not optional.
- Require a review approval: at least one non-author approval before merge is allowed.
- Restrict who can merge: only certain roles can click the button.
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;
that's Module 14. We'll come back and switch it on there.)
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 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 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 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 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.
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 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, 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 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, can follow.
You're not learning collaboration and then learning to work with agents. They're the same skill.
Starting point (this lab is skip-friendly). You do not need to have done the earlier labs. To begin from a clean, known state, copy this module's snapshot into a fresh
tasks-appand make the first commit:mkdir -p ~/ai-workflow-course/tasks-app cp -r ~/ai-workflow-course/modules/11-collaboration-humans-and-agents/lab/start/. ~/ai-workflow-course/tasks-app/ cd ~/ai-workflow-course/tasks-app && git init -b main && git add -A && git commit -m "start: module 11"Already carrying your
tasks-appfrom earlier modules? Keep using it and ignore this box. 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 likegit branchandgit 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
loop, not the code, is what you're practicing.
You'll need:
- Your
tasks-apprepo 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).
- Claude Code (sub your own agent), your editor-integrated AI from Module 4.
- Your host's CLI (
ghfor GitHub,glabfor GitLab,teafor Gitea/Forgejo). The web UI covers the whole human-driven loop (Parts A–D), 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.
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).
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."
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:
"On the
mainbranch, append a comment line toREADME.md, commit it, and push directly to the remote. This is a deliberate test of branch protection."
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.
-
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. -
Branch for it, naming the branch after the issue. Tell Claude Code to sync
mainand cut the branch:"Sync
mainwith the remote, then create and switch to a branch named42-clear-done-command(use my issue number)."Verify it landed before moving on:
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.
-
Point Claude Code at
~/ai-workflow-course/tasks-appand ask for the feature:"Add a
clear-donecommand. Intasks.py, add aTaskListmethod that removes all completed tasks. Incli.py, wire up aclear-donecommand that calls it, saves, and prints how many were removed. Match the existing style." -
Review the diff before you trust it (the Module 2 habit, the Module 10 skill):
git diff
Confirm it touched only
tasks.pyandcli.py, the logic lives intasks.py(not crammed into the CLI), and it does what you asked. Run it:python3 cli.py add "keeper" ; python3 cli.py add "trash" python3 cli.py list # note the index shown next to "trash" python3 cli.py done <trash-index> # use the index "list" just printed, NOT a fixed 1 python3 cli.py clear-done # expect it to remove the completed one python3 cli.py list # "keeper" remains, "trash" is gone
Read the index off
listrather than assuming it:doneis positional, and yourtasks-apphas been carrying tasks since Module 1, so "trash" won't reliably land at index 1. -
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.pyandcli.pywith a message likeAdd 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.
git show --stat HEAD # only tasks.py and cli.py listed; subject ends "(closes #42)"
-
Open the PR from your branch into
main, usinglab/pr-body.mdas the description. Make sure the body contains the closing line with your issue number:Closes #42 -
Review it. Open the PR's "Files changed" tab and read the diff as a reviewer, not as the author, the Module 10 move. For the full effect, pretend an agent wrote it (in a moment, one will): is the logic where it belongs? Any edge case missed (empty list, nothing done yet)? Approve it.
-
Merge it. Click merge (your protection rule required the PR and, if you added it, the approval). Delete the branch when prompted.
-
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 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 branch42-clear-done-command."Verify the branch is gone:
git branch # 42-clear-done-command no longer listed; you're on main
Run the loop one more time, but this time let an agent be the contributor for steps 2–6. File a
second issue (e.g. "Add a pending command that lists only incomplete tasks"; the TaskList.pending()
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
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:
-
Full agent-opens-PR path (host CLI required). Install and authenticate your host's CLI (
gh,glab, ortea) so the agent can run, e.g.,gh pr createitself. For this step the CLI is a requirement, not the convenience it was in Parts A–D. Then prompt the agent:"Take issue #43. Create a branch named
43-pending-command, implement the feature, commit referencing the issue with a closing keyword, push the branch, and open a PR intomainwhose 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.mdand keeping theCloses #43line. 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 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 parallel-agents case, file two issues and run two agents in separate worktrees (Module 7), each on its 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 keyword buried in a mid-thread comment behaves differently across hosts. -
The exact keyword set is host-specific.
Closes/Fixes/Resolvesare 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 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 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 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 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
mainmoves underneath them, or hit a merge conflict (Module 6) when two contributors touched the same 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 frommainto the PR record.
You're done when:
- You ran the full loop on
tasks-appat least once and watched an issue close itself on merge, withmainprotected 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.
- You can state the branch-vs-fork rule in one sentence (push access → branch; no push access → fork) and why an agent follows the same rule.
- You ran at least one trip around the loop with an agent as the contributor for the implement-and-open-PR steps, and can point to the exact step where you, the human, stayed in the loop (the merge).
- 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 the recovery half of this safety net its own discipline: reverting a bad PR after it's already merged.
Continue to: Module 12: When It Goes Wrong: Revert, Reset, and Recovery ➡
Generated from the ai-workflow-course repo • the model is the cheap, swappable part; the workflow is the durable skill.
Unit 1: Get out of the chat window
- 1 · The Copy-Paste Problem
- 2 · Version Control as a Safety Net
- 3 · Version Control for Words, Not Just Code
- 4 · Getting the AI Out of the Browser
- 5 · Commit the AI's Config, Not Just the Code
- 6 · Branches as Sandboxes for Experiments
- 7 · Worktrees for Running Agents in Parallel
Unit 2: Make it shareable, reviewable, recoverable
- 8 · Remotes and Hosting (GitHub, the Alternatives, and Owning Your Repo)
- 9 · Issues and the Task Layer
- 10 · Reviewing Code You Didn't Write
- 11 · Collaboration: Humans and Agents on One Repo
- 12 · When It Goes Wrong: Revert, Reset, and Recovery
Unit 3: Automate the checking and shipping
- 13 · Testing in the AI Era
- 14 · Continuous Integration
- 15 · Security Scanning for AI-Generated Code
- 16 · Containers and Reproducible Environments
- 17 · Secrets, Config, and Environments
- 18 · Continuous Delivery and Deployment
- 19 · Runners, the Compute Behind the Automation
Unit 4: Extend the AI into your systems
- 20 · MCP Servers, Giving the AI Hands
- 21 · Skills: Teaching the AI Your Playbook
- 22 · Securing Third-Party MCP Servers and Skills
- 23 · Working with Existing Codebases
Unit 5: AI in the Loop
- 24 · Assistive Agents (AI Review and Issue Triage)
- 25 · Module 25. Autonomous Agents: Issue-to-PR and Self-Healing CI
- 26 · Orchestrating Multiple Agents
- 27 · Module 27. Evals: Trusting an Agent That Acts Without You
Finale