fix(#2375): pr_is_open state check at submission + atomic fetched_sha INSERT
Two hardening items from argus's review of #2374: 1. PR state check at submission: - Add `pr_is_open(repo, pr)` to forge/pr_merge.rs using `repo_get_pull_request` + `StateType` — early error if the PR is already closed or merged instead of queuing a card that fails later - Call it in `submit_merge_config_pr` before fetching the head sha 2. Atomic fetched_sha INSERT: - Add `fetched_sha: Option<&str>` to `Approvals::submit_kind` so the sha can be included in the INSERT rather than a follow-up UPDATE - MergeConfigPr already knows the sha before inserting the row (pr_head_sha runs first) → pass `Some(&sha)`, drop the separate `set_fetched_sha` call → truly atomic - ApplyCommit still needs two writes (sha resolved by git_fetch_to_tag after the row exists) → pass `None`, `set_fetched_sha` unchanged - All other callers (InitConfig, Spawn, UpdateMetaInputs, SchedulePrompt) pass `None` — no behavioural change - Add `fetched_sha_in_insert_is_readable_via_get` test covering the MergeConfigPr path
This commit is contained in:
parent
d39d05b0b3
commit
96eda4ed6b
7 changed files with 102 additions and 15 deletions
|
|
@ -83,6 +83,7 @@ pub(super) fn handle_request_update_meta_inputs(
|
|||
&commit_ref,
|
||||
description,
|
||||
requester,
|
||||
None,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("{e:#}"))
|
||||
{
|
||||
|
|
@ -162,7 +163,21 @@ async fn submit_merge_config_pr(
|
|||
);
|
||||
}
|
||||
let repo = crate::forge::config_repo(agent);
|
||||
// Verify the PR is still open before queueing an approval that would
|
||||
// fail at approve time anyway (a closed/merged PR has no live head ref
|
||||
// for the drift gate to compare against).
|
||||
if !crate::forge::pr_is_open(&repo, pr_number)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("check PR state for {agent} PR #{pr_number}: {e}"))?
|
||||
{
|
||||
anyhow::bail!(
|
||||
"PR #{pr_number} on {repo} is closed or already merged — \
|
||||
request_merge_config_pr requires an open PR"
|
||||
);
|
||||
}
|
||||
// Fetch the current PR head sha — becomes the "reviewed" sha.
|
||||
// Submitted together with the approval row (atomic single INSERT) so a
|
||||
// crash between submit and set_fetched_sha cannot leave a stranded row.
|
||||
let sha = crate::forge::pr_head_sha(&repo, pr_number)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("fetch PR head sha for {agent} PR #{pr_number}: {e}"))?;
|
||||
|
|
@ -174,12 +189,9 @@ async fn submit_merge_config_pr(
|
|||
&pr_number.to_string(),
|
||||
description,
|
||||
submitter,
|
||||
Some(&sha), // atomic: sha inserted with the row, not in a separate UPDATE
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("queue merge_config_pr approval row: {e:#}"))?;
|
||||
coord
|
||||
.approvals
|
||||
.set_fetched_sha(id, &sha)
|
||||
.map_err(|e| anyhow::anyhow!("persist fetched_sha: {e:#}"))?;
|
||||
let sha_short = sha[..sha.len().min(12)].to_owned();
|
||||
coord.emit_approval_added(crate::coordinator::ApprovalAdded {
|
||||
id,
|
||||
|
|
@ -249,6 +261,7 @@ pub(crate) fn submit_init_config(
|
|||
// parent); it's also the submitter the approval events route
|
||||
// back to. No declared parent = operator-initiated path.
|
||||
parent.unwrap_or("operator"),
|
||||
None, // no sha for InitConfig
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("queue approval row: {e:#}"))?;
|
||||
tracing::info!(%id, %name, "init_config approval queued");
|
||||
|
|
@ -309,6 +322,7 @@ pub(crate) async fn submit_apply_commit(
|
|||
commit_ref,
|
||||
description,
|
||||
submitter,
|
||||
None, // sha resolved after git_fetch_to_tag below; set via set_fetched_sha
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("queue approval row: {e:#}"))?;
|
||||
let tag = format!("proposal/{id}");
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ pub(super) fn handle_request_schedule_prompt(
|
|||
&commit_ref,
|
||||
payload.description.as_deref(),
|
||||
requester,
|
||||
None,
|
||||
) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue