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
+93 -81
View File
@@ -1,7 +1,7 @@
# Module 8 — Remotes and Hosting: GitHub, the Alternatives, and Owning Your Repo
> **One repo on one laptop is one spilled coffee away from gone.** A remote gets your history
> off your machine and somewhere durable — and because every clone carries the full history, a
> off your machine and somewhere durable. And because every clone carries the full history, a
> working team backs itself up just by working.
---
@@ -44,14 +44,14 @@ By the end of this module you can:
A **remote** is a named reference to *another copy of this same repository*, usually somewhere you
can reach over the network. That's it. `origin` is not a
GitHub concept, a GitLab concept, or a Gitea concept — it's a Git concept, and the copy it points at
GitHub concept, a GitLab concept, or a Gitea concept. It's a Git concept, and the copy it points at
is a full, equal Git repo that happens to live on a server.
This is the fact the entire rest of the module rests on, so sit with it: **because a remote is just
This is the fact the entire rest of the module rests on: **because a remote is just
another copy, the commands you use to talk to it are identical no matter who hosts it.** `git push`
to GitHub is byte-for-byte the same operation as `git push` to a **forge** (a Git hosting platform
GitHub, GitLab, Gitea, Forgejo, and the like) you run yourself in a locked-down rack. The provider is
a logistics decision uptime, price, who can see it, where the servers sit not a Git decision. We
to GitHub is byte-for-byte the same operation as `git push` to a **forge** (a Git hosting platform
like GitHub, GitLab, Gitea, or Forgejo) you run yourself in a locked-down rack. The provider is
a logistics decision (uptime, price, who can see it, where the servers sit), not a Git decision. We
lean on GitHub as the worked example below *only* because it's
the one you're most likely to hit first, not because the mechanics change anywhere else.
@@ -85,17 +85,25 @@ the shape is the same:
host).
- **SSH** — `git@host:you/tasks-app.git`. Authenticates with an SSH key you've added to your
account. More setup once, less friction forever.
3. Point your local repo at it and push:
3. Register the remote on the local side and push the history up. The shape of that exchange, with a
first push to an empty remote, looks like this:
```bash
cd ~/ai-workflow-course/tasks-app
git remote add origin <URL-you-copied>
git push -u origin main
```console
$ git remote add origin <URL-you-copied>
$ git push -u origin main
Enumerating objects: 24, done.
...
To github.com:you/tasks-app.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
```
In the lab you direct your agent to run that and then verify the result; here we're just reading
what it does.
That `-u` (short for `--set-upstream`) is worth understanding, not just copying: it records that your
local `main` *tracks* `origin/main`. After it, `git status` will tell you things like "your branch is
ahead of origin/main by 2 commits" the ahead/behind report you met in Module 2, now meaningful
ahead of origin/main by 2 commits", the ahead/behind report you met in Module 2, now meaningful
because there's finally a remote to be ahead *of*. And `git push` / `git pull` with no arguments know
where to go.
@@ -105,15 +113,15 @@ Everyone hits at least one of these. Recognizing them by their error text saves
**1. Authentication fails.** You push and get `Authentication failed`, `Permission denied
(publickey)`, or a `403`. Two different causes hide behind that wall, and they have different fixes.
The common one is *no usable credential at all* you tried an account password (dead on every modern
The common one is *no usable credential at all*: you tried an account password (dead on every modern
host) or never set up a token / SSH key. The sneakier one is a credential that *exists but lacks the
right scope*: a token authenticates fine and then the push is refused with `403` because the token was
never granted write access to repositories. They look alike but you fix them differently — create a
credential vs. *edit the existing token's scopes* (don't regenerate it). For the no-credential case:
for HTTPS, generate a personal access token in the host's settings and use it as your password when
prompted; for SSH, generate a key (`ssh-keygen`) and paste the public half into the host's SSH-keys
settings. This is host-specific UI but the *concept* is identical everywhere — the callout below walks
the shape of getting one.
never granted write access to repositories. They look alike but you fix them differently. One needs a
credential created; the other needs you to *edit the existing token's scopes* (don't regenerate it).
For the no-credential case: for HTTPS, generate a personal access token in the host's settings and use
it as your password when prompted; for SSH, generate a key (`ssh-keygen`) and paste the public half
into the host's SSH-keys settings. This is host-specific UI but the *concept* is identical everywhere,
and the callout below walks the shape of getting one.
> ### Getting a credential (the shape)
>
@@ -167,12 +175,12 @@ pushing to the same place.
### Choosing a host: the comparison
GitHub is the titan. It is by a wide margin the largest forge, it's where most open source lives, and
it's the one AI tooling integrates with *first* when a new coding agent or MCP server ships, GitHub
GitHub dominates. It is by a wide margin the largest forge, it's where most open source lives, and
it's the one AI tooling integrates with *first*: when a new coding agent or MCP server ships, GitHub
support is usually in the first release and everything else trails. That makes it the sane default for
most people, and it's why this module uses it as the worked example. But "default" is not "only," and
for a team with on-prem, air-gapped, or data-control requirements a real and common constraint for
this audience it may be the wrong default. The genuine choice is between **hosted** (someone runs
for a team with on-prem, air-gapped, or data-control requirements (a real and common constraint for
this audience) it may be the wrong default. The genuine choice is between **hosted** (someone runs
the forge; you just use it) and **self-hosted** (you run the forge on your own infrastructure).
> ### Hosting comparison — as of 2026-06-22
@@ -240,7 +248,7 @@ with **1** offsite. Now look at what a normal team doing normal work ends up wit
A four-person team that pushes to one remote is sitting on five-plus complete, independent copies of
the entire project history across multiple locations and machines. They didn't run a backup tool.
They just worked. That's the quiet superpower of a *distributed* version control system: distribution
They just worked. That's the point of a *distributed* version control system: distribution
*is* the redundancy. The 3-2-1 rule, which most ops shops fight to satisfy deliberately, falls out of
a forge and a working team almost for free.
@@ -260,7 +268,7 @@ your secrets, your uncommitted work, your large binaries. We'll hold that though
## The AI angle
A remote isn't only about durability — it's the substrate the AI parts of this course run on.
A remote isn't only about durability. It's what the AI parts of this course run on.
- **Most AI tooling integrates with the forge first, not your laptop.** AI reviewers, issue-to-PR
agents, and the CI that catches code which merely *looks* right (Modules 10, 14, and Unit 5) all
@@ -296,9 +304,12 @@ WSL, or Git Bash on Windows. Continues the `tasks-app` repo from Module 2.
- An account on a Git host. **Hosted track:** GitHub is the worked default, but GitLab, Bitbucket,
Codeberg, or any forge works with the identical commands. **Self-hosted track:** a Forgejo/Gitea
(or other) instance you can reach, and an account on it.
- The ability to authenticate to that host a personal access token (for HTTPS) or an SSH key added
to your account. Set this up first; failure mode #1 above is the most common first-push wall.
- Your AI assistant (still the way you've used it — this lab is about the remote, not the editor).
- The ability to authenticate to that host: a personal access token (for HTTPS) or an SSH key added
to your account. This is the one part you set up by hand in the host's web UI, since it's account
security, not git. Do it first; failure mode #1 above is the most common first-push wall.
- Claude Code (or sub your own agent) in your terminal, set up as in Module 4. In this lab you
*direct the agent* to do the git work — add the remote, push, clone, fetch, pull — and you verify
each result yourself. You don't type the git commands by hand.
### Part A — Create the empty remote and push
@@ -310,19 +321,22 @@ WSL, or Git Bash on Windows. Continues the `tasks-app` repo from Module 2.
> the hosted track is the URL (your forge's hostname) and how you authenticate to your box.
> Everything from here on is the same commands.
2. Point your repo at the remote and push:
2. From `~/ai-workflow-course/tasks-app`, tell your agent what you want and let it run the git. A
prompt like:
> "Add a remote named `origin` at <URL> and push `main` up with upstream tracking."
Then verify it did exactly that, with your own eyes:
```bash
cd ~/ai-workflow-course/tasks-app
git remote -v # probably empty — no remote yet
git remote add origin <URL> # paste the URL you copied
git remote -v # now origin shows, for fetch and push
git push -u origin main # send main up and link it
git remote -v # origin should show, for both fetch and push
```
If `push` errors, match it to the three failure modes above: `Authentication failed` / `Permission
denied` → token or SSH key (#1); `non-fast-forward` / `fetch first` → the remote wasn't empty (#2);
`src refspec main does not match` → branch-name mismatch, check `git branch` (#3). Fix and re-push.
Confirm `origin` points at your URL, and that the push reported `branch 'main' set up to track
'origin/main'`. If the push errored, match the error to the three failure modes above before you
re-prompt: `Authentication failed` / `Permission denied` → token or SSH key (#1); `non-fast-forward`
/ `fetch first` → the remote wasn't empty (#2); `src refspec main does not match` → branch-name
mismatch, check `git branch` (#3). Tell the agent the fix and have it push again.
3. Confirm the offsite copy exists: refresh the host's web page for the repo. Your files and your full
commit history from Module 2 are now sitting on hardware that is not your laptop. **That is the
@@ -333,28 +347,28 @@ WSL, or Git Bash on Windows. Continues the `tasks-app` repo from Module 2.
You're going to demonstrate the 3-2-1 claim with your own eyes: that a clone is a *complete,
independent* copy, history and all — not a snapshot.
4. Make a change locally, commit it, and push it (with the AI if you like — e.g. ask for a `version`
command that prints the app version):
4. Direct your agent to make a change and ship it in one go:
> "Add a `version` command that prints the app version, commit it, and push to origin."
Then verify: `git log --oneline -1` shows the new commit, and `git status` reports your branch is
up to date with `origin/main` (nothing left stranded to push).
5. Have your agent clone the remote into a *separate* directory, as if you were a teammate on a fresh
machine:
> "Clone <URL> into `~/ai-workflow-course/tasks-app-teammate`."
Now inspect the clone yourself. This is the see-it-with-your-own-eyes step, so you run the look:
```bash
# apply the change, then:
git add .
git commit -m "Add version command"
git push # no args needed now, thanks to -u earlier
git -C ~/ai-workflow-course/tasks-app-teammate log --oneline # the ENTIRE history is here
```
5. Now clone the remote into a *separate* directory, as if you were a teammate on a fresh machine:
```bash
cd ~/ai-workflow-course
git clone <URL> tasks-app-teammate
cd tasks-app-teammate
git log --oneline # the ENTIRE history is here — every commit, not just the latest
```
Compare the commit count to your original repo (`git log --oneline | wc -l` in each). They match.
The clone didn't get "the current files" — it got the whole project's memory. That's the property
that makes a working team into an accidental backup system.
Every commit, not just the latest. Compare the commit count to your original repo
(`git log --oneline | wc -l` in each). They match. The clone didn't get "the current files"; it
got the whole project's memory. That's the property that makes a working team into an accidental
backup system.
6. Run the provided check from this module's `lab/` to make the point mechanically:
@@ -376,43 +390,41 @@ independent* copy, history and all — not a snapshot.
### Part C — The everyday loop
7. Edit the README in your *teammate* clone, commit, and push from there:
7. From the *teammate* clone, direct your agent to make and ship a change:
> "In `~/ai-workflow-course/tasks-app-teammate`, note the remote in the README, commit, and push."
8. Back in your *original* repo, get the teammate's commit, but look before you leap. First have the
agent fetch without merging:
> "In `~/ai-workflow-course/tasks-app`, fetch from origin but don't merge yet."
Then read exactly what's incoming yourself, before anything touches your files. This inspection is
the habit, so you run it:
```bash
cd ~/ai-workflow-course/tasks-app-teammate
# edit README.md, then:
git add . && git commit -m "Note the remote in the README"
git push
git -C ~/ai-workflow-course/tasks-app log main..origin/main # SEE what's incoming
```
8. Back in your *original* repo, pull it down:
Once you've seen what's coming, tell the agent to take it:
```bash
cd ~/ai-workflow-course/tasks-app
git fetch # download the new commit, but don't merge yet
git log main..origin/main # SEE exactly what's incoming before you take it
git pull # now merge it into your local main
git log --oneline # the teammate's commit is now here too
```
> "Now pull origin/main into main."
That fetch-then-look-then-pull rhythm is the habit to keep: you saw what was coming before you let
it touch your files. You've now pushed *and* pulled across two independent copies through one
remote — the complete remotes mechanic.
Verify with `git -C ~/ai-workflow-course/tasks-app log --oneline` that the teammate's commit
landed. That fetch-then-look-then-pull rhythm is the habit to keep: you saw what was coming before
you let it touch your files. You've now pushed *and* pulled across two independent copies through
one remote, the complete remotes mechanic.
### Part D (optional) — A second remote
9. Add a *second* remote (a personal fork on another host, or even a bare repo on a USB drive or a
box on your LAN) and push to it too:
9. Direct your agent to add a *second* remote (a personal fork on another host, or even a bare repo on
a USB drive or a box on your LAN) and push to it too:
```bash
git remote add backup <SECOND-URL>
git push backup main
git remote -v # two remotes now: origin and backup
```
> "Add a remote named `backup` at <SECOND-URL> and push `main` to it."
You now literally have the 3-2-1 rule satisfied by hand: your laptop, `origin`, and `backup` — three
copies, more than one location. Nothing about Git stopped you from pointing at as many copies as you
want.
Then verify with `git remote -v`: two remotes now, `origin` and `backup`. You now literally have
the 3-2-1 rule satisfied across your laptop, `origin`, and `backup`: three copies, more than one
location. Nothing about Git stopped you from pointing at as many copies as you want.
---