You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(review): hold the gate when an enforced pre-merge check's files can't be resolved (#1371)
review.pre_merge_checks built changedPaths from getReviewFiles(), which returns [] when the PR's files
are unresolved (review fired before detail-sync AND the inline fetch failed). evaluatePreMergeChecks then
treated every whenPaths-gated check as N/A (`[].some()` is false) and SKIPPED it — including enforce:true
checks — so a guarded PR with green CI auto-MERGED, silently bypassing the maintainer's hard requirement.
A PR always touches >=1 file, so an empty resolved set means UNRESOLVED. Thread filesResolved into
evaluatePreMergeChecks: when false, an enforced whenPaths check emits a new `pre_merge_check_unresolved`
finding that isEvaluationBlocker treats as a NEUTRAL gate (HELD, re-evaluates on the next sync) — never
silently skipping the requirement (auto-merge bypass) and never hard-closing the contributor on a transient
resolution miss. Advisory checks are dropped (no noise); path-less checks are unaffected.
// is N/A (no finding). Empty whenPaths ⇒ the check always applies (title/description/label only).
36
+
if(check.whenPaths.length>0){
37
+
if(!filesResolved){
38
+
// The changed-file set could not be resolved, so we cannot evaluate this path gate. HOLD the gate for an
39
+
// ENFORCED check (re-evaluates when files resolve) instead of silently skipping a hard requirement (which
40
+
// would let a guarded PR auto-merge). An advisory check is just dropped (no noise on a transient miss).
41
+
if(check.enforce)
42
+
findings.push({
43
+
code: PRE_MERGE_CHECK_UNRESOLVED_CODE,
44
+
severity: "warning",
45
+
title: `Pre-merge check held — changed files not resolved: ${check.name}`,
46
+
detail: `Gittensory could not resolve this PR's changed files to evaluate the path-gated check "${check.name}"; the gate is held and re-evaluates automatically.`,
47
+
action: "No action needed — the gate re-evaluates once the PR's files are available.",
if(check.titleContains!==null&&!title.includes(check.titleContains.toLowerCase()))unmet.push(`the title must contain "${check.titleContains}"`);
33
55
if(check.descriptionContains!==null&&!body.includes(check.descriptionContains.toLowerCase()))unmet.push(`the description must contain "${check.descriptionContains}"`);
expect(result.conclusion).toBe("neutral");// held: not a pass (would auto-merge past the unverified check) nor a failure (would auto-close on a transient miss)
it("filesResolved=false HOLDS an enforced whenPaths check (unresolved code) but skips an advisory one and still evaluates non-path checks (#review-audit)",()=>{
0 commit comments