Fix remote goal file materialization

This commit is contained in:
Eric Traut
2026-06-10 12:26:34 -07:00
parent 2c4dfc0dec
commit 77cf2eeea4
7 changed files with 136 additions and 11 deletions
+2 -1
View File
@@ -131,8 +131,9 @@ impl App {
draft: goal_files::GoalDraft,
mode: ThreadGoalSetMode,
) {
let codex_home = app_server.goal_files_codex_home(&self.config.codex_home);
let result =
goal_files::materialize_goal_draft(app_server, &self.config.codex_home, draft).await;
goal_files::materialize_goal_draft(app_server, codex_home.as_ref(), draft).await;
let objective = match result {
Ok(objective) => objective,
Err(err) => {
+10
View File
@@ -238,6 +238,16 @@ impl AppServerSession {
matches!(self.thread_params_mode, ThreadParamsMode::Remote)
}
pub(crate) fn goal_files_codex_home(
&self,
local_codex_home: &AbsolutePathBuf,
) -> Option<AbsolutePathBuf> {
match self.thread_params_mode {
ThreadParamsMode::Embedded => Some(local_codex_home.clone()),
ThreadParamsMode::Remote => self.client.remote_codex_home().cloned(),
}
}
pub(crate) fn server_version(&self) -> Option<&str> {
let AppServerClient::Remote(client) = &self.client else {
return None;
@@ -659,6 +659,43 @@ async fn goal_slash_command_uses_plain_text_for_mentions() {
assert_no_submit_op(&mut op_rx);
}
#[tokio::test]
async fn goal_slash_command_preserves_selected_file_path_text() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.set_feature_enabled(Feature::Goals, /*enabled*/ true);
let thread_id = ThreadId::new();
chat.thread_id = Some(thread_id);
chat.bottom_pane
.set_composer_text("/goal inspect @read".to_string(), Vec::new(), Vec::new());
chat.bottom_pane.on_file_search_result(
"read".to_string(),
vec![codex_file_search::FileMatch {
score: 1,
path: PathBuf::from("README.md"),
match_type: codex_file_search::MatchType::File,
root: test_project_path(),
indices: None,
}],
);
chat.handle_key_event(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
submit_current_composer(&mut chat);
let (actual_thread_id, draft) = loop {
match rx.try_recv().expect("expected goal draft event") {
AppEvent::SetThreadGoalDraft {
thread_id, draft, ..
} => break (thread_id, draft),
_ => continue,
}
};
assert_eq!(actual_thread_id, thread_id);
assert_eq!(draft.objective, "inspect README.md");
assert!(draft.pending_pastes.is_empty());
assert!(draft.local_images.is_empty());
assert_no_submit_op(&mut op_rx);
}
#[tokio::test]
async fn goal_slash_command_emits_attached_images() {
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
+4 -2
View File
@@ -76,7 +76,7 @@ impl GoalFileStore for AppServerSession {
pub(crate) async fn materialize_goal_draft(
store: &mut impl GoalFileStore,
codex_home: &AbsolutePathBuf,
codex_home: Option<&AbsolutePathBuf>,
draft: GoalDraft,
) -> Result<String> {
let mut objective = draft.objective;
@@ -214,12 +214,14 @@ pub(crate) fn objective_file_reference(path: &Path) -> Result<String> {
async fn ensure_output_dir(
store: &mut impl GoalFileStore,
codex_home: &AbsolutePathBuf,
codex_home: Option<&AbsolutePathBuf>,
output_dir: &mut Option<AbsolutePathBuf>,
) -> Result<AbsolutePathBuf> {
if let Some(output_dir) = output_dir {
return Ok(output_dir.clone());
}
let codex_home = codex_home
.context("App server did not report $CODEX_HOME; cannot materialize goal files")?;
let path = codex_home
.join(GOAL_ATTACHMENT_DIR)
.join(Uuid::new_v4().to_string());
+41 -2
View File
@@ -32,7 +32,7 @@ async fn materializes_and_reads_oversized_objective_through_store() {
let reference = materialize_goal_draft(
&mut store,
&codex_home,
Some(&codex_home),
GoalDraft {
objective: objective.clone(),
..Default::default()
@@ -72,7 +72,7 @@ async fn materializes_paste_and_image_through_store() {
let objective = materialize_goal_draft(
&mut store,
&codex_home,
Some(&codex_home),
GoalDraft {
objective,
text_elements: vec![
@@ -118,3 +118,42 @@ fn path_after(text: &str, prefix: &str) -> AbsolutePathBuf {
.expect("path");
AbsolutePathBuf::from_absolute_path_checked(path).expect("absolute path")
}
#[tokio::test]
async fn plain_objective_does_not_need_codex_home() {
let mut store = LocalStore;
let objective = materialize_goal_draft(
&mut store,
/*codex_home*/ None,
GoalDraft {
objective: "read src/lib.rs".to_string(),
..Default::default()
},
)
.await
.expect("materialize plain goal draft");
assert_eq!(objective, "read src/lib.rs");
}
#[tokio::test]
async fn oversized_objective_requires_codex_home() {
let mut store = LocalStore;
let err = materialize_goal_draft(
&mut store,
/*codex_home*/ None,
GoalDraft {
objective: "x".repeat(MAX_THREAD_GOAL_OBJECTIVE_CHARS + 1),
..Default::default()
},
)
.await
.expect_err("oversized objective should require codex home");
assert!(
err.to_string().contains("$CODEX_HOME"),
"expected codex home error, got {err:#}"
);
}