From 046cfbfae0699575e136df048f47af7b18c52ebb Mon Sep 17 00:00:00 2001 From: cm-maple7 Date: Mon, 31 Aug 2026 23:57:53 -0700 Subject: [PATCH] docs: make the fork's origin/upstream split explicit and own the sync 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. --- .agents/skills/updatefirstmate/SKILL.md | 1 + CONTRIBUTING.md | 30 +++++++++++++- docs/documentation-audiences.json | 4 ++ docs/verification/self-update.md | 53 +++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 docs/verification/self-update.md diff --git a/.agents/skills/updatefirstmate/SKILL.md b/.agents/skills/updatefirstmate/SKILL.md index 36e9a80b937..004b59f4f4e 100644 --- a/.agents/skills/updatefirstmate/SKILL.md +++ b/.agents/skills/updatefirstmate/SKILL.md @@ -29,6 +29,7 @@ This touches only the firstmate repo and its own worktrees, never anything under bin/fm-update.sh ``` It fast-forwards this firstmate repo's default branch from origin, then updates every registered local or remote secondmate home through its placement-specific guarded path. + Origin is the only source it advances from, so in a fork, upstream work reaches homes only after it has landed on the fork's own default branch; CONTRIBUTING.md's "Fork remotes and upstream sync" owns that deliberate sync procedure. It prints one status line per target (`updated ..` / `already current` / `skipped: `), followed by two action lines that tell you exactly what to do next: - `reread-firstmate: yes|no` - `nudge-secondmates: fm-...|none` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02c3a28af4a..32db5d80a8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,8 +3,9 @@ Thanks for wanting to contribute. One rule up front: -**Human-authored pull requests targeting `main` must be raised through [`no-mistakes`](https://github.com/kunchenguid/no-mistakes).** +**Human-authored pull requests targeting the upstream repository's `main` must be raised through [`no-mistakes`](https://github.com/kunchenguid/no-mistakes).** We require this to reduce the maintainer's burden of reviewing and merging contributions. +The `Require no-mistakes` check is scoped to the upstream repository, so a downstream fork sets its own delivery rigor per change and legitimately raises direct pull requests on its own `main`. `no-mistakes` puts a local git proxy in front of your real remote. Pushing through it runs an AI-driven review/test/lint pipeline in an isolated worktree, forwards the push upstream only after every check passes, and opens a clean PR automatically. @@ -16,7 +17,8 @@ GitHub Actions and Dependabot are exempt so their automation keeps working, but ## Workflow -1. Fork the repo, then clone the parent repo or set your local `origin` back to the parent (`git@github.com:kunchenguid/firstmate.git`). +1. Fork the repo, then clone your fork so `origin` is the repository you can push to. + Add the upstream repository as a separate read-only remote with `git remote add upstream https://github.com/kunchenguid/firstmate`. 2. Create a branch and make your changes. 3. Initialize the gate with your fork as the push target: `no-mistakes init --fork-url git@github.com:/firstmate.git` (contributing to firstmate requires **no-mistakes v1.46.0+** for structured attestation; without a fork, plain `no-mistakes init` still works for maintainers with push access). 4. Commit your changes. @@ -32,6 +34,30 @@ GitHub Actions and Dependabot are exempt so their automation keeps working, but See the [no-mistakes quick start](https://kunchenguid.github.io/no-mistakes/start-here/quick-start/) for the full first-run walkthrough. +## Fork remotes and upstream sync + +A downstream fork keeps two remotes with different rights, and every contributor and agent in that fork depends on the distinction. +`origin` is the fork you can push to, and it is the only repository that receives branches, pull requests, and merges. +`upstream` is the parent repository, and it is read-only: never push to it, and never open a pull request against it from a fork that is operating independently. +Confirm which one you are about to write to with `git remote get-url origin` before any push or pull-request creation, because the two remotes differ only by owner in the URL. + +Firstmate homes self-update by fast-forwarding from `origin`, which `bin/fm-update.sh` and the [`updatefirstmate`](.agents/skills/updatefirstmate/SKILL.md) skill own. +That is the reason a fork cannot pick up upstream work simply by fetching it: the work has to reach the fork's own default branch first. +Sync the fork deliberately, as its own reviewable change: + +1. `git fetch upstream` and start a branch from the fork's current `main`. +2. `git merge upstream/main` on that branch, producing a merge commit whose first parent is the fork's `main` and whose second parent is the upstream commit being synced. + Merge rather than rebase, so every home can fast-forward onto the result instead of being asked to reconcile rewritten history. +3. Resolve conflicts with evidence rather than preference, and keep a local fix whenever it still covers a case the upstream change does not. +4. Run the repo's gates on the merge result, then push the branch to `origin` and open the pull request on the fork. +5. Land it with the configured merge authority, after which every home fast-forwards to it in the ordinary way. + +After any remote retarget, re-run `no-mistakes init` and inspect the gate mirror's own `git remote -v` before trusting the next pipeline run. +The pipeline rebases and opens pull requests from that mirror's `origin`, not from the checkout you are working in, so a mirror still pointing at the old remote sends work to the wrong repository while every local check still looks correct. + +Treehouse pool worktrees are `git worktree` entries that share the primary checkout's object store and configuration, so they inherit its remotes rather than defining their own. +Correcting the remotes once in the primary checkout is therefore what makes every existing and future pool worktree correct. + ## Repo conventions - This repo is a template for running a firstmate orchestrator agent. diff --git a/docs/documentation-audiences.json b/docs/documentation-audiences.json index 8bb68bd4ba2..c02fb2a66ab 100644 --- a/docs/documentation-audiences.json +++ b/docs/documentation-audiences.json @@ -432,6 +432,10 @@ "path": "docs/verification/runtime-backends.md", "audience": "maintainer-verification" }, + { + "path": "docs/verification/self-update.md", + "audience": "maintainer-verification" + }, { "path": "docs/verification/stow-memory.md", "audience": "maintainer-verification" diff --git a/docs/verification/self-update.md b/docs/verification/self-update.md new file mode 100644 index 00000000000..4e5662e7e39 --- /dev/null +++ b/docs/verification/self-update.md @@ -0,0 +1,53 @@ +# Self-update verification + +Active evidence that `/updatefirstmate` advances a firstmate home from its own `origin`. +The procedure this evidence exercises is owned by [`updatefirstmate`](../../.agents/skills/updatefirstmate/SKILL.md) for the agent path and by [`CONTRIBUTING.md`](../../CONTRIBUTING.md) ("Fork remotes and upstream sync") for the maintainer path. + +## Origin is the only update source + +Verified 2026-09-01 against `cm-maple7/firstmate` with a throwaway home cloned from the fork and deliberately set one commit behind its default branch. + +Setup: + +```sh +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) +``` + +Run: + +```sh +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 advanced by fast-forward to the fork's default branch, and the updater reported the tracked instruction surface as changed so the caller re-reads `AGENTS.md`. +`6aa4beb` is the merge that landed the upstream sync on the fork, so this run also demonstrates the full chain: upstream work reaches a home only after landing on the fork's own default branch. + +Refresh this record by repeating the three commands above whenever the update path or the fork's remote layout changes. + +## Pool worktrees inherit the primary checkout's remotes + +Verified 2026-09-01 on the same fork. +Treehouse pool worktrees are `git worktree` entries sharing the primary checkout's git directory, so they read its remote configuration rather than defining their own. + +```sh +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 +``` + +Correcting the remotes once in the primary checkout is therefore what makes every existing and future pool worktree correct; no per-worktree repair step exists or is needed.