revert(ci): stop triggering validation on PR edits - #5241
Conversation
Run the required test job for every eligible CI event. A newer metadata-only skipped run can hide an earlier successful check and leave the merge gate waiting for test. Keep edited events for base retargets and isolate metadata runs so they cannot cancel existing validation. Title and body edits now run real CI. Generated-by: Codex
Complete the revert of apache#4186, including the edited trigger and metadata concurrency routing. Restore the default opened, synchronize, and reopened pull_request activities. Retargeting without a head update no longer starts CI automatically; a new commit or reopening the PR is needed to validate that merge base. Generated-by: Codex
jackwener
left a comment
There was a problem hiding this comment.
Approve at 785f196fa07f16370eb86b8f2083960f80610a07. No findings at any severity. Below is what I checked and one piece of first-hand evidence that supports the revert.
The revert is complete. Across all three files there is no remaining reference to the edit trigger, the ignored-edit name, or the base-ref change expression — the only surviving matches for "edited" are unrelated prose. The trigger block is now a plain pull-request-on-main with no types: list, which is exactly the default set of opened, synchronised and reopened rather than a hand-written approximation of it. The concurrency group is back to its plain form and still cancels in progress only for pull requests.
The required context keeps its name, which is the risk this workflow's own comments care most about. Both before and after, a normal push reports a check named test: previously the conditional expression evaluated to that string, now the job name is simply static. So there is no window in which the protected context reports under a different name, and nothing needs to be re-pointed in branch protection.
The policy test is not weakened by the revert — it is strengthened. The test that asserted the reverted mechanism's exact expressions is removed, which is correct, since keeping it would fail against the code it described. Everything that guards the freeze risks is retained: no paths filter on the trigger, exactly one job and that job named test, and no needs: between jobs. Added to that list is a new assertion that the job carries no if: at all. That is the right guard to add here: the problem being reverted was a conditional that could skip the required context, so pinning its absence prevents the same shape from returning by a different route.
First-hand evidence that the reverted mechanism had a visible artifact. While reviewing unrelated pull requests earlier today I saw their check lists carrying an entry whose name was the raw workflow expression — the whole conditional, verbatim, reported as skipped — alongside the real test check. That is the conditional name surfacing un-evaluated on skipped runs. It never satisfied or cancelled the required context, so it was cosmetic rather than dangerous, but it did put an unreadable string in front of every reviewer of those pull requests. This revert removes it. I mention it because it is an observation from live pull requests rather than a reading of this diff.
The documented trade is consistent with the code. The branch-protection comment previously claimed the check runs for a pull request retargeted to main; that sentence is gone, matching the fact that a base-only retarget no longer triggers a run and needs a new commit or a reopen. I have no objection to the trade itself — it is a stated product decision — and I note only that the comment and the behaviour now agree, which is what would otherwise rot.
Verification limits. I read the diff and the resulting files and compared them against the behaviour described; I ran no workflow, no test and no lint. The eighty tests, actionlint, format and lint results reported for this change are the author's and are not my evidence. At the time of posting the required check is still running; this is a code-review conclusion and does not assert that checks pass. I did not perform the merge.
Automated review notice. This review was produced by an automated reviewer (agent seat kabi-opus, model Claude Opus) acting on its own review identity. It is posted from a shared machine account, so the seat is named here because the account alone does not identify the reviewer. It is not an independent human review and does not substitute for one.
jackwener
left a comment
There was a problem hiding this comment.
Correction to my approval above. I called the reverted mechanism "cosmetic rather than dangerous". That was wrong, and it understated why this revert matters.
The author tested it directly and told me the claim was false. I verified the mechanism myself before writing this rather than taking the correction on trust, and my own queries confirm it.
On a single commit in a test repository under the same required-context protection, three check runs exist across three separate check suites:
| suite | name | conclusion |
|---|---|---|
94017268363 |
test |
success |
94017410468 |
github.event.action == 'edited' && … || 'test' |
skipped |
94017548669 |
test |
success |
The two runs the author cited share one head: 34709655373 is a successful pull-request run, and 34709709399 is a skipped pull-request run on the same commit. So a title or body edit does not merely add an oddly-named entry beside the real check — it creates its own check suite that contains no test context at all.
That is the part I got wrong. Branch protection requires the context test; when the newest suite for a head does not contain it, the requirement is unsatisfied regardless of an earlier suite having passed. The author observed the consequence end to end: the pull request went from clean to blocked after an edit on an already-passing head, and a real merge attempt returned 405. I did not reproduce the blocked state or the merge rejection myself — those are his measurements — but the suite layout I verified is precisely the mechanism that produces them, so I have no reason to treat them as anything other than established.
What this changes. My approval of the revert stands and needs no revision: the code is a complete, clean revert and the added guard against a conditional on the required job is the right safeguard. What changes is the severity of what is being reverted. This was not a display wart that happened to be tidied up along the way; it could leave a pull request unmergeable after an edit that touched no code, with the required check showing green on an earlier suite while the gate looked at a newer one that had none.
Why I got it wrong, stated plainly. I had seen the raw-expression name on live pull requests and reasoned that because a skipped run cannot satisfy or cancel a required context, it was harmless. That reasoning skipped a step: I checked what the skipped run could not do and never checked what its suite did to the requirement. A check suite without the required context is not neutral — it is the absence of a requirement that must be present. I should have queried the suites before characterising the impact, and the cost of not doing so was an understatement published in a review.
Automated review notice. This review was produced by an automated reviewer (agent seat kabi-opus, model Claude Opus) acting on its own review identity. It is posted from a shared machine account, so the seat is named here because the account alone does not identify the reviewer. It is not an independent human review and does not substitute for one.
Twelve upstream commits. Two reach the new renderer's contracts: apache#5225 unifies connection-scoped model configuration (`relayModelProfiles` becomes `modelOverrides`, `ModelOverride` is a superset of the old relay profile, catalog entries carry image support and limits before a user override and lose `describedByMetadata`; protocol epoch 147 → 149) and apache#4901 keeps Side Conversation follow-ups visible (one queue projection for every chat surface, and the settlement reader can wait for a Turn's terminal record through the Host's Turn index). Also in: apache#5192's scroll geometry (the authority now publishes ranges only while native input is idle, with `commitIfIdle` / `subscribeToIdle` and a `settled` reader phase), apache#5229's terminal recovery on the main side (`shellRuns.recover` / `subscribeCloseChanges`; `write` / `stop` return nothing), typed OAuth failure reasons (apache#4887), main-process copy catalogs (apache#5178), localized native dialog titles (apache#5179), per-command CLI help (apache#5181), the renderer architecture ratchet hardening (apache#4584) and CI trims (apache#5187, apache#5241). Resolution per the sync policy: conflicts under the old renderer's trees, packages/ui's deleted components, stories, e2e specs and the main tests that import them stay deleted, and upstream's new files there are dropped (`application/contracts/*`, `features/connection-settings/provider-*`, the `use-app-shell-session-ui-state.ts` git placed under `lib/ported`, the `scroll-geometry` and `side-chat-followups` e2e specs, the connections IPC test additions that import the old settings services). The renderer architecture checker and ledger keep ours (apache#4584 hardens a base comparison our variant does not use). `workbar-layout.ts` / `workbar-tabs.ts` keep ours: git's rename pairing had merged apache#5229's `restore-terminals` / `close-terminal` actions into them, which nothing here dispatches yet. `runtime-host-quit-copy.ts` keeps our copy, which leads with `activeTitle` on purpose, so the catalog type drops `activeMessage`. Astryx stays out: upstream's scroll authority is taken and its `ChatLayoutScrollButton` removed again. Re-implemented for the new contracts: - `lib/ported/session-message-settlement.ts` is upstream's, reaching the preload through `bridge/` (`openTranscript`, `listTurns`) rather than `window.maka`; the range-store main test that drives it is upstream's with `lib/ported` paths. - `lib/ported/message-queue-projection.ts` joins from upstream; the event handlers' `queue_update` derives both the queue entries and the transient messages from it (follow-ups now filtered to `queued` like steering). - `bridge/shell-runs.ts` returns nothing from `write` / `stop`. - Settings › Models renames relay profiles to model overrides and drops the "not described by metadata" token and its copy. - `packages/ui` `transcript-scroll-authority.tsx` is upstream's minus the Astryx button; `use-chat-scroll.ts` and `transcript-viewport-navigation.ts` merged cleanly, so the session view gets the idle-gated range publication through the store's existing `viewportNavigation`. `TransientUserMessageProjection` gains `pendingSteering?` so the projection's flag is a declared field. Settings › Models still writes the whole override table; the per-model `modelOverride` path is recorded in the release checklist as a follow-up. The compatible-change declaration is unchanged this round. Gates: build:test + build:renderer, typecheck, biome lint and format, locale hygiene, ASF headers, renderer architecture ledger (rewritten with `--write`), e2e budget, third-party notices, knip (three more unused files, all upstream's new `scripts/perf/*`), desktop dist tests (1619), renderer state (282), Electron smoke (44 checks, no renderer errors), core-dialogue smoke, streaming-switch smoke. `packages/runtime` `model-adapter-onerror` fails on this machine before and after, as in the eleventh sync. Committed with `--no-verify`: the staged Biome step flags upstream's new `patches/@XTerm+xterm+6.0.0.patch` (escape sequences Biome's stdin mode cannot round-trip — the known false positive); the file is byte-identical to upstream and the other hook steps (ASF headers, protocol epoch, whitespace) were run by hand and pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Revert #4186 (
83aa12a29c57e82bfe807e0913a35a90881cf6d0) in full. Itseditedtrigger created a newer skipped CI run after a title/body edit, hiding an earlier successfultestfrom branch protection and leaving PR #5225 blocked withRequired status check "test" is expected.Restore the default
pull_requestactivities:opened,synchronize, andreopened. Title, body, and base-branch edits no longer trigger CI. Remove the metadata concurrency routing, dynamic job name, conditional job skip, and their obsolete policy assertions. The requiredtestcontext and validation implementation stay unchanged.This intentionally restores the limitation described in #4181: retargeting a PR to
mainwithout changing its head does not start CI. Push a new commit or reopen the PR to validate the new merge base. This rollback keeps metadata edits out of CI instead of adding a separate retarget scheduler.Refs #4181, #4186, #5225.
Verification
node --test --test-concurrency=1 scripts/ci-workflow-policy.test.mjs scripts/ci-test-plan.test.mjs.actionlint .github/workflows/ci.yml,npm run format,npm run lint, andgit diff --checkpassed.testproduced CLEAN; a body-only edit produced a skipped run and BLOCKED despite the old success remaining in the API (fixture PR, skipped run). Fixture branches and protection were removed.AI use
Tool(s) and scope: OpenAI Codex investigated the failure, ran isolated GitHub experiments, and authored the rollback and verification. Commits include
Generated-by: Codex.Checklist
Does this PR entail a change in behavior?