research: fix Draft 'Invalid Date' + stale-error leak in pipeline card
Two cosmetic bugs surfaced by the successful v0.8.3 pipeline run: 1. **'produced Invalid Date'** — research_outcomes.created_at was an OffsetDateTime serialized by time's default array format (`[y, ordinal, hh, mm, ss, ns, tz]`), which browser's `new Date(...)` can't parse. Add `#[serde(with = "time::serde::rfc3339")]` matching the pattern already in threads.rs / routine_runs.rs. 2. **Stale error text on pipeline card** — the runs stage's `latest_error` walked every run by `created_at DESC` and returned the first non-empty error, so a topic with an earlier failed run + a later completed run kept displaying the old error next to '1 completed'. Now the error only surfaces when the MOST RECENT run itself failed. Historical failures stay in the run count but don't leak their message.
This commit is contained in:
@@ -167,10 +167,22 @@ pub async fn pipeline_state(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|r| r.try_get::<String, _>("status").ok().as_deref() == Some("completed"))
|
.filter(|r| r.try_get::<String, _>("status").ok().as_deref() == Some("completed"))
|
||||||
.count();
|
.count();
|
||||||
let latest_error = run_rows
|
// 2026-07-16: only surface the error from the MOST RECENT run and
|
||||||
.iter()
|
// only if that run itself failed. Previously we walked every run
|
||||||
.find_map(|r| r.try_get::<Option<String>, _>("error").ok().flatten())
|
// and returned the first non-empty error, so a pre-migration
|
||||||
.filter(|s| !s.is_empty());
|
// failed run's stale error kept showing next to a fresh successful
|
||||||
|
// run — reading like "everything is still broken" when it wasn't.
|
||||||
|
let latest_error = run_rows.first().and_then(|r| {
|
||||||
|
let status = r.try_get::<String, _>("status").ok();
|
||||||
|
if status.as_deref() == Some("failed") {
|
||||||
|
r.try_get::<Option<String>, _>("error")
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
// Status rules:
|
// Status rules:
|
||||||
// - 0 runs → skip (nothing to see yet — natural pre-fire state,
|
// - 0 runs → skip (nothing to see yet — natural pre-fire state,
|
||||||
// NOT a failure)
|
// NOT a failure)
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ pub struct Outcome {
|
|||||||
pub version: i32,
|
pub version: i32,
|
||||||
pub body_md: String,
|
pub body_md: String,
|
||||||
pub produced_by_run_id: Option<Uuid>,
|
pub produced_by_run_id: Option<Uuid>,
|
||||||
|
// RFC3339 on the wire so `new Date(...)` in the browser parses it
|
||||||
|
// instead of choking on the `time` crate's default `[y, ordinal,
|
||||||
|
// ...]` array format (surfaced as "Invalid Date" in the Draft
|
||||||
|
// header).
|
||||||
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
pub created_at: OffsetDateTime,
|
pub created_at: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
# bookworm-pinned so the binary's glibc matches the bookworm runtime stage
|
# bookworm-pinned so the binary's glibc matches the bookworm runtime stage
|
||||||
FROM rust:1.94-slim-bookworm AS build
|
FROM rust:1.96-slim-bookworm AS build
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
pkg-config build-essential cmake libssl-dev ca-certificates git \
|
pkg-config build-essential cmake libssl-dev ca-certificates git \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
Reference in New Issue
Block a user