@shipfox/client-workflows 17.0.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-type.log +0 -1
  3. package/CHANGELOG.md +23 -0
  4. package/dist/components/job-detail/job-context-panel.d.ts.map +1 -1
  5. package/dist/components/job-detail/job-context-panel.js +5 -1
  6. package/dist/components/job-detail/job-context-panel.js.map +1 -1
  7. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  8. package/dist/components/job-detail/job-detail-view.js +34 -27
  9. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  10. package/dist/components/job-detail/job-empty-states.d.ts +4 -0
  11. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
  12. package/dist/components/job-detail/job-empty-states.js +43 -3
  13. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  14. package/dist/components/job-detail/step-troubleshooting.js +4 -0
  15. package/dist/components/job-detail/step-troubleshooting.js.map +1 -1
  16. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  17. package/dist/components/step-list/step-list-model.js +1 -0
  18. package/dist/components/step-list/step-list-model.js.map +1 -1
  19. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/job-status-strip.js +20 -7
  21. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  22. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  23. package/dist/components/workflow-run-view/run-workspace-nav.js +6 -5
  24. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  25. package/dist/core/entities/job-execution.d.ts +2 -0
  26. package/dist/core/entities/job-execution.d.ts.map +1 -1
  27. package/dist/core/entities/job-execution.js.map +1 -1
  28. package/dist/core/entities/job.d.ts +6 -3
  29. package/dist/core/entities/job.d.ts.map +1 -1
  30. package/dist/core/entities/job.js +8 -1
  31. package/dist/core/entities/job.js.map +1 -1
  32. package/dist/core/entities/workflow-run.d.ts +7 -4
  33. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  34. package/dist/core/entities/workflow-run.js.map +1 -1
  35. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  36. package/dist/hooks/api/workflow-run-mapper.js +12 -1
  37. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  38. package/dist/tsconfig.test.tsbuildinfo +1 -1
  39. package/package.json +2 -2
  40. package/src/components/job-detail/job-context-panel.tsx +4 -0
  41. package/src/components/job-detail/job-detail-view.tsx +37 -33
  42. package/src/components/job-detail/job-empty-states.test.ts +105 -1
  43. package/src/components/job-detail/job-empty-states.tsx +56 -2
  44. package/src/components/job-detail/step-troubleshooting.tsx +4 -0
  45. package/src/components/job-graph/job-node.test.tsx +2 -2
  46. package/src/components/step-list/step-list-model.ts +1 -0
  47. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  48. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +73 -0
  49. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +31 -0
  50. package/src/components/workflow-run-view/run-workspace-nav.tsx +13 -6
  51. package/src/core/entities/job-execution.ts +2 -0
  52. package/src/core/entities/job-status.test.ts +47 -1
  53. package/src/core/entities/job.ts +18 -4
  54. package/src/core/entities/workflow-run-display.test.ts +28 -0
  55. package/src/core/entities/workflow-run.ts +18 -4
  56. package/src/core/workflow-run.test.ts +13 -0
  57. package/src/hooks/api/workflow-run-mapper.ts +19 -1
  58. package/test/fixtures/workflow-run.ts +32 -12
  59. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 86 files with swc (708.85ms)
2
+ Successfully compiled: 86 files with swc (649.43ms)
@@ -1,2 +1 @@
1
1
  $ shipfox-tsc-check
2
- context canceled
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @shipfox/client-workflows
2
2
 
3
+ ## 19.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 53b87f0: Distinguish queued and executing jobs in workflow run list status strips.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [53b87f0]
12
+ - @shipfox/api-workflows-dto@12.6.0
13
+
14
+ ## 18.0.0
15
+
16
+ ### Minor Changes
17
+
18
+ - 5b1838c: Expose actionable status reasons and messages when materialized job outputs cannot be persisted.
19
+
20
+ ### Patch Changes
21
+
22
+ - cd6fef9: Lets the run job rail use available height and exposes overflow when the job list scrolls.
23
+ - Updated dependencies [5b1838c]
24
+ - @shipfox/api-workflows-dto@12.5.0
25
+
3
26
  ## 17.0.0
4
27
 
5
28
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"job-context-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-context-panel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,GAAG,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAKnF,wBAAgB,eAAe,CAAC,EAAC,GAAG,EAAE,SAAS,EAAC,EAAE;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAC,sCA4BpF"}
1
+ {"version":3,"file":"job-context-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-context-panel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,GAAG,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAKnF,wBAAgB,eAAe,CAAC,EAAC,GAAG,EAAE,SAAS,EAAC,EAAE;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAC,sCA6BpF"}
@@ -16,7 +16,7 @@ export function JobContextPanel({ job, execution }) {
16
16
  ];
17
17
  const statusReason = execution.statusReason ?? job.statusReason;
18
18
  const hasTiming = Boolean(execution.queueTime || execution.runTime);
19
- const hasContext = Boolean(runner?.length || outputs || execution.triggerEvents.length || job.success || statusReason || trace?.length || hasTiming);
19
+ const hasContext = Boolean(runner?.length || outputs || execution.triggerEvents.length || job.success || statusReason || execution.statusReasonMessage || trace?.length || hasTiming);
20
20
  if (!hasContext) return null;
21
21
  return /*#__PURE__*/ _jsx(JobContextSheet, {
22
22
  job: job,
@@ -126,6 +126,10 @@ function JobContextSheet({ job, execution, runner, outputs, trace, statusReason
126
126
  label: "Status reason",
127
127
  value: humanize(statusReason)
128
128
  }) : null,
129
+ execution.statusReasonMessage ? /*#__PURE__*/ _jsx(ContextValue, {
130
+ label: "Failure details",
131
+ value: execution.statusReasonMessage
132
+ }) : null,
129
133
  execution.triggerEvents.length ? /*#__PURE__*/ _jsx(JsonCode, {
130
134
  title: `Trigger events (${execution.triggerEvents.length})`,
131
135
  value: execution.triggerEvents
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-context-panel.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport type {EvaluationTraceEntry, Job, JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\nimport {JsonCode} from './json-code.js';\nimport {EvaluationTrace} from './step-troubleshooting.js';\n\nexport function JobContextPanel({job, execution}: {job: Job; execution: JobExecution}) {\n const runner = execution.runner?.length ? execution.runner : job.runner;\n const outputs = execution.outputs ?? job.outputs;\n const trace = [...(job.evaluationTrace ?? []), ...(execution.evaluationTrace ?? [])];\n const statusReason = execution.statusReason ?? job.statusReason;\n const hasTiming = Boolean(execution.queueTime || execution.runTime);\n const hasContext = Boolean(\n runner?.length ||\n outputs ||\n execution.triggerEvents.length ||\n job.success ||\n statusReason ||\n trace?.length ||\n hasTiming,\n );\n\n if (!hasContext) return null;\n\n return (\n <JobContextSheet\n job={job}\n execution={execution}\n runner={runner}\n outputs={outputs}\n trace={trace}\n statusReason={statusReason}\n />\n );\n}\n\nfunction JobContextSheet({\n job,\n execution,\n runner,\n outputs,\n trace,\n statusReason,\n}: {\n job: Job;\n execution: JobExecution;\n runner: string[] | null;\n outputs: Record<string, unknown> | null;\n trace: EvaluationTraceEntry[];\n statusReason: JobExecution['statusReason'];\n}) {\n const [open, setOpen] = useState(false);\n const {conditionTrace, executionNameTrace} = splitJobEvaluationTrace(trace);\n\n return (\n <Sheet open={open} onOpenChange={setOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Inspect job details\"\n onClick={() => setOpen(true)}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect job details</TooltipContent>\n </Tooltip>\n <SheetContent side=\"right\" className=\"w-full sm:max-w-[560px]\">\n <SheetHeader>\n <SheetTitle>{job.displayName}</SheetTitle>\n <SheetDescription>\n Execution #{execution.sequence} · {execution.displayName}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-section\">\n <div className=\"grid w-full min-w-0 gap-group min-[640px]:grid-cols-2\">\n {runner?.length ? <ContextList label=\"Runner\" values={runner} mono /> : null}\n {execution.queueTime || execution.runTime ? (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n Timing\n </Text>\n <div className=\"flex flex-wrap gap-inline text-xs text-foreground-neutral-muted\">\n {execution.queueTime ? (\n <span>Queue {formatJobExecutionTime(execution.queueTime)}</span>\n ) : null}\n {execution.runTime ? (\n <span>Run {formatJobExecutionTime(execution.runTime)}</span>\n ) : null}\n </div>\n </div>\n ) : null}\n {outputs ? (\n <JsonCode\n title=\"Outputs\"\n value={outputs}\n emptyMessage=\"No outputs declared; the `outputs:` mapping is empty.\"\n />\n ) : null}\n {job.success ? <ContextValue label=\"Condition\" value={job.success} mono /> : null}\n {statusReason ? (\n <ContextValue label=\"Status reason\" value={humanize(statusReason)} />\n ) : null}\n {execution.triggerEvents.length ? (\n <JsonCode\n title={`Trigger events (${execution.triggerEvents.length})`}\n value={execution.triggerEvents}\n />\n ) : null}\n {executionNameTrace.length ? (\n <EvaluationTraceSection\n title={`Execution name evaluation (${executionNameTrace.length})`}\n trace={executionNameTrace}\n />\n ) : null}\n {conditionTrace.length ? (\n <EvaluationTraceSection\n title={`Condition evaluation (${conditionTrace.length})`}\n trace={conditionTrace}\n />\n ) : null}\n </div>\n </SheetBody>\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction EvaluationTraceSection({\n title,\n trace,\n}: {\n title: string;\n trace: readonly EvaluationTraceEntry[];\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {title}\n </Text>\n <EvaluationTrace trace={trace} />\n </div>\n );\n}\n\nfunction ContextValue({\n label,\n value,\n mono = false,\n}: {\n label: string;\n value: string;\n mono?: boolean;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Text>\n <Text\n size=\"xs\"\n className={\n mono\n ? 'break-words font-code text-foreground-neutral-muted'\n : 'text-foreground-neutral-muted'\n }\n >\n {value}\n </Text>\n </div>\n );\n}\n\nfunction ContextList({\n label,\n values,\n mono = false,\n}: {\n label: string;\n values: string[];\n mono?: boolean;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Text>\n <div className=\"flex min-w-0 flex-wrap gap-tight\">\n {values.map((value) => (\n <Code\n key={value}\n as=\"span\"\n variant=\"label\"\n className={mono ? 'text-foreground-neutral-muted' : 'text-foreground-neutral-base'}\n >\n {value}\n </Code>\n ))}\n </div>\n </div>\n );\n}\n\nfunction humanize(value: string): string {\n return value.replaceAll('_', ' ').replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction splitJobEvaluationTrace(trace: readonly EvaluationTraceEntry[]): {\n conditionTrace: EvaluationTraceEntry[];\n executionNameTrace: EvaluationTraceEntry[];\n} {\n const conditionTrace: EvaluationTraceEntry[] = [];\n const executionNameTrace: EvaluationTraceEntry[] = [];\n\n for (const entry of trace) {\n if (!('dropped' in entry) && entry.field === 'job.execution_name') {\n executionNameTrace.push(entry);\n } else {\n conditionTrace.push(entry);\n }\n }\n\n return {conditionTrace, executionNameTrace};\n}\n"],"names":["Icon","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Tooltip","TooltipContent","TooltipTrigger","Code","Text","useState","formatJobExecutionTime","JsonCode","EvaluationTrace","JobContextPanel","job","execution","runner","length","outputs","trace","evaluationTrace","statusReason","hasTiming","Boolean","queueTime","runTime","hasContext","triggerEvents","success","JobContextSheet","open","setOpen","conditionTrace","executionNameTrace","splitJobEvaluationTrace","onOpenChange","asChild","button","type","aria-label","onClick","className","name","size","aria-hidden","side","displayName","sequence","div","ContextList","label","values","mono","bold","span","title","value","emptyMessage","ContextValue","humanize","EvaluationTraceSection","map","as","variant","replaceAll","replace","letter","toUpperCase","entry","field","push"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-context-panel.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {\n Sheet,\n SheetBody,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '@shipfox/react-ui/sheet';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {useState} from 'react';\nimport type {EvaluationTraceEntry, Job, JobExecution} from '#core/workflow-run.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\nimport {JsonCode} from './json-code.js';\nimport {EvaluationTrace} from './step-troubleshooting.js';\n\nexport function JobContextPanel({job, execution}: {job: Job; execution: JobExecution}) {\n const runner = execution.runner?.length ? execution.runner : job.runner;\n const outputs = execution.outputs ?? job.outputs;\n const trace = [...(job.evaluationTrace ?? []), ...(execution.evaluationTrace ?? [])];\n const statusReason = execution.statusReason ?? job.statusReason;\n const hasTiming = Boolean(execution.queueTime || execution.runTime);\n const hasContext = Boolean(\n runner?.length ||\n outputs ||\n execution.triggerEvents.length ||\n job.success ||\n statusReason ||\n execution.statusReasonMessage ||\n trace?.length ||\n hasTiming,\n );\n\n if (!hasContext) return null;\n\n return (\n <JobContextSheet\n job={job}\n execution={execution}\n runner={runner}\n outputs={outputs}\n trace={trace}\n statusReason={statusReason}\n />\n );\n}\n\nfunction JobContextSheet({\n job,\n execution,\n runner,\n outputs,\n trace,\n statusReason,\n}: {\n job: Job;\n execution: JobExecution;\n runner: string[] | null;\n outputs: Record<string, unknown> | null;\n trace: EvaluationTraceEntry[];\n statusReason: JobExecution['statusReason'];\n}) {\n const [open, setOpen] = useState(false);\n const {conditionTrace, executionNameTrace} = splitJobEvaluationTrace(trace);\n\n return (\n <Sheet open={open} onOpenChange={setOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Inspect job details\"\n onClick={() => setOpen(true)}\n className=\"flex size-28 shrink-0 items-center justify-center rounded-4 bg-transparent text-foreground-neutral-muted outline-none transition-colors hover:bg-transparent hover:text-foreground-neutral-base active:bg-transparent focus-visible:shadow-button-neutral-focus\"\n >\n <Icon name=\"informationLine\" size={14} aria-hidden=\"true\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>Inspect job details</TooltipContent>\n </Tooltip>\n <SheetContent side=\"right\" className=\"w-full sm:max-w-[560px]\">\n <SheetHeader>\n <SheetTitle>{job.displayName}</SheetTitle>\n <SheetDescription>\n Execution #{execution.sequence} · {execution.displayName}\n </SheetDescription>\n </SheetHeader>\n <SheetBody className=\"gap-section\">\n <div className=\"grid w-full min-w-0 gap-group min-[640px]:grid-cols-2\">\n {runner?.length ? <ContextList label=\"Runner\" values={runner} mono /> : null}\n {execution.queueTime || execution.runTime ? (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n Timing\n </Text>\n <div className=\"flex flex-wrap gap-inline text-xs text-foreground-neutral-muted\">\n {execution.queueTime ? (\n <span>Queue {formatJobExecutionTime(execution.queueTime)}</span>\n ) : null}\n {execution.runTime ? (\n <span>Run {formatJobExecutionTime(execution.runTime)}</span>\n ) : null}\n </div>\n </div>\n ) : null}\n {outputs ? (\n <JsonCode\n title=\"Outputs\"\n value={outputs}\n emptyMessage=\"No outputs declared; the `outputs:` mapping is empty.\"\n />\n ) : null}\n {job.success ? <ContextValue label=\"Condition\" value={job.success} mono /> : null}\n {statusReason ? (\n <ContextValue label=\"Status reason\" value={humanize(statusReason)} />\n ) : null}\n {execution.statusReasonMessage ? (\n <ContextValue label=\"Failure details\" value={execution.statusReasonMessage} />\n ) : null}\n {execution.triggerEvents.length ? (\n <JsonCode\n title={`Trigger events (${execution.triggerEvents.length})`}\n value={execution.triggerEvents}\n />\n ) : null}\n {executionNameTrace.length ? (\n <EvaluationTraceSection\n title={`Execution name evaluation (${executionNameTrace.length})`}\n trace={executionNameTrace}\n />\n ) : null}\n {conditionTrace.length ? (\n <EvaluationTraceSection\n title={`Condition evaluation (${conditionTrace.length})`}\n trace={conditionTrace}\n />\n ) : null}\n </div>\n </SheetBody>\n </SheetContent>\n </Sheet>\n );\n}\n\nfunction EvaluationTraceSection({\n title,\n trace,\n}: {\n title: string;\n trace: readonly EvaluationTraceEntry[];\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {title}\n </Text>\n <EvaluationTrace trace={trace} />\n </div>\n );\n}\n\nfunction ContextValue({\n label,\n value,\n mono = false,\n}: {\n label: string;\n value: string;\n mono?: boolean;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Text>\n <Text\n size=\"xs\"\n className={\n mono\n ? 'break-words font-code text-foreground-neutral-muted'\n : 'text-foreground-neutral-muted'\n }\n >\n {value}\n </Text>\n </div>\n );\n}\n\nfunction ContextList({\n label,\n values,\n mono = false,\n}: {\n label: string;\n values: string[];\n mono?: boolean;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-tight\">\n <Text size=\"xs\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Text>\n <div className=\"flex min-w-0 flex-wrap gap-tight\">\n {values.map((value) => (\n <Code\n key={value}\n as=\"span\"\n variant=\"label\"\n className={mono ? 'text-foreground-neutral-muted' : 'text-foreground-neutral-base'}\n >\n {value}\n </Code>\n ))}\n </div>\n </div>\n );\n}\n\nfunction humanize(value: string): string {\n return value.replaceAll('_', ' ').replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction splitJobEvaluationTrace(trace: readonly EvaluationTraceEntry[]): {\n conditionTrace: EvaluationTraceEntry[];\n executionNameTrace: EvaluationTraceEntry[];\n} {\n const conditionTrace: EvaluationTraceEntry[] = [];\n const executionNameTrace: EvaluationTraceEntry[] = [];\n\n for (const entry of trace) {\n if (!('dropped' in entry) && entry.field === 'job.execution_name') {\n executionNameTrace.push(entry);\n } else {\n conditionTrace.push(entry);\n }\n }\n\n return {conditionTrace, executionNameTrace};\n}\n"],"names":["Icon","Sheet","SheetBody","SheetContent","SheetDescription","SheetHeader","SheetTitle","Tooltip","TooltipContent","TooltipTrigger","Code","Text","useState","formatJobExecutionTime","JsonCode","EvaluationTrace","JobContextPanel","job","execution","runner","length","outputs","trace","evaluationTrace","statusReason","hasTiming","Boolean","queueTime","runTime","hasContext","triggerEvents","success","statusReasonMessage","JobContextSheet","open","setOpen","conditionTrace","executionNameTrace","splitJobEvaluationTrace","onOpenChange","asChild","button","type","aria-label","onClick","className","name","size","aria-hidden","side","displayName","sequence","div","ContextList","label","values","mono","bold","span","title","value","emptyMessage","ContextValue","humanize","EvaluationTraceSection","map","as","variant","replaceAll","replace","letter","toUpperCase","entry","field","push"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAwB,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AAwB3B,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,iBAAiB,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,iBAAiB,GAClB,EAAE,kBAAkB,+BAqQpB"}
1
+ {"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAwB,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AAyB3B,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,iBAAiB,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,iBAAiB,GAClB,EAAE,kBAAkB,+BAwQpB"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.
3
3
  // biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.
4
4
  import { ApiError } from '@shipfox/client-api';
@@ -19,7 +19,7 @@ import { StepList } from '../step-list/index.js';
19
19
  import { WorkflowRunNotFound, WorkflowRunStaleError } from '../workflow-run-view/workflow-run-states.js';
20
20
  import { JobContextPanel } from './job-context-panel.js';
21
21
  import { JobDetailHeader } from './job-detail-header.js';
22
- import { CarriedOverStepPanel, emptyStateForJob, emptyStateForMissingExecution, jobSucceededSummary } from './job-empty-states.js';
22
+ import { CarriedOverStepPanel, emptyStateForJob, emptyStateForMissingExecution, jobSucceededSummary, MaterializedOutputFailureNotice } from './job-empty-states.js';
23
23
  import { resolveWorkflowJobSelection, workflowJobLandingSelection } from './job-selection.js';
24
24
  import { StepAttemptLogPanel } from './step-attempt-log-panel.js';
25
25
  import { StepInspectorSheet } from './step-troubleshooting.js';
@@ -208,33 +208,40 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
208
208
  className: "sr-only",
209
209
  children: "Logs"
210
210
  }),
211
- selectedJobExecution ? /*#__PURE__*/ _jsx(StepList, {
212
- job: job,
213
- jobExecution: selectedJobExecution,
214
- selectedAttemptId: selectedAttemptId,
215
- defaultSelectedAttemptId: landingSelection?.attemptId,
216
- onSelectedAttemptChange: selectAttempt,
217
- inspectorOpenAttemptId: inspectorOpenAttemptId,
218
- onInspectorOpenChange: onInspectorOpenChange,
219
- autoSelectActiveAttempt: true,
220
- emptyState: emptyStateForJob(job, selectedJobExecution),
221
- showHeader: false,
222
- className: "rounded-none border-0 bg-transparent",
223
- renderExpandedStep: (context)=>/*#__PURE__*/ _jsx(ExpandedStep, {
224
- context: context,
225
- pageScrollRef: pageScrollRef
211
+ selectedJobExecution ? /*#__PURE__*/ _jsxs(_Fragment, {
212
+ children: [
213
+ /*#__PURE__*/ _jsx(MaterializedOutputFailureNotice, {
214
+ jobExecution: selectedJobExecution
226
215
  }),
227
- renderInspector: (entry)=>/*#__PURE__*/ _jsx(StepInspectorSheet, {
228
- entry: entry,
229
- open: true,
230
- onOpenChange: (open)=>onInspectorOpenChange(open ? entry.id : null),
231
- workspaceSlug: workspaceSlug,
232
- projectSlug: projectSlug,
233
- workflowRunId: run.id,
234
- runAttempt: run.runAttempt.attempt,
235
- jobId: job.id,
236
- annotationCount: annotationCountForStep(annotationSummaryQuery.data, entry.step.id, entry.attempt)
216
+ /*#__PURE__*/ _jsx(StepList, {
217
+ job: job,
218
+ jobExecution: selectedJobExecution,
219
+ selectedAttemptId: selectedAttemptId,
220
+ defaultSelectedAttemptId: landingSelection?.attemptId,
221
+ onSelectedAttemptChange: selectAttempt,
222
+ inspectorOpenAttemptId: inspectorOpenAttemptId,
223
+ onInspectorOpenChange: onInspectorOpenChange,
224
+ autoSelectActiveAttempt: true,
225
+ emptyState: emptyStateForJob(job, selectedJobExecution),
226
+ showHeader: false,
227
+ className: "rounded-none border-0 bg-transparent",
228
+ renderExpandedStep: (context)=>/*#__PURE__*/ _jsx(ExpandedStep, {
229
+ context: context,
230
+ pageScrollRef: pageScrollRef
231
+ }),
232
+ renderInspector: (entry)=>/*#__PURE__*/ _jsx(StepInspectorSheet, {
233
+ entry: entry,
234
+ open: true,
235
+ onOpenChange: (open)=>onInspectorOpenChange(open ? entry.id : null),
236
+ workspaceSlug: workspaceSlug,
237
+ projectSlug: projectSlug,
238
+ workflowRunId: run.id,
239
+ runAttempt: run.runAttempt.attempt,
240
+ jobId: job.id,
241
+ annotationCount: annotationCountForStep(annotationSummaryQuery.data, entry.step.id, entry.attempt)
242
+ })
237
243
  })
244
+ ]
238
245
  }) : /*#__PURE__*/ _jsx(EmptyStateForMissingExecution, {
239
246
  job: job
240
247
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  import type { Job, JobExecution, Step, StepError } from '#core/workflow-run.js';
2
2
  import type { StepListEmptyState } from '../step-list/index.js';
3
+ export declare function outputFailureDescriptionForExecution(jobExecution: JobExecution): string | undefined;
4
+ export declare function MaterializedOutputFailureNotice({ jobExecution }: {
5
+ jobExecution: JobExecution;
6
+ }): import("react").JSX.Element | null;
3
7
  export declare function emptyStateForJob(job: Job, jobExecution: JobExecution): StepListEmptyState | undefined;
4
8
  export declare function emptyStateForMissingExecution(job: Job): StepListEmptyState;
5
9
  export declare function skippedJobDescription(reason: Job['statusReason']): string;
@@ -1 +1 @@
1
- {"version":3,"file":"job-empty-states.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-empty-states.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAO9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAG9D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,YAAY,GACzB,kBAAkB,GAAG,SAAS,CAkEhC;AAED,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,GAAG,GAAG,kBAAkB,CA8D1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,GAAG,MAAM,CAoBzE;AAmCD,wBAAgB,oBAAoB,gCAUnC;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACpC,SAAS,GAAG,IAAI,CAgClB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMzF"}
1
+ {"version":3,"file":"job-empty-states.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-empty-states.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAO9E,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAQ9D,wBAAgB,oCAAoC,CAClD,YAAY,EAAE,YAAY,GACzB,MAAM,GAAG,SAAS,CAUpB;AAED,wBAAgB,+BAA+B,CAAC,EAAC,YAAY,EAAC,EAAE;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,sCAiB3F;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,YAAY,GACzB,kBAAkB,GAAG,SAAS,CAsEhC;AAED,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,GAAG,GAAG,kBAAkB,CA8D1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,GAAG,MAAM,CAuBzE;AA2CD,wBAAgB,oBAAoB,gCAUnC;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACpC,SAAS,GAAG,IAAI,CAgClB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMzF"}
@@ -1,7 +1,40 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Callout, CalloutContent, CalloutDescription, CalloutTitle } from '@shipfox/react-ui/callout';
2
3
  import { EmptyState } from '@shipfox/react-ui/empty-state';
3
4
  import { AGENT_CONFIG_ISSUES, deriveJobDisplayStatus, deriveJobExecutionDisplayStatus, STEP_ERROR_REASONS } from '#core/workflow-run.js';
4
5
  import { formatJobExecutionTime } from './job-execution-time-text.js';
6
+ const MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION = 'A materialized job output could not be persisted: it exceeded a size or entry cap, contained a non-JSON-safe value, or referenced an unresolved value. Check the output mapping and values before re-running the workflow.';
7
+ const OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION = 'The materialized job output exceeded its configured size limit. Review the failure details before re-running the workflow.';
8
+ export function outputFailureDescriptionForExecution(jobExecution) {
9
+ if (jobExecution.steps.length === 0) return undefined;
10
+ if (jobExecution.statusReason === 'output_invalid') {
11
+ return jobExecution.statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;
12
+ }
13
+ if (jobExecution.statusReason === 'output_too_large') {
14
+ return jobExecution.statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;
15
+ }
16
+ return undefined;
17
+ }
18
+ export function MaterializedOutputFailureNotice({ jobExecution }) {
19
+ const description = outputFailureDescriptionForExecution(jobExecution);
20
+ if (!description) return null;
21
+ return /*#__PURE__*/ _jsx(Callout, {
22
+ role: "alert",
23
+ type: "error",
24
+ variant: "secondary",
25
+ className: "border-b border-border-neutral-base px-row py-row",
26
+ children: /*#__PURE__*/ _jsxs(CalloutContent, {
27
+ children: [
28
+ /*#__PURE__*/ _jsx(CalloutTitle, {
29
+ children: "Job output could not be persisted"
30
+ }),
31
+ /*#__PURE__*/ _jsx(CalloutDescription, {
32
+ children: description
33
+ })
34
+ ]
35
+ })
36
+ });
37
+ }
5
38
  export function emptyStateForJob(job, jobExecution) {
6
39
  if (job.carriedOver) {
7
40
  return {
@@ -43,7 +76,7 @@ export function emptyStateForJob(job, jobExecution) {
43
76
  if (displayStatus === 'failed') {
44
77
  return {
45
78
  title: 'Job failed before its first step started',
46
- description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason, runner),
79
+ description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason, runner, jobExecution.statusReasonMessage),
47
80
  status: displayStatus
48
81
  };
49
82
  }
@@ -127,13 +160,16 @@ export function skippedJobDescription(reason) {
127
160
  case 'run_cancelled':
128
161
  case 'timed_out':
129
162
  case 'runner_lost':
163
+ case 'output_invalid':
130
164
  case 'step_failed':
131
165
  case 'unknown':
132
166
  case null:
133
167
  return 'This job did not start.';
168
+ case 'output_too_large':
169
+ return 'The materialized job output exceeded its configured size limit.';
134
170
  }
135
171
  }
136
- function preStepFailureDescription(reason, runner = null) {
172
+ function preStepFailureDescription(reason, runner = null, statusReasonMessage = null) {
137
173
  const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';
138
174
  switch(reason){
139
175
  case 'runner_lost':
@@ -145,6 +181,10 @@ function preStepFailureDescription(reason, runner = null) {
145
181
  return 'The execution ended while the run was being cancelled.';
146
182
  case 'step_failed':
147
183
  return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;
184
+ case 'output_too_large':
185
+ return statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;
186
+ case 'output_invalid':
187
+ return statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;
148
188
  case 'condition_errored':
149
189
  return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';
150
190
  case 'dependency_not_completed':
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',\n description: runner?.length\n ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.`\n : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(jobExecution.statusReason ?? job.statusReason, runner),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n if (job.status === 'failed') {\n return {\n title: 'Job failed before an execution was created',\n description: preStepFailureDescription(job.statusReason, job.runner),\n status: 'failed',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n }\n}\n\nfunction preStepFailureDescription(reason: string | null, runner: string[] | null = null): string {\n const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';\n\n switch (reason) {\n case 'runner_lost':\n return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;\n case 'timed_out':\n return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","emptyStateForJob","job","jobExecution","carriedOver","title","description","status","displayStatus","mode","runner","length","join","preStepFailureDescription","statusReason","sequence","undefined","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","runnerCopy","humanizeFailureReason","replaceAll","CarriedOverStepPanel","className","icon","variant","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","type","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","steps","filter","duration","displayDuration"],"mappings":";AAAA,SAAQA,UAAU,QAAO,gCAAgC;AAEzD,SACEC,mBAAmB,EACnBC,sBAAsB,EACtBC,+BAA+B,EAC/BC,kBAAkB,QACb,wBAAwB;AAE/B,SAAQC,sBAAsB,QAAO,+BAA+B;AAEpE,OAAO,SAASC,iBACdC,GAAQ,EACRC,YAA0B;IAE1B,IAAID,IAAIE,WAAW,EAAE;QACnB,OAAO;YACLC,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,MAAMC,gBACJN,IAAIO,IAAI,KAAK,cACTX,gCAAgCK,gBAChCN,uBAAuBK;IAC7B,MAAMQ,SAASP,aAAaO,MAAM,EAAEC,SAASR,aAAaO,MAAM,GAAGR,IAAIQ,MAAM;IAE7E,IAAIP,aAAaI,MAAM,KAAK,aAAaC,kBAAkB,WAAW;QACpE,OAAO;YACLH,OAAOK,QAAQC,SAAS,yBAAyB;YACjDL,aAAaI,QAAQC,SACjB,CAAC,OAAO,EAAED,OAAOE,IAAI,CAAC,MAAM,+DAA+D,CAAC,GAC5F;YACJL,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,WAAW;QAC/B,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,aAAa;QACjC,OAAO;YACLH,OAAO;YACPC,aAAa;YACbC,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,UAAU;QAC9B,OAAO;YACLH,OAAO;YACPC,aAAaO,0BAA0BV,aAAaW,YAAY,IAAIZ,IAAIY,YAAY,EAAEJ;YACtFH,QAAQC;QACV;IACF;IAEA,IAAIA,kBAAkB,aAAa;QACjC,OAAO;YACLH,OAAO;YACPC,aAAa,CAAC,WAAW,EAAEH,aAAaY,QAAQ,CAAC,+CAA+C,CAAC;YACjGR,QAAQC;QACV;IACF;IAEA,OAAOQ;AACT;AAEA,OAAO,SAASC,8BAA8Bf,GAAQ;IACpD,IAAIA,IAAIE,WAAW,EAAE;QACnB,OAAO;YACLC,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIO,IAAI,KAAK,eAAeP,IAAIgB,cAAc,KAAK,aAAa;QAClE,OAAO;YACLb,OAAO;YACPC,aAAa;YACbC,QAAQV,uBAAuBK;QACjC;IACF;IAEA,IAAIA,IAAIO,IAAI,KAAK,eAAeP,IAAIgB,cAAc,KAAK,YAAY;QACjE,OAAO;YACLb,OAAO;YACPC,aAAa;YACbC,QAAQL,IAAIK,MAAM;QACpB;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLF,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,WAAW;QAC5B,OAAO;YACLF,OAAO;YACPC,aAAaa,sBAAsBjB,IAAIY,YAAY;YACnDP,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,aAAa;QAC9B,OAAO;YACLF,OAAO;YACPC,aAAa;YACbC,QAAQ;QACV;IACF;IAEA,IAAIL,IAAIK,MAAM,KAAK,UAAU;QAC3B,OAAO;YACLF,OAAO;YACPC,aAAaO,0BAA0BX,IAAIY,YAAY,EAAEZ,IAAIQ,MAAM;YACnEH,QAAQ;QACV;IACF;IAEA,OAAO;QACLF,OAAO;QACPC,aAAa;QACbC,QAAQL,IAAIK,MAAM;IACpB;AACF;AAEA,OAAO,SAASY,sBAAsBC,MAA2B;IAC/D,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAO;IACX;AACF;AAEA,SAASP,0BAA0BO,MAAqB,EAAEV,SAA0B,IAAI;IACtF,MAAMW,aAAaX,QAAQC,SAAS,CAAC,yBAAyB,EAAED,OAAOE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG;IAEvF,OAAQQ;QACN,KAAK;YACH,OAAO,CAAC,gDAAgD,EAAEC,WAAW,0DAA0D,CAAC;QAClI,KAAK;YACH,OAAO,CAAC,oCAAoC,EAAEA,WAAW,gFAAgF,CAAC;QAC5I,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO,CAAC,uDAAuD,EAAEA,WAAW,uDAAuD,CAAC;QACtI,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO,CAAC,wCAAwC,EAAEA,WAAW,kEAAkE,CAAC;QAClI;YACE,OAAO,CAAC,4BAA4B,EAAEC,sBAAsBF,QAAQ,CAAC,EAAEC,WAAW,uDAAuD,CAAC;IAC9I;AACF;AAEA,SAASC,sBAAsBF,MAAc;IAC3C,OAAOA,OAAOG,UAAU,CAAC,KAAK,KAAKA,UAAU,CAAC,KAAK;AACrD;AAEA,OAAO,SAASC;IACd,qBACE,KAAC7B;QACC8B,WAAU;QACVC,MAAK;QACLrB,OAAM;QACNC,aAAY;QACZqB,SAAQ;;AAGd;AAEA,OAAO,SAASC,uBACdC,IAAU,EACVC,KAAqC;IAErC,IAAIA,UAAU,MAAM,OAAO;IAE3B,MAAMC,YAAYD,MAAMV,MAAM;IAC9B,MAAMY,eACJ,OAAOD,cAAc,YACrBhC,mBAAmBkC,GAAG,CAACF,aAClBA,YACDf;IACN,MAAMkB,sBAAsBJ,MAAMK,gBAAgB,IAAIL,MAAMM,kBAAkB;IAC9E,MAAMD,mBACJ,OAAOD,wBAAwB,YAC/BtC,oBAAoBqC,GAAG,CAACC,uBACnBA,sBACDlB;IACN,MAAMqB,WAAWP,MAAMO,QAAQ,IAAIP,MAAMQ,SAAS;IAClD,MAAMC,iBAAiBP,eACnBA,eACAG,mBACE,yBACAnB;IAEN,IAAIuB,mBAAmBvB,WAAW,OAAO;IAEzC,OAAO;QACLwB,SAAS,OAAOV,MAAMU,OAAO,KAAK,WAAWV,MAAMU,OAAO,GAAG;QAC7DH,UAAUA,aAAa,QAAQ,OAAOA,aAAa,WAAWA,WAAW;QACzEI,QAAQ,OAAOX,MAAMW,MAAM,KAAK,WAAWX,MAAMW,MAAM,GAAGzB;QAC1DI,QAAQmB;QACRJ;QACAO,UAAUb,KAAKc,IAAI,KAAK,WAAWd,KAAKc,IAAI,KAAK,aAAa,UAAU;IAC1E;AACF;AAEA,OAAO,SAASC,qBAAqBf,IAAU,EAAEC,KAAuB;IACtE,OAAOD,KAAKc,IAAI,KAAK,WAAWb,OAAOV,WAAW;AACpD;AAEA,OAAO,SAASyB,oBAAoB3C,GAAQ,EAAE4C,SAAuB;IACnE,IAAI5C,IAAIE,WAAW,IAAI0C,UAAUvC,MAAM,KAAK,aAAa,OAAOS;IAChE,MAAM+B,YAAYD,UAAUE,KAAK,CAACC,MAAM,CAAC,CAACpB,OAASA,KAAKtB,MAAM,KAAK,aAAaI,MAAM;IACtF,IAAIoC,cAAc,GAAG,OAAO/B;IAC5B,MAAMkC,WAAWJ,UAAUK,eAAe;IAC1C,OAAO,GAAGJ,UAAU,KAAK,EAAEA,cAAc,IAAI,KAAK,IAAI,UAAU,EAAEG,WAAW,CAAC,IAAI,EAAElD,uBAAuBkD,WAAW,GAAG,IAAI;AAC/H"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {Callout, CalloutContent, CalloutDescription, CalloutTitle} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nconst MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION =\n 'A materialized job output could not be persisted: it exceeded a size or entry cap, contained a non-JSON-safe value, or referenced an unresolved value. Check the output mapping and values before re-running the workflow.';\nconst OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION =\n 'The materialized job output exceeded its configured size limit. Review the failure details before re-running the workflow.';\n\nexport function outputFailureDescriptionForExecution(\n jobExecution: JobExecution,\n): string | undefined {\n if (jobExecution.steps.length === 0) return undefined;\n\n if (jobExecution.statusReason === 'output_invalid') {\n return jobExecution.statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n }\n if (jobExecution.statusReason === 'output_too_large') {\n return jobExecution.statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n }\n return undefined;\n}\n\nexport function MaterializedOutputFailureNotice({jobExecution}: {jobExecution: JobExecution}) {\n const description = outputFailureDescriptionForExecution(jobExecution);\n if (!description) return null;\n\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"border-b border-border-neutral-base px-row py-row\"\n >\n <CalloutContent>\n <CalloutTitle>Job output could not be persisted</CalloutTitle>\n <CalloutDescription>{description}</CalloutDescription>\n </CalloutContent>\n </Callout>\n );\n}\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',\n description: runner?.length\n ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.`\n : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(\n jobExecution.statusReason ?? job.statusReason,\n runner,\n jobExecution.statusReasonMessage,\n ),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n if (job.status === 'failed') {\n return {\n title: 'Job failed before an execution was created',\n description: preStepFailureDescription(job.statusReason, job.runner),\n status: 'failed',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'output_invalid':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n case 'output_too_large':\n return 'The materialized job output exceeded its configured size limit.';\n }\n}\n\nfunction preStepFailureDescription(\n reason: string | null,\n runner: string[] | null = null,\n statusReasonMessage: string | null = null,\n): string {\n const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';\n\n switch (reason) {\n case 'runner_lost':\n return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;\n case 'timed_out':\n return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;\n case 'output_too_large':\n return statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n case 'output_invalid':\n return statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["Callout","CalloutContent","CalloutDescription","CalloutTitle","EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION","OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION","outputFailureDescriptionForExecution","jobExecution","steps","length","undefined","statusReason","statusReasonMessage","MaterializedOutputFailureNotice","description","role","type","variant","className","emptyStateForJob","job","carriedOver","title","status","displayStatus","mode","runner","join","preStepFailureDescription","sequence","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","runnerCopy","humanizeFailureReason","replaceAll","CarriedOverStepPanel","icon","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","filter","duration","displayDuration"],"mappings":"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"}
@@ -408,6 +408,8 @@ function failureTitle(reason) {
408
408
  return 'Agent harness was unavailable';
409
409
  case 'runner_lost':
410
410
  return 'Runner stopped responding';
411
+ case 'output_too_large':
412
+ return 'Job output exceeded its size limit';
411
413
  case 'timed_out':
412
414
  return 'Step timed out';
413
415
  case 'step_failed':
@@ -454,6 +456,8 @@ function failureDescription(reason) {
454
456
  return 'The runner could not start the agent harness.';
455
457
  case 'runner_lost':
456
458
  return 'The runner stopped responding before the step completed.';
459
+ case 'output_too_large':
460
+ return 'The materialized job output exceeded its configured size limit.';
457
461
  case 'timed_out':
458
462
  return 'The step exceeded its configured time limit.';
459
463
  case 'dependency_not_completed':