Record which inputs a run's caller passed, and mark defaults from it

`Run.passed` (migration d5a3c8e1f246) keeps the names the caller chose, so
the run detail and the comparison table can mark a value as the flow's own
again; rows from before it fall back to the names their params lack. Retries
hand on their parent's list, imports record their entry's.

Also: a cancel of a queued run no longer leaves its id in `_cancelled`, a
linked comparison no longer crashes before the list loads, and the pre-commit
failures are fixed (stale client, `text-[10px]`, worker formatting).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GAeg4Jo6xjZzjwy9QDWMQ
This commit is contained in:
2026-10-05 15:01:33 +02:00
co-authored by Claude Opus 5.5
parent 84e054cdd1
commit 8e655c842f
12 changed files with 134 additions and 31 deletions
+8 -3
View File
@@ -30,6 +30,7 @@ import {
runQueryOptions,
shortCommit,
shortId,
tookDefault,
useCancelRun,
useFlowInputs,
useRetryRun,
@@ -64,11 +65,15 @@ export function RunDetail({ id }: { id: string }) {
...[...declared.keys()].map((name) => ({
name,
value: name in run.params ? run.params[name] : declared.get(name),
fromFlow: !(name in run.params),
fromFlow: tookDefault(run, name),
})),
...Object.keys(run.params)
.filter((name) => !declared.has(name))
.map((name) => ({ name, value: run.params[name], fromFlow: false })),
.map((name) => ({
name,
value: run.params[name],
fromFlow: tookDefault(run, name),
})),
]
return (
@@ -246,7 +251,7 @@ export function Entry({
<dt className={cn(LABEL, "flex items-baseline gap-1.5")}>
{name}
{note && (
<span className="rounded-full border border-border px-1.5 text-[10px]">
<span className="rounded-full border border-border px-1.5 text-xs">
{note}
</span>
)}
+20 -13
View File
@@ -62,6 +62,7 @@ import {
STEP_AXIS,
shortCommit,
shortId,
tookDefault,
useFlowInputs,
varyingKeys,
} from "./queries"
@@ -96,6 +97,8 @@ export function RunsScreen({
const runs: RunRow[] = data?.pages.flat() ?? []
const selected = search.compare ? search.compare.split(",") : []
// Only those the list has read: a linked comparison renders before it has.
const picked = runs.filter((run) => selected.includes(run.id))
const put = (next: string[]) =>
update({
@@ -305,9 +308,7 @@ export function RunsScreen({
{/* Below the chart, and only once there are two runs to hold apart.
The table above keeps its height and the page scrolls instead:
what differs is worth reading in full. */}
{selected.length > 1 && (
<ParamDiff rows={runs.filter((run) => selected.includes(run.id))} />
)}
{picked.length > 1 && <ParamDiff rows={picked} />}
</section>
</div>
)
@@ -359,7 +360,7 @@ export function FlowRail({
>
<span className="min-w-0 flex-1 truncate">{label}</span>
{note && (
<span className="rounded-full border border-border px-1.5 text-[10px]">
<span className="rounded-full border border-border px-1.5 text-xs">
{note}
</span>
)}
@@ -784,16 +785,22 @@ function ParamDiff({ rows }: { rows: RunRow[] }) {
)}
{varying.map((key) => (
<TableCell key={key}>
{key in run.params ? (
<ParamValue value={run.params[key]} />
) : declared.has(key) ? (
// Not "empty": this run was never given the parameter, so
// what it ran with is what the flow declares.
{key in run.params || declared.has(key) ? (
// Not "empty": a run never given the parameter ran with
// what the flow declares, and says so.
<span className="flex items-baseline gap-1.5">
<ParamValue value={declared.get(key)} />
<span className="rounded-full border border-border px-1.5 text-[10px] text-muted-foreground">
default
</span>
<ParamValue
value={
key in run.params
? run.params[key]
: declared.get(key)
}
/>
{tookDefault(run, key) && (
<span className="rounded-full border border-border px-1.5 text-xs text-muted-foreground">
default
</span>
)}
</span>
) : (
<span className="text-muted-foreground text-xs">unset</span>
+11
View File
@@ -321,6 +321,17 @@ export function varyingKeys(runs: { params: Record<string, unknown> }[]) {
})
}
/**
* Whether a run took `name` from its flow rather than from its caller. A run
* from before `passed` was recorded can only say so for a name its params lack.
*/
export function tookDefault(
run: { params: Record<string, unknown>; passed?: string[] | null },
name: string,
): boolean {
return run.passed ? !run.passed.includes(name) : !(name in run.params)
}
/** A parameter value, as narrow as it can be written. */
export function paramText(value: unknown): string {
if (value === null || value === undefined) return "—"