A restored node whose source run is gone says its curve was lost

The run detail works out curve_lost per node when read, from whether its
cached_from run still exists. The run page names the deleted run in place
of the generic NO_CURVE note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx
This commit is contained in:
2026-10-15 10:22:09 +02:00
co-authored by Claude Opus 5.5
parent df2620cede
commit f0bddc56a4
7 changed files with 50 additions and 10 deletions
+8 -7
View File
@@ -22,10 +22,10 @@ import { MetricPicker, RunMetricChart, useMetricNames } from "./MetricChart"
import { OpenInDashboard } from "./OpenInDashboard"
import {
CARD,
curveLost,
downloadArtifact,
FLOW_GONE,
isLive,
NO_CURVE,
paramText,
runQueryOptions,
shortCommit,
@@ -53,10 +53,9 @@ export function RunDetail({ id }: { id: string }) {
const nodes = run.nodes ?? []
const artifacts = run.artifacts ?? []
const reason = statusReason(run)
// A restored node's curve comes from the run it was restored from, so an
// empty chart here means that run is gone rather than that this one failed.
// Said once, where it applies.
const cached = nodes.some((node) => node.status === "cached")
// A restored node's curve comes from the run it was restored from, so it
// goes when that run is deleted. Said once, where it applies.
const lost = nodes.filter((node) => node.curve_lost)
// What the flow declared when this run was submitted. A run from before
// that was kept reads against the flow's current declarations.
@@ -206,8 +205,10 @@ export function RunDetail({ id }: { id: string }) {
</section>
)}
{names.length === 0 && cached && (
<p className={cn(CARD, "text-muted-foreground text-sm")}>{NO_CURVE}</p>
{lost.length > 0 && (
<p className={cn(CARD, "text-muted-foreground text-sm")}>
{curveLost(lost)}
</p>
)}
{Object.keys(run.result ?? {}).length > 0 && (
+14
View File
@@ -285,6 +285,20 @@ export async function cancelThenDelete(runId: string) {
export const NO_CURVE =
"No curve was recorded. A node restored from the cache is read back from the run that produced it, so this draws nothing once that run has been deleted — its outputs are still on the result."
/**
* Which restored nodes lost their curve, and with which deleted run.
*
* Named, since a chart that is merely short a few lines says nothing.
*/
export function curveLost(nodes: { node: string; cached_from?: string }[]) {
const runs = [...new Set(nodes.map((one) => shortId(one.cached_from ?? "")))]
const which =
runs.length === 1
? `run ${runs[0]}, which was`
: `runs ${runs.join(", ")}, which were`
return `The curve of ${nodes.map((one) => one.node).join(", ")} was lost with ${which} deleted. A node restored from the cache reads its curve from the run that recorded it. Its outputs are still on the result.`
}
/** Why a run can name a flow that is not there any more. */
export const FLOW_GONE =
"The flow this ran has been deleted. The run is kept as the record of what ran, but there is nothing left to retry it against."