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:
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user