research-pipeline-diag: state-aware statuses (waiting != failing)
The diagnostic was calling any 0-outcome + 0-completed-runs state a
FAIL — red dot + 'No outcome produced yet — check the runs stage for
the failure reason'. That fires the moment a wizard-materialized
topic lands, before its very first turn even completes, and stays
red for the whole 2-3 min a legitimate coordinator turn runs. Result:
users see 'FAIL' on every fresh topic and can't tell a real failure
from a normal in-flight state.
Backend fix — introduce a `waiting` status (blue/pulsing in UI):
Runs stage:
0 runs -> skip ('No runs yet — pipeline hasn't fired')
any running/queued -> waiting ('N in flight, M completed')
all failed -> fail (with error text)
some failed -> warn
all completed no fails -> ok
Outcomes stage:
outcome_count > 0 -> ok
0 outcomes + 0 runs -> skip ('No outcome yet (pipeline hasn't fired)')
0 outcomes + any running -> waiting ('Waiting for the current run to finish…')
0 outcomes + any failed -> fail (the actual silent-bug case)
0 outcomes + all done ok -> warn (weird — completed but wrote nothing)
Also suppresses the run stage's `latest_error` detail when the run
status is `waiting` or `skip` — reporting a stale error next to an
actively-running job is what made users think the current run had
failed.
Frontend:
- PipelineStage['status'] union grows a 'waiting' arm.
- Pill color: cyan (#5ec8d8) with a pulsing scale/opacity animation
(new cm-pulse keyframe in motion.css).
- Strip summary line: 'Pipeline in flight — waiting for run to finish…'
when there's any waiting stage and no failures.
- Border tint: cyan border when waiting, coral when failing, neutral
otherwise.
Zero backend semantic changes to the outcome-write path — this is
purely UI truth-telling.
This commit is contained in:
@@ -402,7 +402,13 @@ export function ResearchCanvas({
|
||||
width: "100%",
|
||||
padding: "10px 12px",
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${pipeline.stages.some((s) => s.status === "fail") ? "rgba(255,138,122,.4)" : "rgba(255,255,255,.08)"}`,
|
||||
border: `1px solid ${
|
||||
pipeline.stages.some((s) => s.status === "fail")
|
||||
? "rgba(255,138,122,.4)"
|
||||
: pipeline.stages.some((s) => s.status === "waiting")
|
||||
? "rgba(94,200,216,.35)"
|
||||
: "rgba(255,255,255,.08)"
|
||||
}`,
|
||||
background: pipeline.stages.some((s) => s.status === "fail")
|
||||
? "rgba(255,138,122,.06)"
|
||||
: "#101014",
|
||||
@@ -426,19 +432,25 @@ export function ResearchCanvas({
|
||||
borderRadius: "50%",
|
||||
background:
|
||||
s.status === "ok" ? "#5fd08a"
|
||||
: s.status === "waiting" ? "#5ec8d8"
|
||||
: s.status === "warn" ? "#ffb44a"
|
||||
: s.status === "fail" ? "#ff8a7a"
|
||||
: "#4a4a52",
|
||||
animation: s.status === "waiting" ? "cm-pulse 1.4s ease-in-out infinite" : undefined,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</span>
|
||||
<span style={{ flex: 1, color: "#8a8a92" }}>
|
||||
{pipeline.stages.filter((s) => s.status === "fail").length > 0
|
||||
? `${pipeline.stages.filter((s) => s.status === "fail").length} failed stage(s) — click for details`
|
||||
: pipeline.stages.filter((s) => s.status === "warn").length > 0
|
||||
? `${pipeline.stages.filter((s) => s.status === "warn").length} warning(s)`
|
||||
: "All stages ok"}
|
||||
{(() => {
|
||||
const failed = pipeline.stages.filter((s) => s.status === "fail").length;
|
||||
const waiting = pipeline.stages.filter((s) => s.status === "waiting").length;
|
||||
const warn = pipeline.stages.filter((s) => s.status === "warn").length;
|
||||
if (failed > 0) return `${failed} failed stage(s) — click for details`;
|
||||
if (waiting > 0) return "Pipeline in flight — waiting for run to finish…";
|
||||
if (warn > 0) return `${warn} warning(s)`;
|
||||
return "All stages ok";
|
||||
})()}
|
||||
</span>
|
||||
<span style={{ opacity: 0.7 }}>{diagOpen ? "▾" : "▸"}</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user