@shipfox/client-workflows 15.0.0 → 17.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 (225) hide show
  1. package/.storybook/preview.css +4 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +41 -0
  5. package/dist/components/identifier/identifier.js +2 -2
  6. package/dist/components/identifier/identifier.js.map +1 -1
  7. package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
  8. package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
  9. package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
  10. package/dist/components/job-detail/job-context-panel.d.ts +6 -0
  11. package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
  12. package/dist/components/job-detail/job-context-panel.js +224 -0
  13. package/dist/components/job-detail/job-context-panel.js.map +1 -0
  14. package/dist/components/job-detail/job-detail-header.d.ts +10 -1
  15. package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
  16. package/dist/components/job-detail/job-detail-header.js +58 -39
  17. package/dist/components/job-detail/job-detail-header.js.map +1 -1
  18. package/dist/components/job-detail/job-detail-view.d.ts +1 -1
  19. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  20. package/dist/components/job-detail/job-detail-view.js +92 -41
  21. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  22. package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
  23. package/dist/components/job-detail/job-empty-states.js +19 -10
  24. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  25. package/dist/components/job-detail/job-execution-switcher.js +4 -4
  26. package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
  27. package/dist/components/job-detail/json-code.d.ts +14 -0
  28. package/dist/components/job-detail/json-code.d.ts.map +1 -0
  29. package/dist/components/job-detail/json-code.js +93 -0
  30. package/dist/components/job-detail/json-code.js.map +1 -0
  31. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  32. package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
  33. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  34. package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
  35. package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
  36. package/dist/components/job-detail/step-troubleshooting.js +493 -0
  37. package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
  38. package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
  39. package/dist/components/job-graph/job-graph-content.js +5 -4
  40. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  41. package/dist/components/job-graph/job-node.js +3 -3
  42. package/dist/components/job-graph/job-node.js.map +1 -1
  43. package/dist/components/step-list/index.d.ts +1 -0
  44. package/dist/components/step-list/index.d.ts.map +1 -1
  45. package/dist/components/step-list/index.js.map +1 -1
  46. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  47. package/dist/components/step-list/step-list-model.js +5 -0
  48. package/dist/components/step-list/step-list-model.js.map +1 -1
  49. package/dist/components/step-list/step-list.d.ts +5 -1
  50. package/dist/components/step-list/step-list.d.ts.map +1 -1
  51. package/dist/components/step-list/step-list.js +117 -82
  52. package/dist/components/step-list/step-list.js.map +1 -1
  53. package/dist/components/workflow-run-duration-label.js +1 -1
  54. package/dist/components/workflow-run-duration-label.js.map +1 -1
  55. package/dist/components/workflow-run-list/job-status-strip.js +6 -6
  56. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  57. package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
  58. package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
  59. package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
  60. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  61. package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
  62. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  63. package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
  64. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  65. package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
  66. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  67. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
  68. package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
  69. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -1
  70. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  71. package/dist/components/workflow-run-summary/workflow-run-summary.js +9 -13
  72. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  73. package/dist/components/workflow-run-tabs/index.d.ts +3 -0
  74. package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
  75. package/dist/components/workflow-run-tabs/index.js +3 -0
  76. package/dist/components/workflow-run-tabs/index.js.map +1 -1
  77. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
  78. package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
  79. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
  80. package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
  81. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
  82. package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
  83. package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
  84. package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
  85. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +39 -0
  86. package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
  87. package/dist/components/workflow-run-tabs/run-annotation-list.js +211 -0
  88. package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
  89. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
  90. package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
  91. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +90 -25
  92. package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
  93. package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
  94. package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
  95. package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
  96. package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
  97. package/dist/components/workflow-run-view/run-workspace-nav.d.ts +1 -1
  98. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  99. package/dist/components/workflow-run-view/run-workspace-nav.js +16 -10
  100. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  101. package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
  102. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  103. package/dist/components/workflow-run-view/workflow-run-view.d.ts +1 -3
  104. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  105. package/dist/components/workflow-run-view/workflow-run-view.js +217 -59
  106. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  107. package/dist/core/entities/job-execution.d.ts +7 -0
  108. package/dist/core/entities/job-execution.d.ts.map +1 -1
  109. package/dist/core/entities/job-execution.js.map +1 -1
  110. package/dist/core/entities/job.d.ts +9 -0
  111. package/dist/core/entities/job.d.ts.map +1 -1
  112. package/dist/core/entities/job.js.map +1 -1
  113. package/dist/core/entities/step-attempt.d.ts +29 -0
  114. package/dist/core/entities/step-attempt.d.ts.map +1 -1
  115. package/dist/core/entities/step-attempt.js.map +1 -1
  116. package/dist/core/entities/step.d.ts +3 -1
  117. package/dist/core/entities/step.d.ts.map +1 -1
  118. package/dist/core/entities/step.js.map +1 -1
  119. package/dist/core/run-annotation.d.ts +79 -0
  120. package/dist/core/run-annotation.d.ts.map +1 -0
  121. package/dist/core/run-annotation.js +125 -0
  122. package/dist/core/run-annotation.js.map +1 -0
  123. package/dist/core/workflow-run.d.ts +1 -1
  124. package/dist/core/workflow-run.d.ts.map +1 -1
  125. package/dist/core/workflow-run.js.map +1 -1
  126. package/dist/hooks/api/annotations.d.ts +63 -0
  127. package/dist/hooks/api/annotations.d.ts.map +1 -0
  128. package/dist/hooks/api/annotations.js +145 -0
  129. package/dist/hooks/api/annotations.js.map +1 -0
  130. package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
  131. package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
  132. package/dist/hooks/api/run-annotation-mapper.js +22 -0
  133. package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
  134. package/dist/hooks/api/run-annotations.d.ts +25 -0
  135. package/dist/hooks/api/run-annotations.d.ts.map +1 -0
  136. package/dist/hooks/api/run-annotations.js +78 -0
  137. package/dist/hooks/api/run-annotations.js.map +1 -0
  138. package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
  139. package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
  140. package/dist/hooks/api/step-attempt-detail.js +43 -0
  141. package/dist/hooks/api/step-attempt-detail.js.map +1 -0
  142. package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
  143. package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
  144. package/dist/hooks/api/workflow-run-mapper.js +47 -0
  145. package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
  146. package/dist/pages/project-workflows-page.js +19 -19
  147. package/dist/pages/project-workflows-page.js.map +1 -1
  148. package/dist/pages/workflow-run-list-page.js +1 -1
  149. package/dist/pages/workflow-run-list-page.js.map +1 -1
  150. package/dist/routes/inputs.d.ts +7 -2
  151. package/dist/routes/inputs.d.ts.map +1 -1
  152. package/dist/routes/inputs.js +5 -6
  153. package/dist/routes/inputs.js.map +1 -1
  154. package/dist/tsconfig.test.tsbuildinfo +1 -1
  155. package/package.json +10 -9
  156. package/src/components/identifier/identifier.tsx +2 -2
  157. package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
  158. package/src/components/job-detail/job-context-panel.test.tsx +72 -0
  159. package/src/components/job-detail/job-context-panel.tsx +237 -0
  160. package/src/components/job-detail/job-detail-header.test.tsx +3 -0
  161. package/src/components/job-detail/job-detail-header.tsx +65 -34
  162. package/src/components/job-detail/job-detail-view.stories.tsx +555 -48
  163. package/src/components/job-detail/job-detail-view.tsx +101 -42
  164. package/src/components/job-detail/job-empty-states.tsx +23 -10
  165. package/src/components/job-detail/job-execution-switcher.tsx +5 -5
  166. package/src/components/job-detail/json-code.tsx +120 -0
  167. package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
  168. package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
  169. package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
  170. package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
  171. package/src/components/job-detail/step-troubleshooting.tsx +537 -0
  172. package/src/components/job-graph/job-graph-content.tsx +4 -3
  173. package/src/components/job-graph/job-node.tsx +3 -3
  174. package/src/components/step-list/index.ts +1 -0
  175. package/src/components/step-list/step-list-model.ts +5 -0
  176. package/src/components/step-list/step-list.test.tsx +75 -2
  177. package/src/components/step-list/step-list.tsx +73 -20
  178. package/src/components/workflow-run-duration-label.tsx +1 -1
  179. package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
  180. package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
  181. package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
  182. package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
  183. package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
  184. package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
  185. package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
  186. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
  187. package/src/components/workflow-run-summary/workflow-run-summary.tsx +12 -12
  188. package/src/components/workflow-run-tabs/index.ts +7 -0
  189. package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
  190. package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
  191. package/src/components/workflow-run-tabs/run-annotation-list.tsx +273 -0
  192. package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
  193. package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +95 -20
  194. package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
  195. package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
  196. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +44 -0
  197. package/src/components/workflow-run-view/run-workspace-nav.tsx +22 -9
  198. package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
  199. package/src/components/workflow-run-view/workflow-run-view.test.tsx +246 -20
  200. package/src/components/workflow-run-view/workflow-run-view.tsx +255 -37
  201. package/src/core/entities/job-execution.ts +7 -0
  202. package/src/core/entities/job.ts +9 -0
  203. package/src/core/entities/step-attempt.ts +33 -0
  204. package/src/core/entities/step.ts +3 -1
  205. package/src/core/run-annotation.test.ts +315 -0
  206. package/src/core/run-annotation.ts +244 -0
  207. package/src/core/workflow-run.ts +4 -0
  208. package/src/hooks/api/annotations.test.ts +84 -0
  209. package/src/hooks/api/annotations.ts +240 -0
  210. package/src/hooks/api/run-annotation-mapper.ts +30 -0
  211. package/src/hooks/api/run-annotations.ts +137 -0
  212. package/src/hooks/api/step-attempt-detail.ts +67 -0
  213. package/src/hooks/api/workflow-run-mapper.ts +46 -0
  214. package/src/hooks/api/workflow-runs.test.tsx +61 -1
  215. package/src/pages/project-workflows-page.tsx +19 -19
  216. package/src/pages/workflow-job-detail-page.test.tsx +118 -1
  217. package/src/pages/workflow-run-list-page.tsx +1 -1
  218. package/src/routes/inputs.ts +7 -2
  219. package/test/fixtures/workflow-run.ts +7 -0
  220. package/tsconfig.build.tsbuildinfo +1 -1
  221. package/dist/core/workflow-run-tabs.d.ts +0 -9
  222. package/dist/core/workflow-run-tabs.d.ts.map +0 -1
  223. package/dist/core/workflow-run-tabs.js +0 -3
  224. package/dist/core/workflow-run-tabs.js.map +0 -1
  225. package/src/core/workflow-run-tabs.ts +0 -8
@@ -66,9 +66,9 @@ function ProjectWorkflowsPageInner({projectId}: {projectId: string}) {
66
66
  }
67
67
 
68
68
  return (
69
- <div className="flex w-full flex-col gap-24">
69
+ <div className="flex w-full flex-col gap-section">
70
70
  {projectQuery.isPending ? (
71
- <div className="flex flex-col gap-12">
71
+ <div className="flex flex-col gap-cluster">
72
72
  <Skeleton className="h-28 w-1/3" />
73
73
  <Skeleton className="h-18 w-1/2" />
74
74
  </div>
@@ -88,7 +88,7 @@ function ProjectWorkflowsPageInner({projectId}: {projectId: string}) {
88
88
 
89
89
  {projectQuery.data ? (
90
90
  <>
91
- <header className="flex flex-col gap-4">
91
+ <header className="flex flex-col gap-tight">
92
92
  <Header variant="h2">Workflows</Header>
93
93
  <Text size="sm" className="text-foreground-neutral-muted">
94
94
  Synced workflow definitions for this project source.
@@ -170,7 +170,7 @@ function WorkflowDefinitionsList({
170
170
  }) {
171
171
  if (isPending) {
172
172
  return (
173
- <div className="flex flex-col gap-8">
173
+ <div className="flex flex-col gap-inline">
174
174
  <Skeleton className="h-40 w-full" />
175
175
  <Skeleton className="h-40 w-full" />
176
176
  <Skeleton className="h-40 w-full" />
@@ -228,11 +228,11 @@ function WorkflowDefinitionsList({
228
228
  />
229
229
  </TableCell>
230
230
  <TableCell className="max-w-260">
231
- <div className="flex min-w-0 flex-col gap-2">
231
+ <div className="flex min-w-0 flex-col gap-tight">
232
232
  <button
233
233
  type="button"
234
234
  onClick={() => onOpenDefinition(definition)}
235
- className="flex min-w-0 flex-col gap-2 text-left outline-none focus-visible:shadow-border-interactive-with-active rounded-4"
235
+ className="flex min-w-0 flex-col gap-tight text-left outline-none focus-visible:shadow-border-interactive-with-active rounded-4"
236
236
  >
237
237
  <Text size="sm" bold className="truncate">
238
238
  {definition.name}
@@ -276,11 +276,11 @@ function WorkflowDefinitionsList({
276
276
  return (
277
277
  <div
278
278
  key={definition.id}
279
- className="flex flex-col gap-10 border-b border-border-neutral-base p-12 last:border-b-0"
279
+ className="flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0"
280
280
  >
281
281
  <button
282
282
  type="button"
283
- className="flex min-w-0 items-start gap-10 text-left"
283
+ className="flex min-w-0 items-start gap-inline text-left"
284
284
  onClick={() => onOpenDefinition(definition)}
285
285
  >
286
286
  <Icon
@@ -288,7 +288,7 @@ function WorkflowDefinitionsList({
288
288
  className="size-16 shrink-0 text-foreground-neutral-muted"
289
289
  aria-hidden="true"
290
290
  />
291
- <div className="flex min-w-0 flex-col gap-4">
291
+ <div className="flex min-w-0 flex-col gap-tight">
292
292
  <Text size="sm" bold className="break-words">
293
293
  {definition.name}
294
294
  </Text>
@@ -297,7 +297,7 @@ function WorkflowDefinitionsList({
297
297
  </Code>
298
298
  </div>
299
299
  </button>
300
- <div className="flex items-center justify-between gap-8">
300
+ <div className="flex items-center justify-between gap-inline">
301
301
  <Text size="xs" className="text-foreground-neutral-muted">
302
302
  Updated <RelativeTime value={definition.updatedAt} />
303
303
  </Text>
@@ -319,7 +319,7 @@ function WorkflowDefinitionsList({
319
319
 
320
320
  {isFetchNextPageError ? (
321
321
  <Callout role="alert" type="error">
322
- <div className="flex items-center justify-between gap-12">
322
+ <div className="flex items-center justify-between gap-cluster">
323
323
  <Text size="sm">Could not load more workflows.</Text>
324
324
  <Button size="sm" variant="secondary" onClick={onLoadMore}>
325
325
  Retry
@@ -363,7 +363,7 @@ function WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefin
363
363
 
364
364
  return (
365
365
  <Callout role="alert" type="error">
366
- <div className="flex flex-col gap-4">
366
+ <div className="flex flex-col gap-tight">
367
367
  <Text size="sm" bold>
368
368
  Workflow sync failed
369
369
  </Text>
@@ -388,11 +388,11 @@ function WorkflowSyncWarnings({sync}: {sync: DefinitionSyncSummary | null | unde
388
388
 
389
389
  return (
390
390
  <Callout role="status" type="warning">
391
- <div className="flex flex-col gap-6">
391
+ <div className="flex flex-col gap-inline">
392
392
  <Text size="sm" bold>
393
393
  Workflow definition warnings
394
394
  </Text>
395
- <ul className="flex flex-col gap-4">
395
+ <ul className="flex flex-col gap-tight">
396
396
  {warningItems.map(({key, warning}) => (
397
397
  <li key={key}>
398
398
  <Text size="sm">{warning.message}</Text>
@@ -436,18 +436,18 @@ function DefinitionSheet({
436
436
  {definition.configPath ?? 'Manual workflow definition'}
437
437
  </SheetDescription>
438
438
  </SheetHeader>
439
- <SheetBody className="gap-18">
440
- <div className="grid w-full gap-10">
439
+ <SheetBody className="gap-group">
440
+ <div className="grid w-full gap-inline">
441
441
  <Metadata label="Definition id" value={definition.id} />
442
442
  <Metadata label="Source" value={definition.source} />
443
443
  <Metadata label="Ref" value={definition.ref ?? 'Not set'} />
444
444
  <Metadata label="SHA" value={definition.sha ?? 'Not set'} />
445
445
  </div>
446
- <div className="flex w-full flex-col gap-8">
446
+ <div className="flex w-full flex-col gap-inline">
447
447
  <Text size="sm" bold>
448
448
  Normalized definition
449
449
  </Text>
450
- <pre className="max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-12 scrollbar">
450
+ <pre className="max-h-[52vh] w-full overflow-auto rounded-8 border border-border-neutral-base bg-background-neutral-subtle p-panel-compact scrollbar">
451
451
  <Code as="code" className="whitespace-pre text-foreground-neutral-base">
452
452
  {normalizedJson}
453
453
  </Code>
@@ -463,7 +463,7 @@ function DefinitionSheet({
463
463
 
464
464
  function Metadata({label, value}: {label: string; value: string}) {
465
465
  return (
466
- <div className="min-w-0 py-12 first:pt-0 last:pb-0">
466
+ <div className="min-w-0 py-row first:pt-0 last:pb-0">
467
467
  <Text size="xs" className="text-foreground-neutral-muted">
468
468
  {label}
469
469
  </Text>
@@ -1,5 +1,5 @@
1
1
  import {configureApiClient} from '@shipfox/client-api';
2
- import {act, screen} from '@testing-library/react';
2
+ import {act, screen, waitFor} from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import {workflowRunsQueryKeys} from '#hooks/api/workflow-runs.js';
5
5
  import type {WorkflowJobSearch, WorkflowRunsSearch} from '#routes/inputs.js';
@@ -33,8 +33,60 @@ const BACK_TO_SUMMARY_PATTERN = /Back to run summary/;
33
33
  const RUN_MOVED_ON_PATTERN = /Run moved on to/;
34
34
  const LINT_LINK_PATTERN = /lint/;
35
35
  const RELEASE_LINK_PATTERN = /release/;
36
+ const ANNOTATION_LINK_PATTERN = /annotation/;
37
+ const ANNOTATIONS_LINK_PATTERN = /Annotations/;
36
38
 
37
39
  describe('WorkflowJobDetailPage', () => {
40
+ beforeEach(() => {
41
+ jobAnnotations.value = [];
42
+ });
43
+
44
+ test('links the job to its annotations without rendering one', async () => {
45
+ jobAnnotations.value = [
46
+ {
47
+ id: 'aaaaaaaa-1111-4aaa-8aaa-000000000001',
48
+ job_id: JOB_ID,
49
+ job_execution_id: EXECUTION_ID,
50
+ origin_step_id: STEP_ID,
51
+ origin_step_attempt: 2,
52
+ context: 'smoke check',
53
+ style: 'error',
54
+ sequence: 1,
55
+ body: 'Task nine failed.',
56
+ },
57
+ {
58
+ id: 'aaaaaaaa-1111-4aaa-8aaa-000000000002',
59
+ job_id: SIBLING_JOB_ID,
60
+ job_execution_id: EXECUTION_ID,
61
+ origin_step_id: STEP_ID,
62
+ origin_step_attempt: 1,
63
+ context: 'other job',
64
+ style: 'info',
65
+ sequence: 2,
66
+ body: 'Not this job.',
67
+ },
68
+ ];
69
+ configureApiClient({fetchImpl: vi.fn(jobDetailFetch)});
70
+
71
+ renderJobPath(`?jobExecution=${EXECUTION_ID}&runAttempt=1`);
72
+
73
+ const chip = await screen.findByRole('link', {
74
+ name: 'View 1 annotation, highest severity error',
75
+ });
76
+ expect(chip.getAttribute('href')).toContain(`tab=annotations`);
77
+ expect(chip.getAttribute('href')).toContain(`job=${JOB_ID}`);
78
+ expect(screen.queryByText('Task nine failed.')).not.toBeInTheDocument();
79
+ });
80
+
81
+ test('omits the annotation chip when the job produced none', async () => {
82
+ configureApiClient({fetchImpl: vi.fn(jobDetailFetch)});
83
+
84
+ renderJobPath(`?jobExecution=${EXECUTION_ID}&runAttempt=1`);
85
+
86
+ await screen.findByRole('heading', {name: 'release'});
87
+ expect(screen.queryByRole('link', {name: ANNOTATION_LINK_PATTERN})).not.toBeInTheDocument();
88
+ });
89
+
38
90
  test('resolves an exact job execution and step attempt from a deep link', async () => {
39
91
  configureApiClient({fetchImpl: vi.fn(jobDetailFetch)});
40
92
 
@@ -177,6 +229,56 @@ describe('WorkflowJobDetailPage', () => {
177
229
  expect(screen.queryByText('This job has no annotations to show.')).not.toBeInTheDocument();
178
230
  });
179
231
 
232
+ test('keeps the job navigation count run-scoped', async () => {
233
+ jobAnnotations.value = Array.from({length: 5}, (_unused, index) => ({
234
+ id: `aaaaaaaa-1111-4aaa-8aaa-${String(index + 1).padStart(12, '0')}`,
235
+ job_id: JOB_ID,
236
+ job_execution_id: EXECUTION_ID,
237
+ origin_step_id: STEP_ID,
238
+ origin_step_attempt: 1,
239
+ context: `annotation-${index + 1}`,
240
+ style: 'error',
241
+ sequence: index + 1,
242
+ body: 'Body',
243
+ }));
244
+ const annotationRequests: URL[] = [];
245
+ const summaryRequests: URL[] = [];
246
+ configureApiClient({
247
+ fetchImpl: vi.fn((input: RequestInfo | URL) => {
248
+ const request = input as Request;
249
+ const url = new URL(request.url);
250
+ if (url.pathname.endsWith('/annotations')) {
251
+ annotationRequests.push(url);
252
+ return jobDetailFetch(input);
253
+ }
254
+ if (url.pathname.endsWith('/annotations/summary')) {
255
+ summaryRequests.push(url);
256
+ const executionScoped = url.searchParams.has('job_execution_id');
257
+ return Promise.resolve(
258
+ jsonResponse({
259
+ total: executionScoped ? 2 : 5,
260
+ error: executionScoped ? 2 : 5,
261
+ warning: 0,
262
+ info: 0,
263
+ success: 0,
264
+ step_counts: [],
265
+ }),
266
+ );
267
+ }
268
+ return jobDetailFetch(input);
269
+ }),
270
+ });
271
+
272
+ renderJobPath('?runAttempt=1');
273
+
274
+ const annotationsLink = await screen.findByRole('link', {name: ANNOTATIONS_LINK_PATTERN});
275
+ await waitFor(() => expect(annotationsLink).toHaveTextContent('5'));
276
+ expect(annotationRequests.some((url) => !url.searchParams.has('job_execution_id'))).toBe(true);
277
+ expect(
278
+ summaryRequests.some((url) => url.searchParams.get('job_execution_id') === EXECUTION_ID),
279
+ ).toBe(true);
280
+ });
281
+
180
282
  test('explains a failure that happened before the first step started', async () => {
181
283
  configureApiClient({
182
284
  fetchImpl: vi.fn(() => Promise.resolve(jsonResponse(failedBeforeStepsDetailDto()))),
@@ -247,10 +349,21 @@ function renderJobPath(path = '', jobId = JOB_ID) {
247
349
  );
248
350
  }
249
351
 
352
+ /** Annotations for the job page, which renders their count and never their bodies. */
353
+ const jobAnnotations: {value: unknown[]} = {value: []};
354
+
355
+ function annotationsResponse() {
356
+ return {annotations: jobAnnotations.value, has_more: false, next_cursor: null};
357
+ }
358
+
250
359
  function jobDetailFetch(input: RequestInfo | URL) {
251
360
  const request = input as Request;
252
361
  const url = new URL(request.url);
253
362
 
363
+ if (url.pathname.endsWith('/annotations')) {
364
+ return Promise.resolve(jsonResponse(annotationsResponse()));
365
+ }
366
+
254
367
  if (url.pathname.endsWith('/attempts')) {
255
368
  return Promise.resolve(
256
369
  jsonResponse(
@@ -287,6 +400,10 @@ function newerAttemptJobDetailFetch(input: RequestInfo | URL) {
287
400
  const request = input as Request;
288
401
  const url = new URL(request.url);
289
402
 
403
+ if (url.pathname.endsWith('/annotations')) {
404
+ return Promise.resolve(jsonResponse(annotationsResponse()));
405
+ }
406
+
290
407
  if (url.pathname.endsWith('/attempts')) {
291
408
  return Promise.resolve(
292
409
  jsonResponse(
@@ -50,7 +50,7 @@ export function WorkflowRunsPage({
50
50
 
51
51
  return (
52
52
  <div className="flex min-h-0 flex-1 overflow-hidden bg-background-neutral-base">
53
- <div className="mx-auto flex min-h-0 w-full max-w-[1120px] flex-1 flex-col px-24 py-24">
53
+ <div className="mx-auto flex min-h-0 w-full max-w-[1120px] flex-1 flex-col px-frame py-frame">
54
54
  <WorkflowRunList
55
55
  projectId={projectId}
56
56
  workspaceSlug={workspaceSlug}
@@ -1,3 +1,4 @@
1
+ import {RUN_ANNOTATION_SEVERITIES, type RunAnnotationSeverity} from '#core/run-annotation.js';
1
2
  import type {WorkflowRunSelectionInput} from '#core/workflow-run-url-state.js';
2
3
 
3
4
  /**
@@ -12,9 +13,13 @@ export const WORKFLOW_RUN_TABS = ['summary', 'jobs', 'annotations', 'source'] as
12
13
 
13
14
  export type WorkflowRunTab = (typeof WORKFLOW_RUN_TABS)[number];
14
15
 
15
- export const WORKFLOW_RUN_ANNOTATION_SEVERITIES = ['error', 'warning', 'info', 'success'] as const;
16
+ /**
17
+ * The `severity` parameter's vocabulary is the display severity set, not a second list: a URL
18
+ * that could name a severity the list cannot rank would be a filter with no matching rows.
19
+ */
20
+ export const WORKFLOW_RUN_ANNOTATION_SEVERITIES = RUN_ANNOTATION_SEVERITIES;
16
21
 
17
- export type WorkflowRunAnnotationSeverity = (typeof WORKFLOW_RUN_ANNOTATION_SEVERITIES)[number];
22
+ export type WorkflowRunAnnotationSeverity = RunAnnotationSeverity;
18
23
 
19
24
  export interface WorkflowRunsSearch extends WorkflowRunSelectionInput {
20
25
  search?: string;
@@ -233,6 +233,9 @@ export function workflowJobDto(overrides: JobDtoOverrides = {}): WorkflowRunJobD
233
233
  status: 'pending',
234
234
  status_reason: null,
235
235
  carried_over: false,
236
+ success: null,
237
+ runner: null,
238
+ evaluation_trace: null,
236
239
  listening: null,
237
240
  listener_status: 'inactive',
238
241
  resolution_reason: null,
@@ -271,8 +274,10 @@ export function workflowJobExecutionDto(
271
274
  name: 'build',
272
275
  status: 'pending',
273
276
  status_reason: null,
277
+ runner: null,
274
278
  trigger_events: [],
275
279
  outputs: null,
280
+ evaluation_trace: null,
276
281
  queued_at: null,
277
282
  started_at: null,
278
283
  finished_at: null,
@@ -296,8 +301,10 @@ export function workflowStepDto(
296
301
  name: 'build',
297
302
  source_location: null,
298
303
  status: 'pending',
304
+ status_reason: null,
299
305
  type: 'run',
300
306
  config: {},
307
+ evaluation_trace: null,
301
308
  error: null,
302
309
  position: 0,
303
310
  current_attempt: 1,