Files
codex/codex-rs/thread-store/src/local/move_thread_to_section.rs
T
joeytrasatti-openai ad6fc66b6d Add persisted manual ordering for thread sections (#36007)
## What changed

- Add `thread/section/move` to atomically move a thread into, within, or out of a section. Threads can be inserted before an existing member or appended, and moves within a section preserve `sectionEnteredAt`.
- Add `section_position` sorting to `thread/list`, with ascending order as its default, and expose `sectionEnteredAt` in thread responses.
- Persist section positions and entry times in SQLite, including migration of existing section members into recency order. Section membership is no longer updated through `thread/metadata/update`.

## Testing

- Cover section moves, reordering, pagination, persistence across restarts and rollout reconciliation, concurrent updates, and rank renumbering.

GitOrigin-RevId: aec6d7ddedca5277029b5caf5c074975397e956c
2026-07-29 18:37:22 +00:00

59 lines
1.7 KiB
Rust

use super::LocalThreadStore;
use crate::MoveThreadToSectionParams;
use crate::ThreadStoreError;
use crate::ThreadStoreResult;
pub(super) async fn move_thread_to_section(
store: &LocalThreadStore,
params: MoveThreadToSectionParams,
) -> ThreadStoreResult<()> {
if params
.section
.as_deref()
.is_some_and(|section| section.trim().is_empty())
{
return Err(ThreadStoreError::InvalidRequest {
message: "section must not be empty".to_owned(),
});
}
if params.section.is_none() && params.before_thread_id.is_some() {
return Err(ThreadStoreError::InvalidRequest {
message: "before thread cannot be specified without a section".to_owned(),
});
}
let Some(state_db) = store.state_db().await else {
return Err(ThreadStoreError::Unsupported {
operation: "thread/section/move",
});
};
let updated = state_db
.move_thread_to_section(
params.thread_id,
params.section.as_deref(),
params.before_thread_id,
)
.await
.map_err(|err| {
let message = err.to_string();
if message.starts_with("before thread ")
|| message.starts_with("thread ")
|| message.starts_with("section ")
{
ThreadStoreError::InvalidRequest { message }
} else {
ThreadStoreError::Internal {
message: format!("failed to move thread {}: {message}", params.thread_id),
}
}
})?;
if !updated {
return Err(ThreadStoreError::ThreadNotFound {
thread_id: params.thread_id,
});
}
Ok(())
}