From 18dd8c0352dae105362f18351339301e40a5e3c1 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Thu, 11 Jun 2026 13:13:29 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Speed up lane inspect status projection","authority":"XY-910"} --- apps/decodex/src/orchestrator/lane_control.rs | 5 +- apps/decodex/src/orchestrator/status.rs | 52 ++++++++++++++++ .../tests/operator/status/http.rs | 60 +++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/apps/decodex/src/orchestrator/lane_control.rs b/apps/decodex/src/orchestrator/lane_control.rs index c3fd920e8..bc98360a7 100644 --- a/apps/decodex/src/orchestrator/lane_control.rs +++ b/apps/decodex/src/orchestrator/lane_control.rs @@ -306,12 +306,13 @@ pub(super) fn build_lane_inspect_report( issue: &str, run_id: Option<&str>, ) -> Result { - let snapshot = orchestrator::build_operator_status_snapshot( + let runs = orchestrator::build_lane_inspect_operator_runs( project, state_store, + issue, + run_id, DEFAULT_STATUS_RUN_LIMIT, )?; - let runs = matching_lane_runs(&snapshot, issue, run_id); if runs.is_empty() { eyre::bail!( diff --git a/apps/decodex/src/orchestrator/status.rs b/apps/decodex/src/orchestrator/status.rs index 5b6349e6f..0d33354cd 100644 --- a/apps/decodex/src/orchestrator/status.rs +++ b/apps/decodex/src/orchestrator/status.rs @@ -367,6 +367,58 @@ fn build_operator_status_snapshot_with_account_mode( Ok(snapshot) } +fn build_lane_inspect_operator_runs( + project: &ServiceConfig, + state_store: &StateStore, + issue: &str, + run_id: Option<&str>, + limit: usize, +) -> crate::prelude::Result> { + let now_unix_epoch = OffsetDateTime::now_utc().unix_timestamp(); + let (active_runs, recent_runs) = state_store.list_project_runs(project.service_id(), limit)?; + let project_display_name = operator_project_display_name(project); + let mut seen_run_ids = HashSet::new(); + let mut runs = Vec::new(); + + for run in active_runs.into_iter().chain(recent_runs) { + if !seen_run_ids.insert(run.run_id().to_owned()) { + continue; + } + if !project_run_status_issue_matches(&run, issue) { + continue; + } + if run_id.is_some_and(|expected| expected != run.run_id()) { + continue; + } + + runs.push(operator_run_status( + project, + state_store, + &project_display_name, + run, + now_unix_epoch, + )?); + } + + Ok(runs) +} + +fn project_run_status_issue_matches(run: &ProjectRunStatus, issue: &str) -> bool { + let issue = issue.trim(); + let worktree_path = run.worktree_path().map(|path| path.display().to_string()); + let issue_identifier = operator_run_issue_identifier_from_fields( + run.run_id(), + run.branch_name(), + worktree_path.as_deref(), + ); + + run.issue_id() == issue + || issue_identifier.as_deref() == Some(issue) + || issue_identifier + .as_ref() + .is_some_and(|identifier| identifier.eq_ignore_ascii_case(issue)) +} + fn operator_active_run_statuses( project: &ServiceConfig, state_store: &StateStore, diff --git a/apps/decodex/src/orchestrator/tests/operator/status/http.rs b/apps/decodex/src/orchestrator/tests/operator/status/http.rs index 3688078c4..34e3836a9 100644 --- a/apps/decodex/src/orchestrator/tests/operator/status/http.rs +++ b/apps/decodex/src/orchestrator/tests/operator/status/http.rs @@ -1781,8 +1781,68 @@ fn operator_lane_inspect_api_returns_lane_identity() { assert_eq!(data["issue"], "PUB-101"); assert_eq!(data["matchedRunCount"], 1); assert_eq!(data["runs"][0]["runId"], "pub-101-attempt-1"); + assert_eq!(data["runs"][0]["attemptStatus"], "running"); + assert_eq!(data["runs"][0]["activeLease"], true); assert_eq!(data["runs"][0]["threadId"], "thread-1"); assert_eq!(data["runs"][0]["turnId"], "turn-1"); + assert_eq!(data["runs"][0]["softInterruptAvailable"], false); + assert_eq!(data["runs"][0]["hardInterruptRequiresForce"], true); +} + +#[test] +fn operator_lane_inspect_api_filters_by_run_id() { + let (_temp_dir, config, _workflow) = temp_project_layout(); + let state_store = StateStore::open_in_memory().expect("state store should open"); + let registration = ProjectRegistration::from_config( + config.service_id(), + &service_config_path(config.repo_root()), + &config, + true, + "test-fingerprint", + ); + let issue = sample_issue("In Progress", &[]); + let worktree_path = config.worktree_root().join(&issue.identifier); + + fs::create_dir_all(&worktree_path).expect("worktree should exist"); + + state_store.upsert_project(®istration).expect("project should register"); + state_store + .record_run_attempt("pub-101-attempt-1", &issue.id, 1, "running") + .expect("run attempt should record"); + state_store + .upsert_lease(config.service_id(), &issue.id, "pub-101-attempt-1", "In Progress") + .expect("lease should record"); + state_store + .upsert_worktree( + config.service_id(), + &issue.id, + "x/pubfi-pub-101", + &worktree_path.display().to_string(), + ) + .expect("worktree should record"); + + let response = String::from_utf8(orchestrator::build_operator_lane_inspect_http_response( + &state_store, + format!( + "GET {}?projectId=pubfi&issue=PUB-101&runId=pub-101-attempt-2 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n", + orchestrator::OPERATOR_LANE_INSPECT_ENDPOINT_PATH + ) + .as_bytes(), + )) + .expect("lane inspect response should be utf-8"); + let body = response + .split_once("\r\n\r\n") + .map(|(_, body)| body) + .expect("lane inspect response should include body"); + let data: Value = serde_json::from_str(body).expect("lane inspect response should be json"); + + assert!(response.starts_with("HTTP/1.1 400 Bad Request\r\n")); + assert!( + data["error"] + .as_str() + .unwrap_or_default() + .contains("No local lane matched issue `PUB-101` and run `pub-101-attempt-2`") + ); } #[test]