From 827fccef8ea61b5dd80670c3aa63c2dedd152b8b Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Mon, 18 May 2026 23:26:12 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Treat pending land checks as wait","authority":"manual"} --- apps/decodex/src/manual.rs | 33 +++++++++++++++++++++++++--- plugins/decodex/skills/land/SKILL.md | 7 ++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/apps/decodex/src/manual.rs b/apps/decodex/src/manual.rs index 89a34dd0..9f9212ca 100644 --- a/apps/decodex/src/manual.rs +++ b/apps/decodex/src/manual.rs @@ -806,6 +806,15 @@ fn validate_landing_state( ) { eyre::bail!("Pull request `{pr_url}` has failed required checks that need repair."); } + + if let Some(other) = gate_view.status_check_rollup_state + && pull_request::checks_require_wait(Some(other)) + { + eyre::bail!( + "Pull request `{pr_url}` is still waiting on checks: statusCheckRollup=`{other}`." + ); + } + if pull_request::mergeability_unknown(gate_view) { eyre::bail!( "Pull request `{pr_url}` mergeability is still unknown after retry; wait for GitHub to recompute mergeability and retry `decodex land`." @@ -825,9 +834,6 @@ fn validate_landing_state( } match gate_view.status_check_rollup_state { - Some(other) if pull_request::checks_require_wait(Some(other)) => eyre::bail!( - "Pull request `{pr_url}` is still waiting on checks: statusCheckRollup=`{other}`." - ), Some("SUCCESS") | None => { debug_assert!(pull_request::manual_landing_gates_satisfied(gate_view)); @@ -2109,6 +2115,27 @@ exit 1\n", assert!(error.to_string().contains("retry `decodex land`")); } + #[test] + fn landing_state_validation_treats_pending_checks_as_wait_even_when_merge_blocked() { + let mut landing_state = sample_landing_state(); + + landing_state.base_ref_name = String::from("main"); + landing_state.merge_state_status = String::from("BLOCKED"); + landing_state.status_check_rollup_state = Some(String::from("PENDING")); + + let error = manual::validate_landing_state( + &landing_state, + "https://github.com/hack-ink/decodex/pull/64", + "main", + "XY-225", + "deadbeef", + ) + .expect_err("pending checks should wait rather than report a generic blocked merge state"); + + assert!(error.to_string().contains("still waiting on checks")); + assert!(error.to_string().contains("statusCheckRollup=`PENDING`")); + } + #[test] fn execute_land_merge_uses_admin_merge() { let temp_dir = TempDir::new().expect("temp dir should create"); diff --git a/plugins/decodex/skills/land/SKILL.md b/plugins/decodex/skills/land/SKILL.md index 1b09ab62..08bd44ba 100644 --- a/plugins/decodex/skills/land/SKILL.md +++ b/plugins/decodex/skills/land/SKILL.md @@ -28,8 +28,8 @@ with GitHub UI, `gh pr merge`, merge queue actions, raw `git`, or direct API mut ## Sequence -1. Confirm the PR exists, the intended base and head are the ones being landed, and the - repository expects Decodex-owned landing. +1. Confirm the PR exists, the intended base and head are the ones being landed, required + checks are green, and the repository expects Decodex-owned landing. 2. Run `decodex land ""`. 3. For a deliberate non-issue lane, run `decodex land --manual-authority --pr ""`. @@ -52,6 +52,9 @@ but intentionally skips tracker closeout and active-label ownership checks. ## Fail-Closed Rules - If `decodex land` is required, it must run and succeed. +- If `decodex land` reports that checks are still pending or expected, treat that as a + wait condition: keep the tracker issue in its retained review state, keep the active + ownership label in place, wait for CI, and retry `decodex land`. - Do not substitute `gh pr merge`, GitHub UI, merge queue, raw `git`, direct GitHub API mutation, or a hand-assembled merge for a failed or unavailable `decodex land`. - If GitHub merge already happened but `decodex land` stopped during closeout or