Skip to content

fix(actions): reuse overlapping Lopu controller repair PRs - #591

Merged
lopugit merged 2 commits into
github-actionsfrom
codex/lopu-pr-dedup
Sep 3, 2026
Merged

fix(actions): reuse overlapping Lopu controller repair PRs#591
lopugit merged 2 commits into
github-actionsfrom
codex/lopu-pr-dedup

Conversation

@lopugit

@lopugit lopugit commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Diagnosis

The controller-repair publisher only checked for an open PR whose head matched the branch it had just generated:

lopu/workflow-check-fix-${GITHUB_RUN_ID}

Because every workflow run has a different ID, that lookup could never find a repair PR from an earlier run. The generic title then made distinct repairs look identical, while actual duplicate/overlapping repairs accumulated against github-actions.

The live backlog confirmed both failure modes: most similarly titled PRs contained distinct controller fixes, but #575 and #584 addressed the same two stale contract guards. The latter had stronger scoped regression coverage and superseded the former.

Fix

  • Inventory all open controller-repair PRs targeting github-actions before creating a branch.
  • Restrict reuse to same-repository repair heads owned by the repository owner or GitHub Actions bot and carrying the managed branch/title/body identity.
  • Compare complete changed-file inventories and prefer an existing PR that touches any of the same protected files.
  • Merge the current controller base into that repair head, apply the new patch with Git's three-way machinery, and update it only when the combination is clean.
  • Fence the push with the exact inspected head SHA via --force-with-lease.
  • Fail closed when GitHub cannot provide the deduplication inventory or the candidate moves, rather than opening another potentially duplicate PR.
  • Fall back to a separate repair only when an overlapping PR cannot absorb the patch safely.
  • Give genuinely new repair PRs a diagnosis-derived title instead of the indistinguishable generic title.
  • Record each aggregated repair report in the existing PR conversation.

Regression coverage

The routing contract now pins the full behavior: repository-wide repair inventory, trusted ownership, complete file comparison, clean three-way composition, exact-head lease, explicit reused outcome, fail-closed read behavior, and diagnosis-derived new titles.

Validation

  • Entire control-plane-ci.yml verify command set: pass.
  • Entire contract-advisory command set: pass.
  • Graphify CAS: 22/22 pass.
  • Rebase integrity/related-edits/canonical-instruction tests: 6/6 pass.
  • Lopu PR status tests: 7/7 pass.
  • Routing step-window tests: 10/10 pass.
  • resolve-pr-conflicts-routing-contract.mjs --self-test: pass.
  • workflow-control-plane-contract.mjs --self-test: pass.
  • promotion-worker-routing-contract.mjs: pass.
  • Workflow YAML parses and the extracted repair-publisher run: block passes bash -n.
  • git diff --check: clean.
  • Graphify refreshed after the code change; the bounded portable snapshot was staged through the protected stager.

Existing backlog cleanup

Merged reviewed repairs #565, #573, #574, #577, #579, #580, #584, and #588 into github-actions. Closed #575 as superseded by #584.

@github-actions github-actions Bot added the lopu: mergeable The PR branches can currently be merged without conflicts label Sep 3, 2026
@lopugit
lopugit merged commit 3d18a87 into github-actions Sep 3, 2026
79 checks passed
@github-actions github-actions Bot removed the lopu: mergeable The PR branches can currently be merged without conflicts label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🔧 Lopu — the new title line can strand a pushed repair branch with no PR

Reviewed 79756421 against github-actions 22bc5a0f. The dedup design is
sound and I verified the security-relevant parts empirically rather than by
reading (details below). One real defect, found and fixed in this branch.

The defect

This PR introduces:

report_title="$(sed -n 's/^# //p' "$report" | head -n 1 | tr -d '\r')"

under set -euo pipefail. head -n 1 exits after one line, so once the
diagnosis report carries more level-1 headings than the pipe buffer holds, sed
takes SIGPIPE and exits 141 — pipefail propagates it and set -e aborts the
step.

What makes it more than cosmetic is where it aborts:

git … push  "HEAD:refs/heads/$branch"   # ← branch is now on the remote
unset basic PUSH_TOKEN
report_title="$(sed … | head -n 1 …)"   # ← abort lands here
url="$(gh pr create …)"                 # ← never runs

The outcome is a pushed lopu/workflow-check-fix-<run-id> branch carrying the
repair commit, no PR to publish it, and a red step. And it is invisible to
this PR's own dedup logic on the next pass, because that inventories open PRs,
not branches — so the repair is simply lost.

Reproduced with the same set -euo pipefail and a 308 KB report of #
headings:

step: ... branch pushed ...
==> step exit=141   (branch pushed, PR never created)

The fix

One sed, no pipeline — q on the first match is what head -n 1 was there for:

report_title="$(sed -n '/^# /{s/^# //;s/\r//g;p;q;}' "$report")"

Same output, no pipeline to take a signal, and strictly less work. Same input:

title=[Lopu controller fix — finding 0]
==> step exit=0

I also updated this PR's own contract to pin the fixed form and added a guard so
the pipeline cannot come back:

assert.doesNotMatch(
  reviewBlock,
  /report_title=[^\n]*\|[^\n]*head -n 1/u,
  "the diagnosis heading is read without a pipeline — a SIGPIPE there aborts the step
   under pipefail, after the repair branch is pushed and before its PR is created",
);

Both assertions are load-bearing: mutating the workflow back to the piped form
fails the contract, restoring it passes.

What I verified and did not change

I drove the actual jq candidate filter with a hand-built PR list rather than
reading it:

candidate outcome
owner, lopu/workflow-check-fix-<n>, known title kept
owner, non-run-id branch name dropped
isCrossRepository: true dropped
github-actions[bot], matched only by the body marker kept
unknown author, otherwise perfect dropped

The two that matter — a fork head and an unknown author — are both refused, so
the --force-with-lease push can only ever target a same-repo branch this
repo's owner or its bot created. The lease is fenced to the inspected
headRefOid and the fetch re-checks that SHA first, so a head that moved during
dedup is preserved. Both failure reads exit 0 with outcome=dedupe-check-failed
rather than opening a possible duplicate, and url=/outcome=none are
pre-seeded so every early exit leaves well-formed outputs downstream.

Two observations I left alone:

  • The loop breaks on the first overlapping candidate. If that one then fails
    the merge or the 3-way apply, reuse is abandoned entirely rather than trying
    the next overlapping PR. A defensible bound on per-run work, not a defect —
    but worth knowing when reading a a separate repair PR is required warning.
  • outcome=dedupe-check-failed drops the repair for that pass and the diagnosis
    is not preserved anywhere, so the next pass re-derives it from scratch.

Validation

routing-contract --self-test OK · mutation test fails as intended ·
node --test resolve-pr-conflicts-routing-contract.test.mjs 10/10 ·
workflow-control-plane-contract --self-test OK · node --check over every
.github/scripts/*.mjs · workflow YAML parses · bash -n on the extracted
workflow_fix step · live merge into github-actions clean.

Checks at this head were fully green (17 pass / 23 skipping) and the CodeQL
snapshot is empty — independently confirmed 0 open alerts, nothing dismissed.

— Lopu 🌸

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Lopu repository review

Lopu reviewed this PR against github-actions as Thingtime's principal PR and repository manager. Using Claude Opus 5.

Lopu made justified improvements and pushed ef71202 to codex/lopu-pr-dedup.

Lopu review — #591 · fix(actions): reuse overlapping Lopu controller repair PRs

Compared codex/lopu-pr-dedup 79756421 against github-actions 22bc5a0f
(the live base — GitHub's cached test-merge for this PR is current).

What I compared

7 files, +1120 / −441. Excluding the Graphify snapshot refresh, the change is
two files: the workflow_fix step of .github/workflows/resolve-pr-conflicts.yml
and 8 new assertions in .github/scripts/resolve-pr-conflicts-routing-contract.mjs.

I read the whole step rather than the diff hunks, because the change moves the
branch creation from before the commit to after the dedup decision — the
ordering is the substance.

Verdict

The dedup design is sound and the security-relevant parts are right. One real
defect found and fixed
(below). Checks were fully green at this head
(17 pass / 23 skipping, no failure, cancellation or timeout to diagnose), and the
trusted CodeQL snapshot is empty — independently confirmed against
code-scanning/alerts?pr=591, which also returns 0 open. Nothing dismissed.

Defect fixed — an orphaned repair branch with no PR

report_title="$(sed -n 's/^# //p' "$report" | head -n 1 | tr -d '\r')" (new in
this PR) is a pipeline under set -euo pipefail.

head -n 1 exits after the first line. Once the diagnosis report contains more
level-1 headings than the pipe buffer holds, sed gets SIGPIPE and exits 141;
pipefail propagates it; set -e aborts the step.

The damage is in where it aborts. Reading the step top to bottom:

git … push  "HEAD:refs/heads/$branch"     # ← branch is now on the remote
unset basic PUSH_TOKEN
report_title="$(sed … | head -n 1 …)"     # ← abort lands here
url="$(gh pr create …)"                   # ← never runs

So the failure mode is a pushed lopu/workflow-check-fix-<run-id> branch
carrying the repair commit, no PR to publish it, a red step, and — because
the branch exists but no PR does — a repair this very PR's new dedup logic
cannot find either (it inventories open PRs, not branches).

Reproduced exactly, with the same set -euo pipefail and a report of 20,000
# headings (308 KB):

step: ... branch pushed ...
==> step exit=141   (branch pushed, PR never created)

Fix (in this PR's worktree): do it in one sed, no pipeline —
sed -n '/^# /{s/^# //;s/\r//g;p;q;}' "$report". Same output (first #
heading, CRs stripped), and q on the first match is what head -n 1 was
there for, so it is also strictly less work.

title=[Lopu controller fix — finding 0]
step: gh pr create OK
==> step exit=0

I also updated this PR's own contract assertion to pin the fixed form, and
added a doesNotMatch guard so the pipeline cannot come back:

assert.doesNotMatch(
  reviewBlock,
  /report_title=[^\n]*\|[^\n]*head -n 1/u,
  "the diagnosis heading is read without a pipeline — a SIGPIPE there aborts the step
   under pipefail, after the repair branch is pushed and before its PR is created",
);

Both assertions are load-bearing: mutating the workflow back to the piped form
fails the contract (exit 1), and restoring it passes.

What I checked and did not change

The candidate filter is correctly fail-closed. I drove the actual jq
program with a hand-built PR list rather than reading it:

candidate outcome
owner, lopu/workflow-check-fix-<n>, known title kept
owner, but a non-run-id branch name dropped
isCrossRepository: true dropped
github-actions[bot], matched only by the body marker kept
unknown author (mallory), otherwise perfect dropped

The two that matter — a fork head and an unknown author — are both refused, so
the --force-with-lease push below can only ever target a same-repository
branch this repo's owner or its bot created.

Also verified: the lease is fenced to the inspected head (headRefOid), and
the fetch re-checks that SHA before anything is written, so a head that moved
during dedup is preserved rather than overwritten. Both failure reads
(gh pr list, pulls/N/files) exit 0 with outcome=dedupe-check-failed rather
than opening a possibly-duplicate PR. url=/outcome=none are pre-seeded at the
top of the step, so every early exit leaves a well-formed output for the
downstream "Publish Lopu's PR review updates" step.

Two observations I deliberately left alone:

  • The loop breaks on the first overlapping candidate (lowest PR number). If
    that one then fails the merge or the 3-way apply, reuse is abandoned entirely
    rather than trying the next overlapping PR. That is a defensible bound on
    work per run, not a defect — worth knowing when reading a
    a separate repair PR is required warning.
  • outcome=dedupe-check-failed drops the repair for that pass entirely. That is
    the intended fail-closed behaviour and it is announced as a ::warning::, but
    the diagnosis itself is not preserved anywhere, so the next pass has to
    re-derive it.

Validation

check result
resolve-pr-conflicts-routing-contract.mjs --self-test OK
mutation test: revert the yml line → contract must fail fails as intended (exit 1)
node --test resolve-pr-conflicts-routing-contract.test.mjs 10 pass / 0 fail
workflow-control-plane-contract.mjs --self-test OK
node --check over every .github/scripts/*.mjs pass
workflow YAML parses; bash -n on the extracted workflow_fix step pass
SIGPIPE reproduction, before vs after 141 → 0
live merge into github-actions at 22bc5a0f clean

View Lopu workflow run

@github-actions github-actions Bot mentioned this pull request Sep 8, 2026
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