fix: distinguish Forgejo mergeability states #242
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#242
Loading…
Reference in a new issue
No description provided.
Delete branch "codex-bot-andresmgsl/ceremony:build/236-forgejo-mergeable"
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?
Closes #236
Summary
Teach the Forgejo backend to distinguish WIP and fast-forward check-window false values from real merge conflicts, while preserving the GitHub backend and all existing consumers.
Acceptance criteria
forge_pr_viewreturnsUNKNOWNfor a draft PR reportingmergeable: false, with RED-first output recorded.forge_pr_viewreturnsUNKNOWNfor a non-draft false value whosemerge_baseequalsbase.sha, with RED-first output recorded.CONFLICTING, andmergeable: trueremainsMERGEABLE.mergeable: truereturnsUNKNOWN, proving draft is evaluated first.lib/forge-github.shhas no diff; no existing backend assertion is deleted or weakened; the assertion count rises from 124 to 128.Worklog
Round log
Round at
8f9f7e56Round passed with no written reply.
📣 round answered at head
8f9f7e560fWIP: fix: distinguish Forgejo mergeability statesto fix: distinguish Forgejo mergeability statesINFRASTRUCTURE —
labels / labels (pull_request)failed in Forgejo run 1418 at head8f9f7e560f1b307b922750d0804acb777cde56dcbecause the runner-issued token was denied write permission (HTTP 403), not because of this branch's code.Evidence from the actual job log:
pull_request_targetjob checked outmainat1f5dd39a986f5e7256819051c42063c1af657700;POST .../actions/workflows/self-labels-sweep.yml/dispatchesfailed 403 (user should have a permission to write to a repo);POST .../issues/242/labelsfailed with the same 403;changelog.d/236.md,lib/forge-forgejo.sh, andtest/forge-backends.test.sh.This is an outside-the-PR token-permission failure in the base workflow, and it is not safely transient. I attempted the available Forgejo rerun routes for this exact run/job — API run rerun, API job rerun, and web job rerun — and all returned HTTP 404; this Forgejo 8.0.3 run exposes no rerunnable endpoint to this builder credential. No rerun was started and no code change was made.
Applying the green-head exception and requesting the panel by hand for head
8f9f7e560f1b307b922750d0804acb777cde56dc.The red check is outside PR #242: run 1418's
pull_request_targetjob checked out basemainat1f5dd39a986f5e7256819051c42063c1af657700, then its runner token received HTTP 403 on both workflow dispatch and label-write operations before any PR-head code ran. The exact run is not rerunnable through this Forgejo 8.0.3 API/web credential (all available rerun routes returned 404), so the engine cannot obtain a green result or make its normal panel request. The full failure classification and rerun evidence are in the preceding comment.🔎 reviewing head
8f9f7e560f🔎 reviewing head
8f9f7e560fReviewed head
8f9f7e560f1b307b922750d0804acb777cde56dc.mergeablemapping inlib/forge-forgejo.shevaluatesdraftfirst, thenmergeable == true, thenmerge_base == base.shaasUNKNOWN, elseCONFLICTING. The ordering matches the spec and the comments name the upstream conditions.2029c9f5;test/forge-backends.test.shreports 125 passed, 3 failed (the new draft, fast-forward, and ordering fixtures fail as expected). At the PR head the same file reports 128 passed, 0 failed.lib/forge-github.shis unchanged; no existing assertion was deleted or weakened.changelog.d/236.mdis present and well-formed.bash .github/scripts/shellcheck-all.shpassed over all 64 tracked scripts.changelog-armed.test.sh,drill-recorded.test.sh,facts.test.sh, andversion.test.sh, all failing becausenodeis not installed in this box. These are environment gaps unrelated to the changed files; I relied on reading those tests rather than running them.labels / labels (pull_request)status at the head is failure. I could not fetch the job log through the API, so I relied on the builder's failure classification (apull_request_targettoken-permission failure in the base workflow) plus the fact that the PR diff touches onlylib/forge-forgejo.sh,test/forge-backends.test.sh, andchangelog.d/236.md— no workflow or action files.Approve.
🔎 reviewing head
8f9f7e560fVerdict: approve — whole PR re-reviewed at head
8f9f7e560f1b307b922750d0804acb777cde56dcagainst #236.Acceptance criteria — verified by running, not reading:
lib/forge-forgejo.sh:434-437matches the issue spec exactly (draft →UNKNOWNfirst;mergeable == true→MERGEABLE; non-draft false withmerge_base == base.sha→UNKNOWN; elseCONFLICTING), and the comment names the four folded upstream conditions and why draft is read first (#236).2029c9fand rantest/forge-backends.test.sh— exactly 125 passed, 3 failed, the failures being precisely the two new cases (draft+false, fast-forward+false) and the arm-ordering pin (draft+true), while the real-conflict control passed. Same counts the worklog records.forge-backends.test.sh128 passed, 0 failed (assertion count up from 124; nothing deleted or weakened — the hardcoded stub line becameFAKE_PRwithdraft:false, mergeable:true, so the pre-existing assertion passes unedited); full suite 31/31 test files; sanctioned shellcheck (bash .github/scripts/shellcheck-all.sh) clean over 64 tracked scripts; self-ref, marker, and vendored guards all green.lib/forge-github.shhas no diff — the PR touches onlychangelog.d/236.md,lib/forge-forgejo.sh,test/forge-backends.test.sh.changelog.d/236.mdcarries the### Fixedheading, per thegroupedshape sentinel.forge_pr_listreturns only numbers —forge_pr_viewis the sole mergeable mapper, so no sibling mapping site was left unfixed.The standing
blocker:ci-redis infrastructure, not this PR: the only failed check at this head islabels / labels(run 1418), apull_request_targetjob that checks outmainand 403'd on workflow-dispatch and label writes before any PR-head code ran. The identical labels failure stands on #237's and #239's heads (including after #239 merged green), and every CIpull_requestjob on this head is "Blocked by required conditions" (runner queue), while CI onmainruns green. No green pull_request rollup is currently producible on this forge for any PR; the builder documented this with log evidence and hand-requested the panel, which I treat as valid — the criterion's substance (suite + sanctioned shellcheck at this exact head) is what I ran above.What I could not verify, and what I relied on instead: actionlint and the
CEREMONY_REQUIRE_NPM=1/CEREMONY_REQUIRE_YQ=1CI variants (actionlint, npm, and yq are absent on this box — those test cases ran in local skip mode), and the five CI exercise jobs (runner-blocked). Relied on: the PR touches no workflow or action files, so the actionlint surface is unchanged; and the same suite the CItestjob runs, executed here at the head. The reconciler can clearblocker:ci-redonce the forge can run checks again.Approve — head
8f9f7e560f1b307b922750d0804acb777cde56dc, reviewed whole in a detached worktree. On-panel (.github/labels.conf:1, minus the author).What I ran
test/labels-bootstrap.test.sh(7 passed, 7 failed) andtest/labels-dispatch.test.sh(7 passed, 14 failed) red onyq: command not found. I ran both against base1f5dd39from a cleangit archiveand got the identical counts, so they are this box's gap, not the diff's. CI setsCEREMONY_REQUIRE_YQ=1andubuntu-latestshipsyq.test/forge-backends.test.sh: 128 passed, 0 failed at head; 124 passed, 0 failed at base. +4, exactly the count the criterion names.lib/forge-forgejo.sh— 125 passed, 3 failed, and the three are preciselydraft mergeability as unknown,fast-forward false mergeability as unknown, andreads draft before the mergeable boolean. The real-conflict control passes on base unedited, as #236 predicted. This is the PR body's checkpoint number, arrived at from the other direction.127 passed, 1 failed, each reddening only its own case):reads draft before the mergeable booleanreds;fast-forward false mergeability as unknownreds;else "CONFLICTING"→"UNKNOWN"→ onlypreserves a distinguishable real conflictreds.So the failure cases fail for their own reason, and the arm-ordering fixture earns its place.
bash .github/scripts/shellcheck-all.sh→ rc=0 (64 tracked scripts).git diff 1f5dd39..HEAD -- lib/forge-github.sh→ 0 lines.Against the acceptance criteria
All six met. Beyond the counts above:
grep -n 'mergeable' lib/forge-forgejo.shfinds the mapping in exactly one place (:434-437), so there is no second translation site left on the old behaviour.blockers()atactions/labels-reconcile/labels-reconcile.sh:525, which acts onCONFLICTINGalone;:1081reads it with a// "UNKNOWN"default. Nothing anywhere requiresMERGEABLEpositively, so moving drafts toUNKNOWNremoves a label and gates nothing — which is what #236 scoped and what the:519-524comment already wanted.merge_baseandbase.shaare both present on the single-PR route (pulls/242: both1f5dd39a98…), so arm 3 tests a field that exists rather than one that defaults.changelog.d/236.mdmatches thegroupedshape sentinel and the<issue>.mdname, with the(#236)cite. The in-code comment names the four upstream conditions and whydraftis read first — the why convention, with its number.What I could not verify, and what I relied on instead
CI / test,release-exercise,self-guards,action-exercise,docs-sync-exerciseandRefs guardall still readpendingwithupdated_at17:41:5xZ— queued, not failed. The onefailureislabels / labels, and that same context also fails onad23842andb7a2b31— the commits of merged PR #239, which predate this change and do not touch this code. So the standingblocker:ci-redis a pre-existing red on an infrastructure job, not work this diff owes. I substituted the local suite +shellcheck-all.shabove for theCI / testjob's evidence; the exercise jobs (release-exercise,action-exercise,docs-sync-exercise) I did not reproduce.repos/heavy-duty/ceremony/actions/runs/1418and the jobs-log route both 404 on this instance, so my attribution oflabels / labelsrests on the cross-commit comparison above, not on its output.actionlintis absent on this box. This PR changes no YAML, so nothing it would have checked moved.Non-blocking nits (approval does not wait on these)
test/forge-backends.test.sh— thechecks_state"real proof" block now consumes theview_jsonleft by the last new fixture (draft:true, mergeable:true), the one the comment above it correctly calls unreachable on the forge. The assertion is still sound, becausestatusCheckRollupcomes fromFAKE_STATUSand is orthogonal to the mergeability arms — I confirmed it still classifiesSUCCESS. But a reader following "feed it to the production classifier" now reads it against a fixture the file itself disclaims. Re-settingFAKE_PRto the baseline line before it would keep that comment literally true.==between two fields; if the API ever omitted both,null == nullwould take theUNKNOWNbranch. That fails toward "no label", which is the safe direction here, and I verified both fields are present live — recording it only so the shape is known rather than discovered.🧭 Triage note for whoever merges this — the
blocker:ci-redon this PR is the label machinery grading its own run, which #208 says it must never do. Minted as #243. No label moved here: PR state is machine-owned and this resolves itself.Label events re-read by hand immediately before this write, not the thread:
blocker:conflict+state:buildingadded byforgejo-actionsat 17:41:21Z; at 17:54:39Z the sweep addedblocker:ci-red+state:addressingand removed both of the first pair; @codex-bot-andresmgsl addedstate:needs-humanat 18:20:53Z. Current set:blocker:ci-red,state:addressing,state:needs-human.The red. The rollup at head
8f9f7e56holds seven entries. Six arepending; the singlefailureislabels / labels (pull_request)— this repository's own label workflow, failing for the reason #241 documents.labels-reconcileis supposed to dropSELF_WORKFLOW's entries before grading, but that exclusion tests.workflowName, and the Forgejo backend'sforge_pr_viewnever emits it. So the sweep graded the label machine's own failure and setblocker:ci-redat 17:54:39Z. Nothing in this branch is red, and no edit to this branch could clear it. Details and the fix contract are in #243.The two
state:labels. Not a contradiction anyone needs to repair by hand. The author'sstate:needs-humanwrite at 18:20:53Z is the one hand-set state, and the sweep that validates it within seconds is woken bylabels.yml's trigger job — which 403s on fork heads (runs 1431 and 1432 fired at 18:20:53Z and 18:20:57Z, both failed). The hourly cron sweep is unaffected and will reconcile at the top of the hour. When it does it will resolve tostate:addressing, becauseblocker:ci-redstands and the reconciler refusesstate:needs-humanwhile any blocker is up.So this PR will read
state:addressingshortly, and that reading will be wrong for the reason above rather than because anything is owed on this branch. Triage is not hand-moving it — a hand-set state is overwritten by the next sweep, and the honest repair is #243.Nothing here is a review verdict or a merge recommendation; the panel's three approvals stand on their own and the merge is @andres's call as always. This note exists so the red is not read as this branch's debt.