loops: reorder history collapsed under the progress pill
Completes the reorder rationale loop — the previous commit captured
REORDER: markers server-side but nothing surfaced them. Now the loops
sidebar renders a small "N reorders ▸" button under the progress pill
whenever the loop has any reorder events. Click expands to a
newest-first list of "iter <n> · <rationale>" lines so a reviewer can
see, at a glance, when the plan was adjusted and why.
Backend:
- LoopProgress DTO gains recent_reorders: Vec<Value> — newest-first,
capped at 5 so the card stays compact. Full history remains on the
loop row's reorder_events column.
- cm_db::repo::loops::recent_reorders — reads the jsonb array, returns
the last N in newest-first order.
- list_progress populates it per loop.
Frontend:
- LoopReorderEvent + recent_reorders on LoopProgress type.
- LoopsList tracks openHistoryId per-loop (one open at a time).
- Card renders history button + expanded panel styled to match the
progress pill above.
Notes:
- The event object schema is {run_id, iteration, text, ts}. Fields are
optional in the TS type so future schema tweaks don't break the
render.
- 5-item cap chosen so the sidebar card doesn't grow unbounded. If a
loop accumulates a lot of reorders, follow-up UI can render the full
history on the loop detail page.
This commit is contained in:
@@ -254,6 +254,31 @@ pub async fn set_zeroclaw_container(
|
||||
/// downstream mini-timeline can show WHEN the plan was adjusted and
|
||||
/// WHY. Idempotent: appending a duplicate text/run_id combo is allowed
|
||||
/// (rare — indicates the parser matched twice on the same line).
|
||||
/// Read the reorder_events array for a loop, newest-first, capped at
|
||||
/// `limit`. Used by the progress endpoint to surface a compact recent
|
||||
/// history on the sidebar card. Empty array for standalone loops or
|
||||
/// loops whose coordinator hasn't emitted any REORDER markers yet.
|
||||
pub async fn recent_reorders(
|
||||
pool: &PgPool,
|
||||
loop_id: Uuid,
|
||||
limit: i64,
|
||||
) -> Result<Vec<Value>, DbError> {
|
||||
use sqlx::Row;
|
||||
let row: Option<sqlx::postgres::PgRow> =
|
||||
sqlx::query("SELECT reorder_events FROM loops WHERE id = $1")
|
||||
.bind(loop_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
let arr: Vec<Value> = row
|
||||
.and_then(|r| r.try_get::<Value, _>("reorder_events").ok())
|
||||
.and_then(|v| v.as_array().map(|a| a.clone()))
|
||||
.unwrap_or_default();
|
||||
// Appended in chronological order (oldest → newest); reversing then
|
||||
// taking `limit` yields the newest N in newest-first order.
|
||||
let recent: Vec<Value> = arr.into_iter().rev().take(limit as usize).collect();
|
||||
Ok(recent)
|
||||
}
|
||||
|
||||
pub async fn append_reorder_event(
|
||||
pool: &PgPool,
|
||||
loop_id: Uuid,
|
||||
|
||||
Reference in New Issue
Block a user