Skip to content

fix(bin): refuse merges without verified green checks (land of upstream #1614) - #48

Merged
sbracewell64 merged 1 commit into
mainfrom
fm/merge-path-verifies-no-ci-green
Aug 6, 2026
Merged

sbracewell64 merged 1 commit into
mainfrom
fm/merge-path-verifies-no-ci-green

Conversation

@sbracewell64

Copy link
Copy Markdown
Owner

Lands upstream PR kunchenguid#1614 onto this fork's trunk.

Provenance

Upstream PR kunchenguid/firstmate#1614 — fix(bin): refuse merges without verified green checks
Exact commit ported e3e4b470fd8eff73ce581160dc94b223032eaad3
Its base upstream c8edff36b8466ea0fe547d3abf4b8aa330489986
Landed onto 3611e49 (sbracewell64/firstmate main)

Nothing is redesigned and nothing re-reviewed. The guard, its refusal wording, the --allow-unverified override, the merge_verification=/merge_verified_head= metadata keys, and the crew-state mapping changes are upstream's as written.

Why it did not apply cleanly

The two trunks diverged at upstream kunchenguid#1495. Every resolution below is fork-versus-upstream divergence, not a change to what kunchenguid#1614 does.

  • bin/fm-pr-merge.sh — this fork resolves a task's identity through either a live meta or the durable landing record a released task keeps; upstream has no equivalent. The record resolution stays, and verification is placed between it and the recording step, so upstream's property holds exactly: a head the guard refuses leaves no pr= recorded and no merge poll armed. META is bound to whichever record the task actually has, so the verification metadata write is unchanged.
  • tests/fm-pr-merge.test.sh — upstream's file is the base, with this fork's six released-task cases and fixtures re-added; their gh mocks now answer the verification read as well as the forge-view read.
  • test_missing_meta_refuses_before_merge — exists on both trunks asserting opposite behavior. Upstream refuses a task with no record before any forge lookup; this fork deliberately rebuilds that record from the pull request itself. The fork's version is kept, because that reconstruction is this trunk's behavior.
  • AGENTS.md, docs/architecture.md, docs/scripts.md — upstream's sentences folded into the fork's own text for the landing record, the task base references, and the merge poll's conflict reporting.

Verification on this fork

  • bin/fm-lint.sh clean; bin/fm-doc-audience-check.sh ok.
  • tests/fm-pr-merge.test.sh 34 pass, tests/fm-crew-state.test.sh 54 pass, tests/fm-pr-check-security.test.sh 41 pass.
  • Checked once against the live forge with the merge command mocked, on a pull request with three genuinely failing check runs: the pre-change path issued pr merge ... --squash and exited 0; this one refused, naming the head and the failing count, before arming anything.

Expected red on this fork: PR must be raised via no-mistakes (attestation gate, direct-PR delivery per the captain's authorization of 2026-08-04) and Behavior portable serial 2 (fails on the fork trunk itself, inherited rather than introduced). No attestation marker is written or implied.

Do not merge without the captain's word.

kunchenguid#1614)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
@sbracewell64
sbracewell64 merged commit ed376cf into main Aug 6, 2026
12 of 14 checks passed
sbracewell64 added a commit that referenced this pull request Aug 9, 2026
kunchenguid#1614) (#48)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
sbracewell64 added a commit that referenced this pull request Aug 9, 2026
Squash-reconcile of the fork trunk (48 commits through ed376cf, fork PRs
#8-#48 plus CI mirrors) rebased onto upstream main at 74230fc, resolving
72 conflicted files. The fork trunk carries: the fm-launch-lib.sh
one-owner launch refactor, the slot-base/contribution-target task base
contract (fm-task-base-lib.sh), the zero-budget model registry, fleet
admission control stages 0/1, the wake-outcome ledger, the LoopSpec
canonical representation, the research-approved-work corpus scanner, the
70% compaction doctrine, from-firstmate steer markers, the fleet launcher
menu and Windows-to-WSL bridge, forge-verified merge gates, and the
fleet-view per-argument cap fix.

Conflict-resolution decisions a reviewer cannot see from the diff alone:

- Where the fork carried an older in-flight import of upstream work
  (delivery contracts kunchenguid#1563, remote secondmate homes kunchenguid#1576, trace context
  kunchenguid#995, and every add/add remote-secondmate file), upstream's landed and
  further-evolved version wins outright; nothing fork-specific lived in
  those copies (verified blob-by-blob against upstream history).
- Upstream's --relaunch lifecycle control plane and the fork's task base
  contract both survive: base derivation and the brief base-contract
  guard are gated to fresh spawns (RELAUNCH=0) because a relaunch reuses
  the recorded worktree and bases, and the trunk may have moved since.
- Launch commands stay one-owner in bin/fm-launch-lib.sh: upstream's Muse
  Code adapter is ported into the library (template arm, model flag,
  effort mapping with max->ultra) instead of resurrecting the inline
  fm-spawn template, and fm-spawn's post-template FM_PI_HARNESS prefix is
  dropped because the library's pi templates carry the marker themselves.
- Herdr presentation spaces keep upstream's 0.8.0-floor semantics
  (config off/on/empty, two-argument fm_backend_herdr_presentation_enabled)
  across spawn, config inheritance, docs, and tests.
- fm-pr-check derives its per-task lock from state/<id>.meta explicitly
  so upstream's kunchenguid#1568 meta locking coexists with the fork's #34 landing
  records (fm_meta_lock_path rejects a .landing path).
- tests/lib.sh keeps both the fork's identity-verified background-process
  reaper and upstream's fm_fake_version_tool fixture.
- Merged prose follows upstream's X-mode -> Relay rename.

Pre-existing on both parents, not introduced here:
tests/fm-calm-pi-extension.test.sh fails under system Node 22
(ERR_UNKNOWN_FILE_EXTENSION), verified identical on the base commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sbracewell64 added a commit that referenced this pull request Aug 9, 2026
kunchenguid#1614) (#48)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
sbracewell64 added a commit that referenced this pull request Aug 10, 2026
kunchenguid#1614) (#48)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
sbracewell64 added a commit that referenced this pull request Aug 11, 2026
kunchenguid#1614) (#48)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
sbracewell64 added a commit that referenced this pull request Aug 11, 2026
kunchenguid#1614) (#48)

Ports upstream PR kunchenguid#1614 onto this fork's trunk. Nothing here is redesigned:
the guard, its refusal wording, the --allow-unverified override, the
merge_verification=/merge_verified_head= metadata keys, and the crew-state
mapping changes are upstream's as written.

Provenance:
  upstream PR   kunchenguid#1614
  head commit   e3e4b47
  its base      c8edff3
  landed onto   3611e49 (sbracewell64/firstmate main)

The two trunks diverged at upstream kunchenguid#1495, so the diff did not apply cleanly.
Resolutions, all of them fork-versus-upstream divergence rather than changes
to what kunchenguid#1614 does:

bin/fm-pr-merge.sh - this fork resolves a task's identity through either a live
meta or the durable landing record a released task keeps, which upstream has
no equivalent of. The record resolution stays, and verification is placed
between it and the recording step, so upstream's property still holds exactly:
a head the guard refuses leaves no pr= recorded and no merge poll armed. META
is bound to whichever record the task actually has, so the verification
metadata write addresses it unchanged.

tests/fm-pr-merge.test.sh - upstream's file is the base, with this fork's six
released-task cases and their fixtures re-added. Their gh mocks now answer the
verification read as well as the forge-view read.

test_missing_meta_refuses_before_merge exists on both trunks asserting opposite
behavior: upstream refuses a task with no record before any forge lookup, while
this fork deliberately rebuilds that record from the pull request itself. The
fork's version of the case is kept, because that reconstruction is this
trunk's behavior.

AGENTS.md, docs/architecture.md, docs/scripts.md - upstream's sentences folded
into the fork's own text for the landing record, the task base references, and
the merge poll's conflict reporting.

Verified on this fork: fm-lint.sh clean, fm-doc-audience-check.sh ok, and
tests/fm-pr-merge.test.sh (34), tests/fm-crew-state.test.sh (54) and
tests/fm-pr-check-security.test.sh (41) all pass. Checked once against the live
forge with the merge command mocked: the pre-change path issued a squash merge
for a pull request with three failing check runs, and this one refused it,
naming the head and the failing count, before arming anything.
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