feat: sync Firstmate landings to the configured fork - #33
Merged
Merged
Conversation
An approved firstmate landing fast-forwarded only local main, so the fork fell behind what the home runs, remote second mates synced stale code, and landed PRs still showed open. After the local fast-forward, firstmate's own repository now pushes local main to origin's main as a plain fast-forward once fm-landing-remote verify passes, reads the branch back, and proves a recorded PR merged with the shared forge read. A non-fast-forward, a failed push, or an unproved read-back keeps the local landing, names why and the command to finish, and exits 3. Project landings are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Captain's intent
"wait why the fuck our local setup isnt upload to the fucking gothub ?? whatelse is the fucning point of having github at all ?our own work needs to fucking go there like it is our fucking fork for crist sake isnt that the entire point ??? can you please after that get it all sync eoth our fork and why dont we already sne doit general fizes back to the original project (i thought we did that already) and make it routine hes every week sure we do the updates then we sync it all eith our fork so we keep our setups and all our unique changes are both sent to the og and to our own fucking fork which exists exactt for this fucking reason"
Context, 4 Oct 2026: approved firstmate work lands on this home's local main through bin/fm-merge-local.sh, and the PR on our fork https://github.com/BohnBawerick/firstmate stays open by design, so nothing pushed local main to GitHub. By 4 Oct our fork's main was 24 commits behind the local main this home runs. That broke the remote second mates' sync, because they fetch from the fork, and it left PRs that had really landed showing as open. Firstmate pushed by hand twice that day as clean fast-forwards (71bd89c..1726136, then ..44647b7), and GitHub then showed those PRs as merged. The weekly upstream merge and sending general fixes to the original project are a separate routine task; this task is only about our own fork.
Firstmate spec
Load the
firstmate-coding-guidelinesskill before editing anything.Make every approved firstmate landing also update our fork, so GitHub always holds what this home runs:
bin/fm-merge-local.shfast-forwards local main for this home's own firstmate repository, push local main to the landing remote's main (origin, perbin/fm-landing-remote.sh) as a plain fast-forward. Never force, never create a merge commit, and never push anywhere else.bin/fm-pr-merge.shproves a merge.Out of scope: the weekly upstream merge, upstream PRs, and changes to project landings.
What Changed
Risk Assessment
✅ Low: Captain, the change is bounded to Firstmate self-repository landings, validates the fork destination before pushing, preserves local landings on synchronization failure, and adds behavior-level regression coverage.
Testing
Ran the focused fm-merge-local behavior suite, then drove eight isolated live scenarios through the real landing and PR-merge scripts using disposable Git remotes. The happy path also performed a normal read-only GitHub GraphQL check of merged PR 32. All scenarios passed, the fixtures were removed, and the worktree is clean. These are CLI changes with no rendered UI, so the reviewer evidence is a CLI transcript.
Evidence: Live fork-sync product transcript
Source: Live fork-sync product transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 4 issues found → auto-fixed (3) ✅
bin/fm-merge-local.sh:205- The new push does not prove its destination.fm-landing-remote.sh verifysucceeds for any single-remote clone and checks onlyorigin's fetch URL, whilegit push originuses any configuredremote.origin.pushurl. A clone whose soleoriginis the parent, or a correctly remapped clone with a stale parentpushurl, reaches line 229 and can push to the parent despite the explicit "never push anywhere else" requirement. The reads at lines 209 and 233 still inspect the fetch URL, so the latter case can mutate the parent before failing read-back. Require a proven remapped origin and verify every push URL before pushing. The positive fixture at tests/fm-merge-local.test.sh:510 uses the unproved single-origin shape, while its refusal case at line 690 covers only a leftoverforkremote.bin/fm-merge-local.sh:199- The explicit fallback for a missingorigincontradicts requirements 1 and 3: every approved Firstmate landing must update the fork, and an update failure must report that the fork was not updated and exit non-zero. A self-repository task can fast-forward local main, then lines 199-201 skip both fork sync and PR read-back while returning success. Remove this unrequired no-fork success mode and route it through the existing non-zerofork_not_syncedpath.bin/fm-merge-local.sh:261- The GitLab read-back branch adds a second provider contract that the stated GitHub-fork requirement does not need. Narrow this path to the required GitHub PR form and reject other providers instead of maintaining an unused GitLab mode.bin/fm-merge-local.sh:252-FM_MERGE_LOCAL_READBACK_DELAYintroduces a new operator-configurable option, validation rule, and documented contract, although the intent only requires bounded read-back confirmation. Remove the option and keep the delay internal; tests can replacesleepthrough their executable test environment if they need zero-delay retries.🔧 Fix applied.
6 issues (4 errors, 2 warnings) still open:
bin/fm-merge-local.sh:205- The new push does not prove its destination.fm-landing-remote.sh verifysucceeds for any single-remote clone and checks onlyorigin's fetch URL, whilegit push originuses any configuredremote.origin.pushurl. A clone whose soleoriginis the parent, or a correctly remapped clone with a stale parentpushurl, reaches line 229 and can push to the parent despite the explicit "never push anywhere else" requirement. The reads at lines 209 and 233 still inspect the fetch URL, so the latter case can mutate the parent before failing read-back. Require a proven remapped origin and verify every push URL before pushing. The positive fixture at tests/fm-merge-local.test.sh:510 uses the unproved single-origin shape, while its refusal case at line 690 covers only a leftoverforkremote.bin/fm-merge-local.sh:199- The explicit fallback for a missingorigincontradicts requirements 1 and 3: every approved Firstmate landing must update the fork, and an update failure must report that the fork was not updated and exit non-zero. A self-repository task can fast-forward local main, then lines 199-201 skip both fork sync and PR read-back while returning success. Remove this unrequired no-fork success mode and route it through the existing non-zerofork_not_syncedpath.bin/fm-merge-local.sh:261- The GitLab read-back branch adds a second provider contract that the stated GitHub-fork requirement does not need. Narrow this path to the required GitHub PR form and reject other providers instead of maintaining an unused GitLab mode.bin/fm-merge-local.sh:252-FM_MERGE_LOCAL_READBACK_DELAYintroduces a new operator-configurable option, validation rule, and documented contract, although the intent only requires bounded read-back confirmation. Remove the option and keep the delay internal; tests can replacesleepthrough their executable test environment if they need zero-delay retries.bin/fm-merge-local.sh:237- The new fork synchronization breaks the supported self-repository path at bin/fm-pr-merge.sh:1499. That path merges the GitHub PR first, then calls this script. With GitHub's default squash merge, origin/main contains a new squash commit while the local task branch contains the original commits. Lines 231-240 fast-forward local main to the task branch and then reject origin/main as divergent, leaving local and remote main split after the irreversible forge merge. The existing test misses this because its fixture has no upstream, so the fix-round fallback at line 199 exits successfully without syncing. The prior fix round left this sibling caller behind. The smallest containment is to refuse self-repository use in fm-pr-merge.sh before the forge mutation and direct callers to fm-merge-local.sh. Preserving the existing entry point requires a new reconciliation policy, so that product choice needs authorization.bin/fm-merge-local.sh:199- The prior fix round tests whethergit remote get-url upstreamsucceeds instead of whether the upstream remote exists. A remapped checkout whoseremote.upstream.urlwas removed but whose remote section remains, for example throughremote.upstream.fetch, reaches this branch after the local landing, reports that no fork is configured, and exits 0 without syncing. This is the malformed-remap sibling that the missing-origin case did not cover. Check for the remote name separately, then route an unreadable upstream URL throughfork_not_synced.🔧 Fix applied.
7 issues (5 errors, 2 warnings) still open:
bin/fm-merge-local.sh:205- The new push does not prove its destination.fm-landing-remote.sh verifysucceeds for any single-remote clone and checks onlyorigin's fetch URL, whilegit push originuses any configuredremote.origin.pushurl. A clone whose soleoriginis the parent, or a correctly remapped clone with a stale parentpushurl, reaches line 229 and can push to the parent despite the explicit "never push anywhere else" requirement. The reads at lines 209 and 233 still inspect the fetch URL, so the latter case can mutate the parent before failing read-back. Require a proven remapped origin and verify every push URL before pushing. The positive fixture at tests/fm-merge-local.test.sh:510 uses the unproved single-origin shape, while its refusal case at line 690 covers only a leftoverforkremote.bin/fm-merge-local.sh:199- The explicit fallback for a missingorigincontradicts requirements 1 and 3: every approved Firstmate landing must update the fork, and an update failure must report that the fork was not updated and exit non-zero. A self-repository task can fast-forward local main, then lines 199-201 skip both fork sync and PR read-back while returning success. Remove this unrequired no-fork success mode and route it through the existing non-zerofork_not_syncedpath.bin/fm-merge-local.sh:261- The GitLab read-back branch adds a second provider contract that the stated GitHub-fork requirement does not need. Narrow this path to the required GitHub PR form and reject other providers instead of maintaining an unused GitLab mode.bin/fm-merge-local.sh:252-FM_MERGE_LOCAL_READBACK_DELAYintroduces a new operator-configurable option, validation rule, and documented contract, although the intent only requires bounded read-back confirmation. Remove the option and keep the delay internal; tests can replacesleepthrough their executable test environment if they need zero-delay retries.bin/fm-merge-local.sh:237- The new fork synchronization breaks the supported self-repository path at bin/fm-pr-merge.sh:1499. That path merges the GitHub PR first, then calls this script. With GitHub's default squash merge, origin/main contains a new squash commit while the local task branch contains the original commits. Lines 231-240 fast-forward local main to the task branch and then reject origin/main as divergent, leaving local and remote main split after the irreversible forge merge. The existing test misses this because its fixture has no upstream, so the fix-round fallback at line 199 exits successfully without syncing. The prior fix round left this sibling caller behind. The smallest containment is to refuse self-repository use in fm-pr-merge.sh before the forge mutation and direct callers to fm-merge-local.sh. Preserving the existing entry point requires a new reconciliation policy, so that product choice needs authorization.bin/fm-merge-local.sh:199- The prior fix round tests whethergit remote get-url upstreamsucceeds instead of whether the upstream remote exists. A remapped checkout whoseremote.upstream.urlwas removed but whose remote section remains, for example throughremote.upstream.fetch, reaches this branch after the local landing, reports that no fork is configured, and exits 0 without syncing. This is the malformed-remap sibling that the missing-origin case did not cover. Check for the remote name separately, then route an unreadable upstream URL throughfork_not_synced.bin/fm-merge-local.sh:261- After the task control lock is released at line 170, this rereadspr=from mutable task metadata. Ordinary landed-work teardown uses the same control lock and may then remove the record, so a slow fork push can reach line 262 with an emptyPR_URLand exit 0 without proving the original recorded GitHub PR merged. ID reuse can instead make it inspect another incarnation's PR. Round 2 left this lifecycle sibling behind. Snapshot the validatedpr=for the expected spawn generation before releasing the control lock, then use that snapshot for read-back.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
tests/fm-merge-local.test.shRead-onlygh pr list -R BohnBawerick/firstmate --state merged --limit 3 --json number,url,mergedAtDisposable live fixtures invokingbin/fm-merge-local.shagainst real local bare Git remotes for successful push, divergence, remote rejection, mismatched push URL, no-upstream, non-GitHub PR, and task-record reuse casesNormal authenticatedgh api graphqlread throughbin/fm-merge-local.shfor merged PRhttps://github.com/BohnBawerick/firstmate/pull/32Disposable live fixture invokingbin/fm-pr-merge.shwith a call-recordingghexecutable to prove refusal occurs before forge accessVerified fixture teardown, evidence creation, and worktree cleanliness withgit status --short✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.