Skip to content

Commit c672c78

Browse files
{"schema":"decodex/commit/1","summary":"Land Fix retained handoff reconciliation","authority":"manual"}
2 parents 3931f13 + 1136abe commit c672c78

8 files changed

Lines changed: 462 additions & 96 deletions

File tree

apps/decodex/src/agent/tracker_tool_bridge/review.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,6 @@ impl<'a> TrackerToolBridge<'a> {
486486
self.persist_review_orchestration_marker(review_context, &orchestration_marker)?;
487487

488488
if let Err(error) = self.tracker.update_issue_state(&self.issue.id, success_state_id) {
489-
if let Some(state_store) = self.state_store {
490-
state_store.clear_review_markers(&self.issue.id)?;
491-
}
492-
493489
return Err(Report::new(ReviewHandoffWritebackFailed {
494490
issue_identifier: self.issue.identifier.clone(),
495491
run_id: review_context.run_id.clone(),

apps/decodex/src/agent/tracker_tool_bridge/tests/review/handoff.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ fn review_handoff_apply_does_not_duplicate_existing_ledger_event() {
387387
}
388388

389389
#[test]
390-
fn reports_partial_review_handoff_when_state_transition_fails_after_tracker_record_write() {
390+
fn keeps_review_handoff_marker_when_state_transition_fails_after_tracker_record_write() {
391391
let temp_dir = TempDir::new().expect("tempdir should create");
392392
let tracker = FakeTracker::with_state_update_error("tracker state write failed");
393393
let issue = sample_issue();
@@ -445,11 +445,13 @@ fn reports_partial_review_handoff_when_state_transition_fails_after_tracker_reco
445445
assert!(error.to_string().contains("tracker state write failed"));
446446
assert_eq!(tracker.comments.borrow().len(), 1);
447447
assert!(tracker.state_updates.borrow().is_empty());
448-
assert!(
448+
assert_eq!(
449449
bridge_state_store(&bridge)
450450
.review_handoff_marker(TEST_SERVICE_ID, &issue.id, "x/decodex-pub-618")
451451
.expect("runtime handoff marker read should succeed")
452-
.is_none()
452+
.expect("partial review handoff should keep the retained marker")
453+
.pr_url(),
454+
"https://github.com/hack-ink/decodex/pull/49"
453455
);
454456
}
455457

apps/decodex/src/manual.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ pub(crate) fn run_land(config_path: Option<&Path>, request: &ManualLandRequest)
209209
}
210210
if context.prepared_closeout.is_some() && context.review_handoff.is_none() {
211211
eyre::bail!(
212-
"`decodex land` issue closeout requires a retained review handoff marker so it can write deterministic Linear execution ledger events. Run `decodex recovery review-handoff rebind` for `{}` before retrying.",
212+
"`decodex land` issue closeout requires a retained review handoff marker so it can write deterministic Linear execution ledger events. Run `decodex recover review-handoff rebind` for `{}` before retrying.",
213213
context.current_branch
214214
);
215215
}
@@ -534,7 +534,11 @@ fn finalize_land_closeout(
534534
eyre::eyre!("`decodex land` issue cleanup requires a retained review handoff marker.")
535535
})?;
536536

537-
clear_manual_closeout_runtime_state(state_store, &prepared_closeout.issue.id)?;
537+
clear_manual_closeout_runtime_state(
538+
state_store,
539+
&prepared_closeout.issue.id,
540+
handoff.run_id(),
541+
)?;
538542
clear_manual_closeout_issue_scope(
539543
&prepared_closeout.tracker,
540544
&prepared_closeout.issue,
@@ -1578,10 +1582,17 @@ where
15781582
Ok(())
15791583
}
15801584

1581-
fn clear_manual_closeout_runtime_state(state_store: &StateStore, issue_id: &str) -> Result<()> {
1585+
fn clear_manual_closeout_runtime_state(
1586+
state_store: &StateStore,
1587+
issue_id: &str,
1588+
handoff_run_id: &str,
1589+
) -> Result<()> {
15821590
state_store.succeed_active_run_attempts_for_issue(issue_id).wrap_err_with(|| {
15831591
format!("Failed to finalize active runtime attempts for issue `{issue_id}`.")
15841592
})?;
1593+
1594+
succeed_manual_land_handoff_attempt(state_store, issue_id, handoff_run_id)?;
1595+
15851596
state_store
15861597
.clear_lease(issue_id)
15871598
.wrap_err_with(|| format!("Failed to clear runtime lease for issue `{issue_id}`."))?;
@@ -1592,6 +1603,28 @@ fn clear_manual_closeout_runtime_state(state_store: &StateStore, issue_id: &str)
15921603
Ok(())
15931604
}
15941605

1606+
fn succeed_manual_land_handoff_attempt(
1607+
state_store: &StateStore,
1608+
issue_id: &str,
1609+
handoff_run_id: &str,
1610+
) -> Result<()> {
1611+
let Some(attempt) = state_store.run_attempt(handoff_run_id)? else {
1612+
return Ok(());
1613+
};
1614+
1615+
if attempt.issue_id() != issue_id {
1616+
eyre::bail!(
1617+
"Manual land handoff run `{handoff_run_id}` belongs to issue `{}`, not `{issue_id}`.",
1618+
attempt.issue_id()
1619+
);
1620+
}
1621+
if attempt.status() != "succeeded" {
1622+
state_store.update_run_status(handoff_run_id, "succeeded")?;
1623+
}
1624+
1625+
Ok(())
1626+
}
1627+
15951628
fn linear_label_not_on_issue_error(error: &Report) -> bool {
15961629
error
15971630
.chain()
@@ -2749,7 +2782,7 @@ exit 1\n",
27492782
let issue = sample_issue("issue-1", "XY-225", true, &["decodex:active:pubfi"]);
27502783
let other_issue = sample_issue("issue-2", "XY-226", true, &["decodex:active:pubfi"]);
27512784
let handoff = state::ReviewHandoffMarker::new(
2752-
"run-1",
2785+
"run-1-failed",
27532786
1,
27542787
"y/decodex-xy-225",
27552788
"https://github.com/hack-ink/decodex/pull/67",
@@ -2783,7 +2816,7 @@ exit 1\n",
27832816
.record_run_attempt("run-2", &other_issue.id, 1, "running")
27842817
.expect("other issue running attempt should persist");
27852818

2786-
manual::clear_manual_closeout_runtime_state(&state_store, &issue.id)
2819+
manual::clear_manual_closeout_runtime_state(&state_store, &issue.id, handoff.run_id())
27872820
.expect("manual closeout runtime state should clear");
27882821

27892822
assert!(
@@ -2834,7 +2867,7 @@ exit 1\n",
28342867
.expect("run attempt lookup should succeed")
28352868
.expect("run attempt should remain")
28362869
.status(),
2837-
"failed"
2870+
"succeeded"
28382871
);
28392872
assert_eq!(
28402873
state_store

0 commit comments

Comments
 (0)