Skip to content

docs: make the fork origin/upstream split explicit and own the upstream-sync procedure - #6

Merged
cm-maple7 merged 1 commit into
mainfrom
fm/fm-fork-as-origin-followthrough-2
Sep 1, 2026
Merged

cm-maple7 merged 1 commit into
mainfrom
fm/fm-fork-as-origin-followthrough-2

Conversation

@cm-maple7

Copy link
Copy Markdown
Owner

Follow-through on making this fork's origin/upstream split explicit, after the upstream sync landed in #5.

Our fork operates with origin = cm-maple7/firstmate (read-write) and upstream = kunchenguid/firstmate (read-only), but the tracked prose still read as if origin were the parent repository. The two remotes differ only by owner in the URL, so a wrong push or a pull request raised against the parent looks identical to a correct one until it lands.

What changed

CONTRIBUTING.md becomes the single owner of the fork's remote layout and the upstream-sync procedure. A new "Fork remotes and upstream sync" section states which remote is writable, that upstream is never pushed to and never receives a pull request from an independently operating fork, and the confirm-before-you-push habit (git remote get-url origin).

It then writes down the procedure executed in #5, because a fork cannot pick up upstream work by fetching alone - homes fast-forward from origin, so the work has to reach the fork's own default branch first:

  1. git fetch upstream, branch from the fork's main.
  2. git merge upstream/main - a merge commit, not a rebase, so homes fast-forward instead of reconciling rewritten history.
  3. Resolve conflicts with evidence, keeping a local fix wherever it still covers a case the upstream change does not.
  4. Run the gates on the merge result, push to origin, open the PR on the fork.
  5. Land with the configured merge authority; homes then fast-forward normally.

It also records the gate-mirror lesson: after any remote retarget, re-run no-mistakes init and inspect the mirror's own git remote -v, because the pipeline rebases and opens pull requests from that mirror's origin, not from the checkout you are working in. A stale mirror sends work to the wrong repository while every local check still looks correct.

Two corrections in CONTRIBUTING.md were load-bearing, not cosmetic:

  • The workflow step told contributors to set their local origin back to the parent (git@github.com:kunchenguid/firstmate.git). That is the one instruction in the repo that actively pointed a writable remote at upstream. It now keeps origin on the repository you can push to and adds the parent as a separate read-only upstream remote.
  • The contribution rule is now scoped to pull requests targeting the upstream repository's main, matching the Require no-mistakes check that PR fix(tests): stop the public-followup fixture from expiring #3 already scoped that way, so a downstream fork legitimately sets its own delivery rigor per change.

docs/verification/self-update.md (new, maintainer-verification, registered in docs/documentation-audiences.json) records the active evidence below.

.agents/skills/updatefirstmate/SKILL.md keeps owning the agent self-update path and gains one cross-reference line, so the sync procedure is stated once and pointed at from elsewhere.

/updatefirstmate verified end to end against the fork

In a throwaway home cloned from the fork and set one commit behind its default branch:

$ git clone https://github.com/cm-maple7/firstmate.git "$HOME_DIR"
$ git -C "$HOME_DIR" remote -v
origin	https://github.com/cm-maple7/firstmate.git (fetch)
origin	https://github.com/cm-maple7/firstmate.git (push)

$ git -C "$HOME_DIR" reset --hard HEAD~1 && git -C "$HOME_DIR" log --oneline -1
d77251e fix(bin): absorb background-run stale wakes and trust declared pauses over ci-monitoring (#2)

$ FM_ROOT_OVERRIDE="$HOME_DIR" FM_HOME="$HOME_DIR" bash bin/fm-update.sh
firstmate: updated d77251e..6aa4beb (instructions changed: AGENTS.md, bin, .agents/skills)
reread-firstmate: yes
nudge-secondmates: none

$ git -C "$HOME_DIR" log --oneline -1
6aa4beb Merge pull request #5 from cm-maple7/fm/fm-fork-as-origin-followthrough

The home fast-forwarded to the fork's default branch and correctly reported the instruction surface as changed. Because 6aa4beb is the merge that landed the upstream sync, this run also demonstrates the full chain end to end: upstream work reaches a home only after landing on the fork's own main.

Future treehouse pool clones inherit the fork remotes

They do, automatically, and no per-worktree repair step exists or is needed. Pool worktrees are git worktree entries that share the primary checkout's git directory, so they read its remote configuration rather than defining their own:

$ git -C <pool-worktree> rev-parse --git-common-dir
/Users/charlie/src/firstmate/.git

$ git -C <pool-worktree> remote get-url origin
https://github.com/cm-maple7/firstmate.git

Verified across the live pool: every listed worktree resolves origin to the fork. Correcting the remotes once in the primary checkout is what makes every existing and future pool worktree correct.

No script changed

No firstmate repository URL is hardcoded anywhere in bin/ or .agents/skills/:

$ grep -rn 'github.com[:/][A-Za-z0-9-]*/firstmate' bin/ .agents/skills/
(no matches)

Every code path - self-update, fleet sync, and remote-secondmate seeding - already resolves from each home's own origin, so origin = fork needs no code change. Remote-secondmate seeding resolves each project's origin dynamically and validates it through bin/fm-project-origin-lib.sh.

kunchenguid literals deliberately kept

None of these asserts write access or pull-request targeting, so all were left alone:

  • bin/fm-install-treehouse.sh, bin/fm-bootstrap.sh, docs/architecture.md - install sources and links for the separate treehouse and no-mistakes projects.
  • docs/fm-test-portable-shards.md, docs/gitlab-merge-watch.md - upstream CI run and PR links kept as verification evidence, which would be falsified by rewriting them.
  • .github/workflows/no-mistakes-required.yml - the scoping condition that makes the job upstream-only, plus the upstream action reference.
  • README.md - the public install clone URL and the project's X badge; cloning the upstream project is correct for a public consumer and implies nothing about push rights.

Suggested follow-up, not done here

upstream's push URL in the primary checkout is currently a writable GitHub URL. Setting git remote set-url --push upstream DISABLED there would make an accidental git push upstream fail at the client instead of relying on discipline. That is local, untracked configuration in the primary checkout rather than a change to this repository, so it is left for the captain.

Gates

bin/fm-lint.sh                  # ShellCheck 0.11.0 pinned; actionlint 1.7.12 pinned, 3 workflow files valid
bin/fm-doc-audience-check.sh    # ok surfaces=90 local_links=303

… procedure

Our fork operates with origin=cm-maple7/firstmate (read-write) and
upstream=kunchenguid/firstmate (read-only), but the tracked prose still read as
if origin were the parent repository. The concrete hazard is that both remotes
differ only by owner in the URL, so a wrong push or a pull request raised
against the parent looks identical to a correct one until it lands.

CONTRIBUTING.md becomes the single owner of the fork's remote layout and of the
deliberate upstream-sync procedure: fetch upstream, merge it into a branch so
the result is a merge commit homes can fast-forward onto, resolve conflicts with
evidence, run the gates, open the pull request on the fork, and land it with the
configured merge authority.

Two corrections there were load-bearing rather than cosmetic. The workflow step
that told contributors to set their local origin back to the parent repository
now has them keep origin on the repository they can push to and add the parent
as a separate read-only upstream remote. The contribution rule is now scoped to
pull requests targeting the upstream repository, matching the Require
no-mistakes check that is already scoped that way, so a fork legitimately sets
its own delivery rigor per change.

The section also records the gate-mirror lesson: after any remote retarget,
re-run `no-mistakes init` and inspect the mirror's own `git remote -v`, because
the pipeline rebases and opens pull requests from that mirror's origin rather
than from the checkout being worked in, so a stale mirror sends work to the
wrong repository while every local check still looks correct.

docs/verification/self-update.md records the active evidence that
/updatefirstmate advances a home from its own origin, verified end to end
against the fork in a throwaway clone, plus the evidence that treehouse pool
worktrees inherit the primary checkout's remotes because they share its git
directory. The updatefirstmate skill keeps owning the agent path and gains one
cross-reference line, so the sync procedure is stated once.

No script changed: no firstmate repo URL is hardcoded in bin/ or the skills, so
every code path already resolves from each home's own origin. The remaining
kunchenguid literals are genuine upstream references - the treehouse and
no-mistakes projects, upstream CI run links kept as verification evidence, and
the public install clone URL - and none of them asserts write access or
pull-request targeting.
@cm-maple7
cm-maple7 merged commit 43df826 into main Sep 1, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant