research-pipeline-diag: state-aware statuses (waiting != failing)
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 37s
ci / rust (push) Successful in 2m53s
ci / e2e (push) Has been skipped
ci / publish (push) Successful in 2m48s

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:
Omar Sobh
2026-07-11 08:38:15 -07:00
parent 6e06bcf136
commit ee05037095
4 changed files with 103 additions and 18 deletions
@@ -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>