rename queued state to ready
Migration 012 updates existing rows from 'queued' to 'ready'.
Migration 013 recreates the tasks table with a CHECK constraint
for the new set of valid states (icebox, ready, in_progress, done).
Enum variant renamed from Queued to Ready, as_str returns 'ready'.
Parsing still accepts 'queued' for backward compatibility. CSS
classes and web UI updated to match.
diff --git a/migrations/012_rename_queued_to_ready.sql b/migrations/012_rename_queued_to_ready.sql
new file mode 100644
index 0000000..2d29052
--- /dev/null
+++ b/migrations/012_rename_queued_to_ready.sql
@@ -0,0 +1 @@
+UPDATE tasks SET state = 'ready' WHERE state = 'queued';
diff --git a/migrations/013_add_state_check.sql b/migrations/013_add_state_check.sql
new file mode 100644
index 0000000..7442be9
--- /dev/null
+++ b/migrations/013_add_state_check.sql
@@ -0,0 +1,27 @@
+-- Re-add CHECK constraint for valid state values (now includes 'ready').
+PRAGMA foreign_keys = OFF;
+
+CREATE TABLE tasks_new (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ key TEXT NOT NULL UNIQUE,
+ backlog_id INTEGER NOT NULL REFERENCES backlogs(id) ON DELETE CASCADE,
+ title TEXT NOT NULL,
+ description TEXT,
+ state TEXT NOT NULL DEFAULT 'icebox'
+ CHECK (state IN ('icebox', 'ready', 'in_progress', 'done')),
+ position TEXT NOT NULL,
+ archived INTEGER NOT NULL DEFAULT 0,
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
+ done_at TEXT,
+ CHECK ((state = 'done') = (done_at IS NOT NULL))
+);
+
+INSERT INTO tasks_new (id, key, backlog_id, title, description, state, position, archived, created_at, updated_at, done_at)
+SELECT id, key, backlog_id, title, description, state, position, archived, created_at, updated_at, done_at
+FROM tasks;
+
+DROP TABLE tasks;
+ALTER TABLE tasks_new RENAME TO tasks;
+
+PRAGMA foreign_keys = ON;
diff --git a/src/bin/ranger/commands/backlog.rs b/src/bin/ranger/commands/backlog.rs
index a611656..2842ee5 100644
--- a/src/bin/ranger/commands/backlog.rs
+++ b/src/bin/ranger/commands/backlog.rs
@@ -74,7 +74,7 @@ pub async fn run(pool: &SqlitePool, command: BacklogCommands, json: bool) -> Res
let states: Vec<State> = if done {
vec![State::Done]
} else {
- vec![State::InProgress, State::Queued, State::Icebox]
+ vec![State::InProgress, State::Ready, State::Icebox]
};
if json {
diff --git a/src/bin/ranger/commands/serve.rs b/src/bin/ranger/commands/serve.rs
index 703a481..8320255 100644
--- a/src/bin/ranger/commands/serve.rs
+++ b/src/bin/ranger/commands/serve.rs
@@ -104,13 +104,13 @@ async fn render_board(state: &AppState, backlog_name: &str) -> color_eyre::Resul
let prefixes = key::unique_prefix_lengths(&all_keys);
let mut in_progress = Vec::new();
- let mut queued = Vec::new();
+ let mut ready = Vec::new();
let mut icebox = Vec::new();
let mut done = Vec::new();
for s in [
ranger::models::State::InProgress,
- ranger::models::State::Queued,
+ ranger::models::State::Ready,
ranger::models::State::Icebox,
ranger::models::State::Done,
] {
@@ -122,14 +122,14 @@ async fn render_board(state: &AppState, backlog_name: &str) -> color_eyre::Resul
let views = to_task_views(&tasks, &prefixes, &mut conn).await?;
match s {
ranger::models::State::InProgress => in_progress = views,
- ranger::models::State::Queued => queued = views,
+ ranger::models::State::Ready => ready = views,
ranger::models::State::Icebox => icebox = views,
ranger::models::State::Done => done = views,
}
}
- let total = in_progress.len() + queued.len() + icebox.len() + done.len();
- let active = in_progress.len() + queued.len();
+ let total = in_progress.len() + ready.len() + icebox.len() + done.len();
+ let active = in_progress.len() + ready.len();
Ok(html! {
(DOCTYPE)
@@ -170,7 +170,7 @@ async fn render_board(state: &AppState, backlog_name: &str) -> color_eyre::Resul
div.counts { (active) " active · " (total) " total" }
}
div.board {
- (render_backlog_panel(&in_progress, &queued))
+ (render_backlog_panel(&in_progress, &ready))
(render_column_panel("Icebox", "state-icebox", &icebox))
(render_column_panel("Done", "state-done", &done))
}
@@ -180,15 +180,15 @@ async fn render_board(state: &AppState, backlog_name: &str) -> color_eyre::Resul
})
}
-fn render_backlog_panel(in_progress: &[TaskView], queued: &[TaskView]) -> Markup {
- let count = in_progress.len() + queued.len();
+fn render_backlog_panel(in_progress: &[TaskView], ready: &[TaskView]) -> Markup {
+ let count = in_progress.len() + ready.len();
html! {
div.panel {
div.panel-header {
h2 { "Backlog" }
span.count { (count) }
}
- @if in_progress.is_empty() && queued.is_empty() {
+ @if in_progress.is_empty() && ready.is_empty() {
div.empty { "No active tasks" }
} @else {
@if !in_progress.is_empty() {
@@ -198,9 +198,9 @@ fn render_backlog_panel(in_progress: &[TaskView], queued: &[TaskView]) -> Markup
}
}
}
- @if !queued.is_empty() {
- div.state-queued {
- @for task in queued {
+ @if !ready.is_empty() {
+ div.state-ready {
+ @for task in ready {
(render_task(task))
}
}
diff --git a/src/models.rs b/src/models.rs
index 3a73072..45426c2 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -7,7 +7,7 @@ use crate::timestamp::Timestamp;
#[serde(rename_all = "snake_case")]
pub enum State {
Icebox,
- Queued,
+ Ready,
InProgress,
Done,
}
@@ -16,7 +16,7 @@ impl State {
pub fn as_str(&self) -> &'static str {
match self {
State::Icebox => "icebox",
- State::Queued => "queued",
+ State::Ready => "ready",
State::InProgress => "in_progress",
State::Done => "done",
}
@@ -26,7 +26,7 @@ impl State {
pub fn rank(&self) -> u8 {
match self {
State::Icebox => 0,
- State::Queued => 1,
+ State::Ready => 1,
State::InProgress => 2,
State::Done => 3,
}
@@ -42,7 +42,7 @@ impl std::str::FromStr for State {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"icebox" => Ok(State::Icebox),
- "queued" | "ready" => Ok(State::Queued),
+ "ready" | "queued" => Ok(State::Ready),
"in_progress" => Ok(State::InProgress),
"done" => Ok(State::Done),
_ => Err(InvalidStateError(s.to_string())),
@@ -123,7 +123,7 @@ mod tests {
fn state_roundtrips_through_display_and_parse() {
for (state, expected) in [
(State::Icebox, "icebox"),
- (State::Queued, "queued"),
+ (State::Ready, "ready"),
(State::InProgress, "in_progress"),
(State::Done, "done"),
] {
@@ -135,9 +135,9 @@ mod tests {
}
#[test]
- fn ready_parses_as_queued() {
- let parsed: State = "ready".parse().unwrap();
- assert_eq!(parsed, State::Queued);
+ fn queued_parses_as_ready() {
+ let parsed: State = "queued".parse().unwrap();
+ assert_eq!(parsed, State::Ready);
}
#[test]
diff --git a/src/ops/task.rs b/src/ops/task.rs
index 31c774b..8ca48fa 100644
--- a/src/ops/task.rs
+++ b/src/ops/task.rs
@@ -597,7 +597,7 @@ mod tests {
CreateTask {
title: "Queued task",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -621,7 +621,7 @@ mod tests {
&mut conn,
bl.id,
&ListFilter {
- state: Some(State::Queued),
+ state: Some(State::Ready),
..Default::default()
},
)
@@ -676,14 +676,14 @@ mod tests {
task.id,
Some("Updated"),
Some("A description"),
- Some(State::Queued),
+ Some(State::Ready),
)
.await
.unwrap();
assert_eq!(updated.title, "Updated");
assert_eq!(updated.description.as_deref(), Some("A description"));
- assert_eq!(updated.state, State::Queued);
+ assert_eq!(updated.state, State::Ready);
}
#[tokio::test]
@@ -1027,7 +1027,7 @@ mod tests {
CreateTask {
title: "Q",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1048,7 +1048,7 @@ mod tests {
let err = move_task(&mut conn, &queued, Placement::Before(&done))
.await
.unwrap_err();
- assert!(err.to_string().contains("queued"));
+ assert!(err.to_string().contains("ready"));
assert!(err.to_string().contains("done"));
}
@@ -1086,7 +1086,7 @@ mod tests {
CreateTask {
title: "Queued 1",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1130,7 +1130,7 @@ mod tests {
CreateTask {
title: "Queued 1",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1141,7 +1141,7 @@ mod tests {
CreateTask {
title: "Queued 2",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1160,16 +1160,16 @@ mod tests {
.unwrap();
// Move in_progress task to queued — should land before Queued 1
- let updated = edit(&mut conn, ip.id, None, None, Some(State::Queued))
+ let updated = edit(&mut conn, ip.id, None, None, Some(State::Ready))
.await
.unwrap();
- assert_eq!(updated.state, State::Queued);
+ assert_eq!(updated.state, State::Ready);
let queued = list(
&mut conn,
bl.id,
&ListFilter {
- state: Some(State::Queued),
+ state: Some(State::Ready),
..Default::default()
},
)
@@ -1195,7 +1195,7 @@ mod tests {
CreateTask {
title: "First",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1206,7 +1206,7 @@ mod tests {
CreateTask {
title: "Second",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1216,7 +1216,7 @@ mod tests {
let original_pos = t1.position.clone();
// Edit to same state — position should not change
- let updated = edit(&mut conn, t1.id, None, None, Some(State::Queued))
+ let updated = edit(&mut conn, t1.id, None, None, Some(State::Ready))
.await
.unwrap();
assert_eq!(updated.position, original_pos);
@@ -1225,7 +1225,7 @@ mod tests {
&mut conn,
bl.id,
&ListFilter {
- state: Some(State::Queued),
+ state: Some(State::Ready),
..Default::default()
},
)
@@ -1246,7 +1246,7 @@ mod tests {
CreateTask {
title: "Queued task",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1484,7 +1484,7 @@ mod tests {
CreateTask {
title: "Tagged queued",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1495,7 +1495,7 @@ mod tests {
CreateTask {
title: "Untagged queued",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1508,7 +1508,7 @@ mod tests {
&mut conn,
bl.id,
&ListFilter {
- state: Some(State::Queued),
+ state: Some(State::Ready),
tag: Some("bug".to_string()),
..Default::default()
},
@@ -1553,7 +1553,7 @@ mod tests {
CreateTask {
title: "Queued task",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1577,7 +1577,7 @@ mod tests {
CreateTask {
title: "Task",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1613,7 +1613,7 @@ mod tests {
.unwrap();
assert!(task.done_at.is_some());
- let updated = edit(&mut conn, task.id, None, None, Some(State::Queued))
+ let updated = edit(&mut conn, task.id, None, None, Some(State::Ready))
.await
.unwrap();
assert!(
@@ -1634,7 +1634,7 @@ mod tests {
CreateTask {
title: "First done",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1645,7 +1645,7 @@ mod tests {
CreateTask {
title: "Second done",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
@@ -1656,7 +1656,7 @@ mod tests {
CreateTask {
title: "Third done",
backlog_id: bl.id,
- state: Some(State::Queued),
+ state: Some(State::Ready),
description: None,
},
)
diff --git a/static/style.css b/static/style.css
index 53d432b..72c57b7 100644
--- a/static/style.css
+++ b/static/style.css
@@ -24,13 +24,13 @@
/* ── Color tokens: state ── */
--color-in-progress: #629a3e;
- --color-queued: #3d6098;
+ --color-ready: #3d6098;
--color-done: #8ba83b;
--color-icebox: #8a8878;
/* ── State row tints (12 % opacity) ── */
--tint-in-progress: rgba(210, 170, 30, 0.12);
- --tint-queued: rgba(160, 160, 160, 0.12);
+ --tint-ready: rgba(160, 160, 160, 0.12);
--tint-done: rgba(98, 154, 62, 0.24);
--tint-icebox: rgba(61, 96, 152, 0.20);
@@ -237,7 +237,7 @@ header .counts {
}
.section-label-in-progress { color: var(--color-in-progress); }
-.section-label-queued { color: var(--color-queued); }
+.section-label-ready { color: var(--color-ready); }
.section-label-done { color: var(--color-done); }
.section-label-icebox { color: var(--color-icebox); }
@@ -384,7 +384,7 @@ details.task[open] > summary .expand-icon {
/* === State-specific row tints === */
.state-in-progress .task { background: #fdf6e3; }
-.state-queued .task { background: #f5f5f3; }
+.state-ready .task { background: #f5f5f3; }
.state-done .task { background: #eef5e6; }
.state-icebox .task { background: #e8eef6; }
diff --git a/tests/cli.rs b/tests/cli.rs
index d238423..6717270 100644
--- a/tests/cli.rs
+++ b/tests/cli.rs
@@ -259,7 +259,7 @@ fn full_workflow() {
// Should show task state sections
assert!(
stdout.contains("[in_progress]")
- || stdout.contains("[queued]")
+ || stdout.contains("[ready]")
|| stdout.contains("[icebox]")
);
@@ -333,10 +333,7 @@ fn full_workflow() {
!stdout.contains("[in_progress]"),
"--done should not show in_progress"
);
- assert!(
- !stdout.contains("[queued]"),
- "--done should not show queued"
- );
+ assert!(!stdout.contains("[ready]"), "--done should not show ready");
assert!(
!stdout.contains("[icebox]"),
"--done should not show icebox"
@@ -350,7 +347,7 @@ fn full_workflow() {
assert!(output.status.success());
let detail: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert!(detail["tasks"]["done"].is_array());
- assert!(detail["tasks"]["queued"].is_null());
+ assert!(detail["tasks"]["ready"].is_null());
assert!(detail["tasks"]["in_progress"].is_null());
// Backlog show JSON without --done excludes done tasks