mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Restore thread recency with compatible migration history (#28671)
## Summary - Revert #28655, restoring the thread `recencyAt` behavior introduced by #27910. - Move `threads_recency_at` to migration 0039 so it no longer collides with `external_agent_config_imports` at version 0038. - Repair databases that already applied the recency migration as version 38 by moving the matching migration-history row to version 39 before SQLx validation. The current version-38 migration can then apply normally. ## Validation - `just test -p codex-state migrations::tests::repairs_recency_migration_that_was_applied_as_version_38` - `just test -p codex-state -p codex-rollout -p codex-thread-store -p codex-app-server-protocol -p codex-tui`: 3,439 passed; six TUI tests could not open the machine's existing read-only incident database at `~/.codex/sqlite/state_5.sqlite`. - `just fix -p codex-state` - `just fmt` - Verified that state migration versions are unique.
This commit is contained in:
@@ -2501,6 +2501,7 @@ mod tests {
|
||||
model_provider: "openai".to_string(),
|
||||
created_at: 1,
|
||||
updated_at: 2,
|
||||
recency_at: Some(3),
|
||||
status: v2::ThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: cwd.clone(),
|
||||
@@ -2544,6 +2545,7 @@ mod tests {
|
||||
"modelProvider": "openai",
|
||||
"createdAt": 1,
|
||||
"updatedAt": 2,
|
||||
"recencyAt": 3,
|
||||
"status": {
|
||||
"type": "idle"
|
||||
},
|
||||
|
||||
@@ -174,6 +174,7 @@ fn thread_resume_response_round_trips_initial_turns_page() {
|
||||
model_provider: "openai".to_string(),
|
||||
created_at: 1,
|
||||
updated_at: 1,
|
||||
recency_at: Some(1),
|
||||
status: ThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: absolute_path("tmp"),
|
||||
@@ -3600,6 +3601,7 @@ fn thread_lifecycle_responses_default_missing_optional_fields() {
|
||||
|
||||
assert_eq!(start.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
assert_eq!(start.thread.parent_thread_id, None);
|
||||
assert_eq!(start.thread.recency_at, None);
|
||||
assert_eq!(resume.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
assert_eq!(fork.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
assert_eq!(start.active_permission_profile, None);
|
||||
@@ -3608,6 +3610,14 @@ fn thread_lifecycle_responses_default_missing_optional_fields() {
|
||||
assert_eq!(fork.active_permission_profile, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_recency_sort_key_serializes_as_snake_case() {
|
||||
assert_eq!(
|
||||
serde_json::to_value(ThreadSortKey::RecencyAt).expect("sort key should serialize"),
|
||||
json!("recency_at")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_start_params_preserve_explicit_null_service_tier() {
|
||||
let params: TurnStartParams = serde_json::from_value(json!({
|
||||
|
||||
@@ -1086,6 +1086,7 @@ pub enum ThreadSourceKind {
|
||||
pub enum ThreadSortKey {
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
RecencyAt,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, JsonSchema, TS)]
|
||||
|
||||
@@ -152,6 +152,9 @@ pub struct Thread {
|
||||
/// Unix timestamp (in seconds) when the thread was last updated.
|
||||
#[ts(type = "number")]
|
||||
pub updated_at: i64,
|
||||
/// Unix timestamp (in seconds) used for thread recency ordering.
|
||||
#[ts(type = "number | null")]
|
||||
pub recency_at: Option<i64>,
|
||||
/// Current runtime status for the thread.
|
||||
pub status: ThreadStatus,
|
||||
/// [UNSTABLE] Path to the thread on disk.
|
||||
|
||||
Reference in New Issue
Block a user