1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-10-09 21:03:17 +02:00

fix: retain completed summary during regeneration display

This commit is contained in:
archivr-qa 2026-08-24 16:50:51 +02:00
parent 13ae315d99
commit 33cdb21926
No known key found for this signature in database
4 changed files with 110 additions and 31 deletions

View file

@ -51,9 +51,12 @@ pub struct EntryDetail {
pub source_metadata_json: String,
pub display_metadata_json: Option<String>,
pub artifacts: Vec<EntryArtifactSummary>,
/// Most recently updated summary for this entry, if any has ever been
/// requested. Always `None` on a fresh capture — summarization is manual.
/// Most recent completed summary for this entry. Always `None` on a fresh
/// capture — summarization is manual.
pub latest_summary: Option<EntrySummaryView>,
/// Latest non-completed generation attempt, kept separate so a replacement
/// never displaces readable completed content.
pub summary_attempt: Option<EntrySummaryView>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
@ -354,7 +357,8 @@ pub fn get_entry_detail(
})?
.collect::<rusqlite::Result<Vec<_>>>()?;
let latest_summary = database::latest_entry_summary(conn, entry_id)?;
let latest_summary = database::latest_completed_entry_summary(conn, entry_id)?;
let summary_attempt = database::latest_entry_summary_attempt(conn, entry_id)?;
Ok(Some(EntryDetail {
summary,
@ -363,6 +367,7 @@ pub fn get_entry_detail(
display_metadata_json,
artifacts,
latest_summary,
summary_attempt,
}))
}

View file

@ -1644,7 +1644,8 @@ pub fn find_entry_summary(
}
/// Most recently touched summary for an entry, whatever its status.
/// Backs `EntryDetail.latest_summary` and the GET summary route.
/// Used where a caller explicitly needs the most recent attempt regardless of
/// whether it has completed.
pub fn latest_entry_summary(
conn: &Connection,
entry_id: i64,
@ -1679,6 +1680,24 @@ pub fn latest_completed_entry_summary(
.map_err(Into::into)
}
/// The newest replacement attempt that has not completed. This includes failed
/// rows so authenticated callers can show the failure beside a retained result.
pub fn latest_entry_summary_attempt(
conn: &Connection,
entry_id: i64,
) -> Result<Option<EntrySummaryRecord>> {
conn.query_row(
&format!(
"{ENTRY_SUMMARY_COLS} WHERE s.entry_id = ?1 AND s.status != 'completed'
ORDER BY s.updated_at DESC, s.id DESC LIMIT 1"
),
[entry_id],
map_entry_summary,
)
.optional()
.map_err(Into::into)
}
pub fn create_archive_run(
conn: &Connection,
created_by_user_id: i64,
@ -4719,6 +4738,36 @@ mod tests {
);
}
#[test]
fn latest_summary_attempt_is_separate_from_the_retained_completed_summary() {
let c = conn();
let entry = create_entry_fixture(&c, "private", None, None);
let completed =
upsert_pending_entry_summary(&c, entry.id, "claude_cli", None, "v1", "old").unwrap();
update_entry_summary_status(&c, &completed, "completed", Some("previous"), None).unwrap();
let pending =
upsert_pending_entry_summary(&c, entry.id, "claude_cli", None, "v1", "new").unwrap();
assert_eq!(
latest_completed_entry_summary(&c, entry.id).unwrap().unwrap().summary_uid,
completed
);
assert_eq!(
latest_entry_summary_attempt(&c, entry.id).unwrap().unwrap().summary_uid,
pending
);
update_entry_summary_status(&c, &pending, "failed", None, Some("boom")).unwrap();
assert_eq!(
latest_completed_entry_summary(&c, entry.id).unwrap().unwrap().summary_text.as_deref(),
Some("previous")
);
assert_eq!(
latest_entry_summary_attempt(&c, entry.id).unwrap().unwrap().status,
"failed"
);
}
#[test]
fn fail_stalled_entry_summaries_marks_pending_and_running_with_restart_message() {
let c = conn();