feat(gitea-merge-queue#131 PR-A): is_runtime_bump_exempt + dup-close + wire #2891
Reference in New Issue
Block a user
Delete Branch "fix/131-runtime-bump-exemption"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements RFC internal#131 PR-A (auto-bump sweeper exemption) per CTO spec
9c2e9c88(CEO approval referencea55e7641). Lands inmolecule-core/.gitea/scripts/gitea-merge-queue.pyper the 9c2e9c88 spec.What this bypasses: auto-merge a qualifying BOT runtime-bump with ZERO human reviews — bypasses the 2-genuine convention + the 1 required human approval a bot can't give. Does NOT bypass any required CI context (all required checks including the condition-2 runtime-smoke must still be green). Non-qualifying → normal 2-genuine.
What this does: trust a curated allowlist of "boring" runtimes (CEO-Asst-populated) to auto-merge single-line
.runtime-versionbumps WITHOUT 2-genuine review, but ONLY if every guard and condition below holds. Exists to break the rebase-churn loop for runtime-v releases (otherwise every bump queues behind normal review cadence and lands a day late).Implementation
GUARDS (all must hold):
author == RUNTIME_BUMP_BOT_USER(defaultbump-bot).runtime-versionAND has exactly 1 added + 1 removed non-header lineCONDITIONS (all must hold):
.runtime-version== latest runtime-v release tag (queried from the runtime repo; not merely valid semver)required_contextsincludes a real runtime-provision/compat smoke (NOT static build/lint) — case-insensitive substring match againstRUNTIME_PROVISION_SMOKE_CONTEXTS<runtime>@<version>) is onRUNTIME_BUMP_EXEMPT_TEMPLATES(CEO-Asst-populated allowlist; default empty → no runtime eligible)BREAKING_LABELSFAIL-CLOSED: any guard/condition unverifiable → NOT exempt → PR routes through normal 2-genuine path. We never auto-merge on uncertainty.
Files changed
.gitea/scripts/gitea-merge-queue.pyis_runtime_bump_exempt()predicate,get_pull_diff_files(),latest_runtime_v_tag_for(),_is_active_release_candidate(),dup_close_superseded_bump_prs(),close_pull(), wire intoevaluate_merge_readiness+_evaluate_candidate+process_once, new constants (RUNTIME_BUMP_BOT_USER,RUNTIME_BUMP_EXEMPT_TEMPLATES,RUNTIME_PROVISION_SMOKE_CONTEXTS,BREAKING_LABELS,RELEASE_CANDIDATE_LABELS,RUNTIME_VERSION_FILE).gitea/scripts/tests/test_gitea_merge_queue.pyTests
All 112 tests in
test_gitea_merge_queue.pypass (82 original + 30 new):required_contextsrejected; case-insensitive substring match verifiedruntime_bump_exempt=Truebypasses approvals check;runtime_bump_exempt=Falsestill enforces_is_active_release_candidate: detects RC label; ignores closed PRs; ignores self; returns False on no RCLint
ast.parseOK on both filesApiError,ValueError,TypeError, or named exception types)_env()helper)curl -u(no curl in this script at all)api()takes path + headers, no creds in argv)What does NOT get bypassed
evaluate_merge_readiness)required_contextsgreen gate (step 4) — including the runtime-smoke contextmergeable=Truegate (step 5)What DOES get bypassed
required_approvalscheck (step 3) — for bump-bot.runtime-versionPRs only, gated by all 3 conditions abovedup-close rationale
When the bump-bot republishes a bump (CI was red → bot repushed with same version), or a new version lands while the old is still open, multiple bump PRs race for the same
(runtime, version)slot. The exemption would auto-merge the OLDEST first (FIFO) — but the OLDEST has stale CI. We close the older ones (and post a comment) so the auto-merge path only ever sees the freshest. Best-effort: a single close failure logs and continues (does NOT abort the tick).Out of scope
RUNTIME_BUMP_EXEMPT_TEMPLATESto include any specific runtimes (that's CEO-Asst's call per the spec)Test plan
🤖 Generated with Claude Code
RFC internal#131 PR-A per CTO spec 9c2e9c88 (CEO approval a55e7641). WHAT THIS LANDS =============== 1. is_runtime_bump_exempt(pr, pr_files, required_contexts, latest_runtime_v_tag, rc_active) -> (exempt, reason) in gitea-merge-queue.py. FAIL-CLOSED: any unverifiable guard/condition returns (False, reason) and the PR routes through the normal 2-genuine path. GUARDS (all must hold): - author == RUNTIME_BUMP_BOT_USER (default 'bump-bot') - diff touches EXACTLY .runtime-version AND has 1 added + 1 removed non-header line - no active release-candidate in progress CONDITIONS (all must hold): 1. ==SSOT: VERSION part of .runtime-version == latest runtime-v release tag (queried from the runtime repo; not merely valid semver) 2a. GATE-ADEQUACY (CI): required_contexts includes a real runtime-provision/compat smoke (NOT static build/lint) — match is case-insensitive substring against RUNTIME_PROVISION_SMOKE_CONTEXTS 2b. GATE-ADEQUACY (template): runtime name (parsed from '<runtime>@<version>') is on RUNTIME_BUMP_EXEMPT_TEMPLATES (CEO-Asst-populated allowlist) 3. EXCLUDE-BREAKING: not semver-MAJOR (major>0) AND no label in BREAKING_LABELS 2. evaluate_merge_readiness() extended with runtime_bump_exempt: bool = False kwarg. When True, required_approvals is forced to 0 for the check (the approvals bar is the ONLY thing the exemption bypasses; main-green + required-contexts-green + mergeable=True gates are ALL still enforced). 3. _evaluate_candidate() computes the exempt flag (with a fail-safe try/except so an API blip → NOT exempt, normal path) and passes it to evaluate_merge_readiness. An audit ::notice:: logs WHY the approvals gate was bypassed. 4. dup_close_superseded_bump_prs() closes all but the newest bump-bot PR per title group. The exemption would auto-merge the OLDEST first (FIFO) — but the oldest has stale CI. The dup-close keeps the freshest so the auto-merge path only ever sees the fresh smoke. close_fn + comment_fn are injected for testability. 5. Helpers (fail-safe, ApiError/ValueError/TypeError → empty/None): - get_pull_diff_files(pr_number) — GET /repos/.../pulls/{n}/files - latest_runtime_v_tag_for(pr) — queries runtime repo tags, picks most recent vX.Y.Z - _is_active_release_candidate(prs, pr_number) — checks RELEASE_CANDIDATE_LABELS (default 'release-candidate,rc') - close_pull(pr_number) — PATCH /repos/.../issues/{n} state=closed 6. process_once() now calls dup_close_superseded_bump_prs() at the start of the candidate loop (best-effort, log-and-continue on per-close ApiError). UNIT TESTS (30 new, all 112 in the file pass) ============================================= - GUARD 1 (author): non-bump-bot author rejected; missing user field rejected - GUARD 2 (single-line): no .runtime-version file rejected; multi-file diff rejected; multi-line .runtime-version diff rejected; empty patch text rejected - GUARD 3 (RC): active RC rejected - CONDITION 1 (==SSOT): unverifiable latest tag rejected; SSOT mismatch (version differs) rejected - CONDITION 2a (smoke): no smoke context rejected; empty required_contexts rejected; case-insensitive substring match verified - CONDITION 2b (allowlist): runtime not on allowlist rejected; unparseable runtime name (no '@') rejected - CONDITION 3 (breaking): semver-MAJOR rejected; breaking label rejected; unparseable semver rejected - Happy path: all conditions pass → exempt; v-prefix versions parse correctly - Fail-closed: non-dict file entry rejected; empty files rejected - evaluate_merge_readiness: runtime_bump_exempt=True bypasses approvals check; runtime_bump_exempt=False still enforces - dup_close: keeps newest, closes older; no-op on single PR; ignores non-bump-bot authors - _is_active_release_candidate: detects RC label; ignores closed PRs; ignores self; returns False on no RC LINT ==== - file parses (ast.parse OK) - no bare except (all catches are specific: ApiError, ValueError, TypeError, or named exception types) - no inline tokens (all secrets via _env() helper) - no curl -u (no curl in this script at all) - no secrets in argv (api() takes path/headers, no creds in argv) DOES NOT bypass =============== - main-green gate (step 1 of evaluate_merge_readiness) - required_contexts green gate (step 4) - mergeable=True gate (step 5) - any non-required-context check (those still don't block, same as before) DOES bypass =========== - required_approvals check (step 3) — for bump-bot .runtime-version PRs only, gated by all 3 conditions above No self-merge. Per spec, route to 2-genuine (security-review + qa-review).The dup_close_superseded_bump_prs() helper had a code smell: it computed an unused 'key' variable, suppressed the unused-var warning with '_ = key', and then bucketed by ('bump', title) anyway. The inline comments described 'caller is expected to group by (runtime, version)' but the code grouped by title, which is only implicitly (runtime, version) for the standard bump-bot title format. The implicit grouping worked (all 3 existing dup-close tests passed) but the design intent (bucket by (runtime, version)) was abandoned mid-implementation. This commit makes the intent explicit: 1. New _parse_bump_pr_title() helper extracts (runtime_name, version) from a bump-bot PR title. Handles three formats: - 'runtime:<name>-v<ver>' (dash form) - '<name> bump to <ver>' (verb form) - 'runtime: <name> bump to <ver>' (hybrid: prefix + verb) Returns None for unrecognized titles — caller treats these as singleton buckets (no spurious dedup). Version normalization ('1.2.3' vs 'v1.2.3') is applied so the bucket key is stable across formats. 2. dup_close_superseded_bump_prs() now uses _parse_bump_pr_title as the bucket key. Non-matching titles get a unique sentinel key so they end up in a singleton bucket (no-op close). 3. Removed the dead 'key = ...; _ = key' code and the misleading 'caller groups by (runtime, version)' comment. 4. The 'test_runtime_bump_exempt_accepts_case_insensitive_smoke_substring' test was misnamed — it actually tested the allowlist-miss path (default empty allowlist, never set). Renamed and reworked to actually exercise the smoke case-insensitivity (sets the allowlist, uses an uppercase required context, asserts exempt=True). 5. Added 6 new tests: - _parse_bump_pr_title format-1/format-2/hybrid parsing - _parse_bump_pr_title normalizes versions across formats - _parse_bump_pr_title returns None for unrecognized titles - dup_close separates runtime versions (regression guard) All 118 tests pass (24 existing runtime-bump + 6 new + 88 unrelated). py_compile clean. Lesson: when implementing a bucket-with-then-reduce pattern, the bucket key MUST match the comment's described grouping — leftover 'key = ...; _ = key' lines (suppressing unused-variable warnings) are a tell that the design intent was abandoned mid-implementation. The test that 'happened to pass' was only passing because the test fixtures happened to use unique title strings; a fixture with two PRs of the SAME (runtime, version) under the OLD title-as-key bucketing also worked (same title), but a future fixture with a DIFFERENT title format (e.g. 'runtime:claude-code-v1.2.3' vs 'claude-code bump to v1.2.3') for the same (runtime, version) would have been bucketed separately and NOT deduped. The new explicit parser makes the dedup invariant hold across title formats. Co-Authored-By: Claude <noreply@anthropic.com>#2891 self-review RC-turnaround (2026-06-15) — pushed cleanup commit
27299c40Issue found + fixed:
dup_close_superseded_bump_prs()had a code smell where the bucket key was implicit (used the full PR title as the key) but the inline comments described a "caller is expected to group by (runtime, version)" design. The code worked because bump-bot titles happen to encode (runtime, version) for the standard formats, but the design intent (dedup by (runtime, version) across title formats) was abandoned mid-implementation.What changed:
_parse_bump_pr_title()helper extracts(runtime_name, version)from the title. Handles 3 formats:runtime:<name>-v<ver>(dash form)<name> bump to <ver>(verb form)runtime: <name> bump to <ver>(hybrid: prefix + verb)Returns None for unrecognized titles → caller treats as singleton bucket (no spurious dedup).
dup_close_superseded_bump_prs()now uses the parsed (runtime, version) tuple as the bucket key. Non-matching titles get a unique sentinel key.key = ...; _ = keycode + the misleading "caller groups" comment.test_runtime_bump_exempt_accepts_case_insensitive_smoke_substring— it was actually testing the allowlist-miss path (default empty allowlist, never set). Reworked to set the allowlist, use an uppercase required context, and assert exempt=True (the case-insensitivity claim under test).Tests added (6 new, all pass):
_parse_bump_pr_titleformat-1/format-2/hybrid parsing1.2.3vsv1.2.3produce same key)dup_closeseparates different (runtime, version) tuples (regression guard)Result: 118/118 tests pass (24 existing runtime-bump + 6 new + 88 unrelated). py_compile clean. PR head updated to
27299c40.Holding for: 2-genuine + green CI before merge. CR2 is down per PM; CR3 undeployed → 2-genuine gate is blocked. PR is ready when the gate opens. Will not self-merge.
🤖 Generated with Claude Code
COMMENT (no APPROVE) — implementation verified correct & safe, but flagging an enforcement-policy decision for PM/driver sign-off before I cast a genuine APPROVE.
Per the dispatch ("if any genuinely weakens enforcement, do NOT approve-through — flag me"): this PR adds a path where a runtime-bump PR auto-merges with ZERO human approvals —
evaluate_merge_readinesssetseffective_required_approvals = 0 if runtime_bump_exempt else required_approvals. That's a deliberate (RFC internal#131 PR-A) relaxation of the approval gate, so I'm surfacing it rather than approving-through.The implementation itself is excellent and I have no code objection — it's textbook fail-closed:
is_runtime_bump_exemptrequires ALL of: author == bump-bot (Gitea-auth, unspoofable); diff is exactly.runtime-versionwith exactly 1 added + 1 removed line (no bundling — any other file → not exempt);==SSOT(the version must equal a real published runtime-v release tag, not merely valid semver); a runtime-provision-smoke context present in BP required_contexts; runtime onRUNTIME_BUMP_EXEMPT_TEMPLATES; non-breaking (no semver-major, no breaking label). Every unverifiable/missing condition → NOT exempt (falls back to 2-genuine).mergeablestill required.RUNTIME_BUMP_EXEMPT_TEMPLATESis empty by default → the exemption is inert until the CEO-Asst explicitly opts a runtime in. The wire/dup-close/skip-non-ready queue logic all keep the fail-closed posture (force_merge never bypasses required contexts or approvals).What I need before APPROVE: explicit confirmation that the RFC internal#131 PR-A sign-off authorizes merging the 0-approval auto-merge mechanism (even though it ships inert). It's the kind of enforcement-policy change the dispatch said to route for conscious sign-off, not a single-reviewer approve-through. On your/driver confirmation I'll convert this to APPROVE immediately — the code is ready and required-5 is green. (Researcher is the other genuine.)
APPROVE — exceptionally well-guarded, fail-closed merge-queue exemption. It CANNOT bypass any required gate; it only waives the human-approval count (which a bot can't satisfy) for a tightly-constrained bump. No blocking defects. Reviewed @
27299c40(1st-genuine; CI all-required green). This is arch/security-adjacent (merge-gate automation) — I scrutinized hard; it strengthens, not weakens, the merge discipline.is_runtime_bump_exempt— cannot be abused ✅. Every guard is fail-closed ("unverifiable → not exempt → normal 2-genuine"):.runtime-version, 1 added + 1 removed line — any other file, any extra line, a non-dict entry, or missing patch text → reject. A malicious bump physically cannot smuggle other changes — this is the key anti-abuse guard.required_contextsincludes a real runtime-provision/compat smoke (provisions + runs a workspace), not static build/lint. C2b runtime ∈ curatedRUNTIME_BUMP_EXEMPT_TEMPLATES.Required CI is NOT bypassed ✅ (the central concern).
evaluate_merge_readinessline 1150:effective_required_approvals = 0 if runtime_bump_exempt else required_approvals— the exemption ONLY zeroes the approval count;required_contexts_green(all BP-required contexts incl. the runtime-smoke) is still enforced. So zero-human-review is sound precisely because a real provision-smoke + all required CI must be green.dup_close_superseded_bump_prs— safe ✅. Closes only bump-bot-authored, single-line-bump PRs, only the OLDER duplicates per (runtime, version) keeping the newest (latest updated_at → highest number);if len(group) <= 1: continueand non-standard-title→own-bucket make single/hand-edited/non-duplicate PRs no-ops. It cannot close a non-bot or non-duplicate PR. Rationale is sound (an older bump has stale CI; closing it forces the auto-merge to act on the freshest smoke).Tests ✅ 680 lines covering the guards, fail-closed paths, and dedup. Readability ✅ outstanding docstrings stating the fail-closed contract per guard.
One note for the 2nd reviewer (not blocking): confirm C2a's gate-adequacy matches the smoke to the SPECIFIC bumped runtime, not just "some provision-smoke is required." If a future allowlisted runtime lacked its own smoke context, a generic-smoke check could let it auto-merge on an unrelated smoke. The operator-curated allowlist + the ==SSOT version match mitigate this, but a per-runtime smoke assertion would close the gap fully.
Net: airtight, fail-closed, can't bypass required gates. APPROVE.
— CR2