@tea-agent/loop-agent 0.7.5 → 0.8.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 (127) hide show
  1. package/AGENTS.md +143 -142
  2. package/CHANGELOG.md +148 -164
  3. package/README.md +206 -204
  4. package/bin/agent-worker.js +22 -22
  5. package/bin/loop-agent.js +21 -21
  6. package/dist/commands/init.js +518 -488
  7. package/dist/commands/loop-benchmark.js +11 -11
  8. package/dist/commands/pi-reuse-benchmark.js +16 -16
  9. package/dist/executors/cursor-executor.js +1 -1
  10. package/dist/governance/manifest-types.js +1 -1
  11. package/dist/task/runtime.js +27 -27
  12. package/dist/worker/cli.js +3 -3
  13. package/dist/worker/observability/event-store.js +2 -1
  14. package/dist/worker/observability/read-model.js +13 -11
  15. package/dist/worker/observe/paths.js +2 -2
  16. package/dist/worker/observe/routes.js +4 -3
  17. package/dist/worker/observe/static/app.js +1479 -1480
  18. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  19. package/dist/worker/observe/static/dag-layout.js +83 -83
  20. package/dist/worker/observe/static/index.html +63 -63
  21. package/dist/worker/observe/static/styles.css +722 -722
  22. package/dist/worker/pool/run-store.js +7 -8
  23. package/dist/worker/run-task/run-task.js +11 -2
  24. package/dist/worker/runner/run-ready.js +1 -1
  25. package/dist/workflows/dag/canvas-observer.js +275 -275
  26. package/docs/README.md +80 -79
  27. package/docs/agent-dag-recovery-playbook.md +184 -184
  28. package/docs/agent-dag-runner.md +42 -42
  29. package/docs/architecture/runtime-boundaries.md +162 -162
  30. package/docs/cursor-executor-usage.md +25 -25
  31. package/docs/decisions/README.md +3 -3
  32. package/docs/design/README.md +49 -49
  33. package/docs/development-principles.md +73 -73
  34. package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -1749
  35. package/docs/exec-plans/README.md +6 -6
  36. package/docs/exec-plans/active/README.md +11 -11
  37. package/docs/exec-plans/completed/README.md +35 -34
  38. package/docs/feature-workflow.md +187 -187
  39. package/docs/harness-methodology-debugging.md +153 -153
  40. package/docs/harness-methodology-tdd.md +130 -130
  41. package/docs/harness-methodology-verification.md +27 -27
  42. package/docs/init-surface.manifest.json +245 -241
  43. package/docs/loop-agent-harness.md +63 -55
  44. package/docs/production-readiness.md +96 -96
  45. package/docs/progress/README.md +3 -3
  46. package/docs/reports/README.md +9 -9
  47. package/docs/skills/README.md +6 -6
  48. package/docs/skills/vetted-skill-registry.md +26 -26
  49. package/docs/templates/adr.md +60 -60
  50. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  51. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  52. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
  53. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  54. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  55. package/docs/templates/agent-dag-report.schema.json +454 -454
  56. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  57. package/docs/templates/agent-dag.base.json +195 -195
  58. package/docs/templates/agent-dag.final-verification.json +190 -190
  59. package/docs/templates/agent-dag.schema.json +316 -316
  60. package/docs/templates/agent-dag.supervised-implementation.json +500 -500
  61. package/docs/templates/exec-plan.md +64 -64
  62. package/docs/templates/feature-spec.md +53 -53
  63. package/docs/templates/harness.schema.json +218 -0
  64. package/docs/templates/hybrid-dag.json +193 -193
  65. package/docs/templates/init-evolution-review.md +33 -33
  66. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  67. package/docs/templates/product-line/AGENTS.md +8 -8
  68. package/docs/templates/product-line/README.md +9 -9
  69. package/docs/templates/product-line/acceptance.yaml +14 -14
  70. package/docs/templates/product-line/closeout.yaml +9 -9
  71. package/docs/templates/product-line/design.md +13 -13
  72. package/docs/templates/product-line/links.md +10 -10
  73. package/docs/templates/product-line/requirement.md +17 -17
  74. package/docs/templates/product-line/task-graph.yaml +15 -15
  75. package/docs/templates/product-line/task.yaml +65 -65
  76. package/docs/templates/product-line/test-plan.md +7 -7
  77. package/docs/templates/production-readiness-checklist.md +57 -57
  78. package/docs/templates/progress-log.md +17 -17
  79. package/docs/templates/project-start-checklist.md +9 -9
  80. package/docs/templates/qa-report.md +48 -48
  81. package/docs/templates/sprint-contract.md +29 -29
  82. package/docs/templates/worker-dogfood-evidence.md +52 -52
  83. package/docs/templates/worker-dogfood-setup.md +48 -48
  84. package/docs/verification-matrix.md +49 -49
  85. package/examples/decision-gate-agent-dag.json +123 -123
  86. package/examples/example-dag.json +51 -51
  87. package/examples/hybrid-loop-agent-dag.json +194 -194
  88. package/harness.json +73 -71
  89. package/package.json +68 -67
  90. package/scripts/check-product-line-docs.sh +22 -22
  91. package/scripts/check-task-pool-root.sh +32 -0
  92. package/skills/ai-engineering-context/SKILL.md +48 -48
  93. package/skills/code-review-core/SKILL.md +20 -20
  94. package/skills/codebase-scout/SKILL.md +19 -19
  95. package/skills/init-capability-evolution/SKILL.md +69 -69
  96. package/skills/loop-agent/SKILL.md +149 -149
  97. package/skills/loop-agent/references/README.md +67 -67
  98. package/skills/loop-agent/references/command-reference.md +432 -412
  99. package/skills/loop-agent/references/harness-policy.md +263 -263
  100. package/skills/loop-agent/references/hybrid-dag.md +216 -216
  101. package/skills/loop-agent/references/learned/README.md +21 -21
  102. package/skills/loop-agent/references/long-running-loop.md +59 -59
  103. package/skills/loop-agent/references/model-routing.md +36 -36
  104. package/skills/loop-agent/references/multi-worktree.md +54 -54
  105. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  106. package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
  107. package/skills/loop-agent/references/pi-prompt.md +23 -23
  108. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +81 -81
  109. package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
  110. package/skills/loop-agent/references/task-workflow.md +89 -89
  111. package/skills/loop-agent/references/verification-and-failure-handling.md +128 -128
  112. package/skills/requesting-code-review/SKILL.md +101 -101
  113. package/skills/requesting-code-review/code-reviewer.md +168 -168
  114. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  115. package/skills/systematic-debugging/SKILL.md +296 -296
  116. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  117. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  118. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  119. package/skills/systematic-debugging/find-polluter.sh +63 -63
  120. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  121. package/skills/systematic-debugging/test-academic.md +14 -14
  122. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  123. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  124. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  125. package/skills/test-driven-development/SKILL.md +20 -20
  126. package/skills/verification-before-completion/SKILL.md +154 -154
  127. package/skills/webapp-testing/SKILL.md +19 -19
@@ -1,722 +1,722 @@
1
- :root {
2
- --bg: #0d1117;
3
- --surface: #161b22;
4
- --surface-raised: #1c2128;
5
- --border: #30363d;
6
- --text: #c9d1d9;
7
- --muted: #8b949e;
8
- --link: #58a6ff;
9
- --green: #3fb950;
10
- --red: #f85149;
11
- --yellow: #d29922;
12
- --blue: #58a6ff;
13
- --gray-red: #8b3a3a;
14
- --space-1: 4px;
15
- --space-2: 8px;
16
- --space-3: 12px;
17
- --space-4: 16px;
18
- --space-6: 24px;
19
- --space-8: 32px;
20
- --radius-sm: 4px;
21
- --radius-md: 8px;
22
- --radius-lg: 12px;
23
- --shadow-1: 0 4px 18px rgba(0, 0, 0, 0.2);
24
- --transition-fast: 140ms ease;
25
- }
26
-
27
- * {
28
- box-sizing: border-box;
29
- }
30
-
31
- body {
32
- margin: 0;
33
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
34
- font-size: 13px;
35
- line-height: 1.5;
36
- background: var(--bg);
37
- color: var(--text);
38
- }
39
-
40
- code,
41
- pre,
42
- .output-block,
43
- .rank-node-id,
44
- .dag-node-label,
45
- .dag-node-meta,
46
- .dag-node-status,
47
- td:first-child,
48
- .meta-item dd {
49
- font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
50
- }
51
-
52
- #app {
53
- max-width: 1200px;
54
- margin: 0 auto;
55
- padding: 1rem 1.5rem 2rem;
56
- }
57
-
58
- .site-header {
59
- display: flex;
60
- flex-wrap: wrap;
61
- align-items: center;
62
- gap: 1rem 1.5rem;
63
- margin-bottom: 1.5rem;
64
- padding-bottom: 0.75rem;
65
- border-bottom: 1px solid var(--border);
66
- }
67
-
68
- .logo {
69
- color: var(--text);
70
- text-decoration: none;
71
- font-weight: 600;
72
- }
73
-
74
- .logo:hover {
75
- color: var(--link);
76
- }
77
-
78
- .top-nav {
79
- display: flex;
80
- gap: 1rem;
81
- }
82
-
83
- .nav-link {
84
- color: var(--muted);
85
- text-decoration: none;
86
- font-size: 12px;
87
- }
88
-
89
- .nav-link:hover {
90
- color: var(--link);
91
- }
92
-
93
- .header-refresh {
94
- margin-left: auto;
95
- color: var(--muted);
96
- font-size: 11px;
97
- }
98
-
99
- .breadcrumb {
100
- flex-basis: 100%;
101
- color: var(--muted);
102
- font-size: 12px;
103
- }
104
-
105
- .breadcrumb a {
106
- color: var(--link);
107
- text-decoration: none;
108
- }
109
-
110
- .breadcrumb a:hover {
111
- text-decoration: underline;
112
- }
113
-
114
- h2 {
115
- margin: 0 0 0.75rem;
116
- font-size: 1.1rem;
117
- font-weight: 600;
118
- }
119
-
120
- h3,
121
- .section-heading {
122
- margin: 0 0 0.5rem;
123
- font-size: 0.95rem;
124
- font-weight: 600;
125
- }
126
-
127
- .panel {
128
- background: var(--surface);
129
- border: 1px solid var(--border);
130
- border-radius: var(--radius-md);
131
- padding: 0.75rem 1rem;
132
- margin-bottom: 1rem;
133
- }
134
-
135
- .dag-graph-wrap {
136
- display: grid;
137
- gap: var(--space-2);
138
- }
139
-
140
- .dag-graph-toolbar {
141
- display: flex;
142
- align-items: center;
143
- justify-content: space-between;
144
- gap: var(--space-3);
145
- }
146
-
147
- .dag-graph-summary,
148
- .dag-graph-fallback {
149
- color: var(--muted);
150
- font-size: 12px;
151
- }
152
-
153
- .dag-graph-fallback { margin: 0; }
154
-
155
- .dag-graph-reset {
156
- appearance: none;
157
- border: 1px solid var(--border);
158
- border-radius: var(--radius-sm);
159
- background: var(--surface-raised);
160
- color: var(--text);
161
- padding: 6px 10px;
162
- cursor: pointer;
163
- }
164
-
165
- .dag-graph-reset:hover,
166
- .dag-graph-reset:focus-visible { border-color: var(--link); }
167
-
168
- .dag-graph-viewport {
169
- min-height: 220px;
170
- max-height: 620px;
171
- overflow: auto;
172
- border: 1px solid var(--border);
173
- border-radius: var(--radius-md);
174
- background: radial-gradient(circle at 1px 1px, rgba(139, 148, 158, 0.18) 1px, transparent 0) 0 0 / 20px 20px, var(--bg);
175
- scroll-behavior: smooth;
176
- }
177
-
178
- .dag-graph { display: block; }
179
- .dag-edge { fill: none; stroke: var(--muted); stroke-width: 2; opacity: 0.75; transition: opacity var(--transition-fast); }
180
- .dag-edge + .dag-edge { stroke-width: 1.8; }
181
- #dag-arrow path { fill: var(--muted); }
182
- .dag-graph-node { cursor: pointer; transition: opacity var(--transition-fast); }
183
- .dag-graph-node rect { fill: var(--surface-raised); stroke: var(--border); stroke-width: 1.5; }
184
- .dag-graph-node:hover rect,
185
- .dag-graph-node:focus-visible rect { stroke: var(--link); stroke-width: 2.5; }
186
- .dag-graph-node:focus { outline: none; }
187
- .dag-graph-node.selected rect { stroke: var(--link); stroke-width: 3; filter: drop-shadow(0 0 6px rgba(88, 166, 255, 0.45)); }
188
- .dag-graph-node.status-running rect,
189
- .dag-graph-node.status-started rect { stroke: var(--yellow); }
190
- .dag-graph-node.status-failed rect,
191
- .dag-graph-node.status-error rect { stroke: var(--red); fill: rgba(248, 81, 73, 0.1); }
192
- .dag-graph-node.status-skipped rect { stroke-dasharray: 5 4; }
193
- .dag-node-label { fill: var(--text); font-size: 13px; font-weight: 700; }
194
- .dag-node-meta { fill: var(--muted); font-size: 11px; }
195
- .dag-node-status { fill: var(--text); font-size: 11px; }
196
- .dag-dimmed { opacity: 0.25; }
197
- .dag-graph-node.status-running { animation: graph-node-pulse 2s ease-in-out infinite; }
198
-
199
- @keyframes graph-node-pulse {
200
- 50% { filter: drop-shadow(0 0 7px rgba(210, 153, 34, 0.45)); }
201
- }
202
-
203
- @media (max-width: 680px) {
204
- #app { padding-inline: var(--space-3); }
205
- .meta-grid { grid-template-columns: 1fr; }
206
- .dag-graph-viewport { max-height: 480px; }
207
- table { min-width: 760px; }
208
- .panel { overflow-x: auto; }
209
- }
210
-
211
- @media (prefers-reduced-motion: reduce) {
212
- *, *::before, *::after {
213
- animation-duration: 0.01ms !important;
214
- animation-iteration-count: 1 !important;
215
- scroll-behavior: auto !important;
216
- transition-duration: 0.01ms !important;
217
- }
218
- }
219
-
220
- .kpi-grid {
221
- display: grid;
222
- grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
223
- gap: 0.75rem;
224
- padding: 0.75rem;
225
- }
226
-
227
- .kpi-card {
228
- background: var(--bg);
229
- border: 1px solid var(--border);
230
- border-radius: 6px;
231
- padding: 0.6rem 0.75rem;
232
- text-align: center;
233
- }
234
-
235
- .kpi-card.kpi-highlight {
236
- border-color: var(--yellow);
237
- background: rgba(210, 153, 34, 0.08);
238
- }
239
-
240
- .kpi-card.kpi-danger {
241
- border-color: var(--red);
242
- }
243
-
244
- .kpi-label {
245
- color: var(--muted);
246
- font-size: 10px;
247
- text-transform: uppercase;
248
- margin-bottom: 0.25rem;
249
- }
250
-
251
- .kpi-value {
252
- font-size: 1.4rem;
253
- font-weight: 700;
254
- }
255
-
256
- .active-dag-list {
257
- display: flex;
258
- flex-direction: column;
259
- gap: 0.75rem;
260
- }
261
-
262
- .active-dag-card {
263
- background: var(--bg);
264
- border: 1px solid var(--border);
265
- border-radius: 6px;
266
- padding: 0.75rem 1rem;
267
- }
268
-
269
- .active-dag-card-running {
270
- border-color: var(--yellow);
271
- box-shadow: 0 0 0 1px rgba(210, 153, 34, 0.25);
272
- animation: pulse-border 2s ease-in-out infinite;
273
- }
274
-
275
- @keyframes pulse-border {
276
- 0%,
277
- 100% {
278
- border-color: var(--yellow);
279
- }
280
- 50% {
281
- border-color: rgba(210, 153, 34, 0.5);
282
- }
283
- }
284
-
285
- .active-dag-header {
286
- display: flex;
287
- align-items: center;
288
- gap: 0.75rem;
289
- margin-bottom: 0.35rem;
290
- }
291
-
292
- .active-dag-title {
293
- font-weight: 600;
294
- flex: 1;
295
- }
296
-
297
- .active-dag-meta {
298
- display: flex;
299
- flex-direction: column;
300
- gap: 0.2rem;
301
- color: var(--muted);
302
- font-size: 12px;
303
- margin-bottom: 0.5rem;
304
- }
305
-
306
- .active-dag-link {
307
- color: var(--link);
308
- text-decoration: none;
309
- font-size: 12px;
310
- }
311
-
312
- .active-dag-link:hover {
313
- text-decoration: underline;
314
- }
315
-
316
- .dashboard-footer {
317
- margin-top: 0.75rem;
318
- color: var(--muted);
319
- font-size: 12px;
320
- }
321
-
322
- .meta-grid {
323
- display: grid;
324
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
325
- gap: 0.5rem 1.5rem;
326
- }
327
-
328
- .meta-item dt {
329
- color: var(--muted);
330
- font-size: 11px;
331
- text-transform: uppercase;
332
- margin: 0;
333
- }
334
-
335
- .meta-item dd {
336
- margin: 0.15rem 0 0;
337
- }
338
-
339
- table {
340
- width: 100%;
341
- border-collapse: collapse;
342
- }
343
-
344
- th,
345
- td {
346
- text-align: left;
347
- padding: 0.4rem 0.6rem;
348
- border-bottom: 1px solid var(--border);
349
- }
350
-
351
- th {
352
- color: var(--muted);
353
- font-size: 11px;
354
- text-transform: uppercase;
355
- font-weight: 600;
356
- }
357
-
358
- tbody tr:hover {
359
- background: rgba(255, 255, 255, 0.03);
360
- }
361
-
362
- tbody tr.clickable {
363
- cursor: pointer;
364
- }
365
-
366
- tbody tr.dag-node-selected {
367
- background: rgba(88, 166, 255, 0.12);
368
- }
369
-
370
- .timeline-node-label {
371
- color: var(--muted);
372
- font-size: 12px;
373
- margin: 0 0 0.5rem;
374
- }
375
-
376
- .process-timeline-scroll {
377
- max-height: 320px;
378
- overflow-y: auto;
379
- border: 1px solid var(--border);
380
- border-radius: 4px;
381
- background: var(--bg);
382
- }
383
-
384
- .process-timeline {
385
- list-style: none;
386
- margin: 0;
387
- padding: 0.25rem 0;
388
- }
389
-
390
- .process-timeline-item {
391
- display: flex;
392
- gap: 0.75rem;
393
- padding: 0.35rem 0.75rem;
394
- border-bottom: 1px solid var(--border);
395
- font-size: 12px;
396
- }
397
-
398
- .process-timeline-item:last-child {
399
- border-bottom: none;
400
- }
401
-
402
- .process-timeline-time {
403
- flex: 0 0 4.5rem;
404
- color: var(--muted);
405
- font-variant-numeric: tabular-nums;
406
- }
407
-
408
- .process-timeline-label {
409
- flex: 1;
410
- word-break: break-word;
411
- }
412
-
413
- tbody tr.active-task {
414
- background: rgba(210, 153, 34, 0.12);
415
- }
416
-
417
- .badge {
418
- display: inline-block;
419
- padding: 0.1rem 0.45rem;
420
- border-radius: 4px;
421
- font-size: 11px;
422
- font-weight: 600;
423
- }
424
-
425
- .badge-succeeded,
426
- .badge-finished,
427
- .badge-completed,
428
- .badge-done {
429
- background: rgba(63, 185, 80, 0.2);
430
- color: var(--green);
431
- }
432
-
433
- .badge-failed,
434
- .badge-error {
435
- background: rgba(248, 81, 73, 0.2);
436
- color: var(--red);
437
- }
438
-
439
- .badge-partial-failed {
440
- background: rgba(248, 81, 73, 0.15);
441
- color: #ff9a8b;
442
- border: 1px solid rgba(248, 81, 73, 0.35);
443
- }
444
-
445
- .badge-running,
446
- .badge-started {
447
- background: rgba(210, 153, 34, 0.2);
448
- color: var(--yellow);
449
- }
450
-
451
- .badge-skipped {
452
- background: rgba(139, 148, 158, 0.15);
453
- color: var(--muted);
454
- }
455
-
456
- .badge-stale {
457
- background: rgba(139, 58, 58, 0.3);
458
- color: #e06c75;
459
- }
460
-
461
- .badge-reused {
462
- background: rgba(88, 166, 255, 0.2);
463
- color: var(--blue);
464
- }
465
-
466
- .badge-pending,
467
- .badge-blocked,
468
- .badge-unknown,
469
- .badge-queued {
470
- background: rgba(139, 148, 158, 0.15);
471
- color: var(--muted);
472
- }
473
-
474
- .liveness-active {
475
- background: rgba(63, 185, 80, 0.2);
476
- color: var(--green);
477
- }
478
-
479
- .liveness-quiet {
480
- background: rgba(210, 153, 34, 0.2);
481
- color: var(--yellow);
482
- }
483
-
484
- .liveness-stale {
485
- background: rgba(139, 58, 58, 0.3);
486
- color: #e06c75;
487
- }
488
-
489
- .liveness-timeout-risk {
490
- background: rgba(248, 81, 73, 0.25);
491
- color: var(--red);
492
- }
493
-
494
- .rank-lanes {
495
- display: flex;
496
- flex-direction: column;
497
- gap: 0.75rem;
498
- }
499
-
500
- .rank-lane {
501
- border-left: 3px solid var(--border);
502
- padding-left: 0.75rem;
503
- }
504
-
505
- .rank-lane-label {
506
- color: var(--muted);
507
- font-size: 11px;
508
- font-weight: 600;
509
- margin-bottom: 0.35rem;
510
- }
511
-
512
- .rank-lane-nodes {
513
- display: flex;
514
- flex-wrap: wrap;
515
- gap: 0.5rem;
516
- }
517
-
518
- .rank-node-chip {
519
- display: inline-flex;
520
- align-items: center;
521
- gap: 0.35rem;
522
- background: var(--bg);
523
- border: 1px solid var(--border);
524
- border-radius: 4px;
525
- padding: 0.25rem 0.5rem;
526
- font-size: 12px;
527
- }
528
-
529
- .rank-node-active {
530
- border-color: var(--yellow);
531
- background: rgba(210, 153, 34, 0.12);
532
- }
533
-
534
- .rank-node-error {
535
- border-color: var(--red);
536
- background: rgba(248, 81, 73, 0.1);
537
- }
538
-
539
- .rank-node-id {
540
- font-weight: 600;
541
- }
542
-
543
- .rank-node-dur {
544
- color: var(--muted);
545
- font-size: 10px;
546
- }
547
-
548
- .step-timeline {
549
- list-style: none;
550
- margin: 0;
551
- padding: 0;
552
- }
553
-
554
- .step-timeline li {
555
- display: flex;
556
- align-items: center;
557
- gap: 0.75rem;
558
- padding: 0.35rem 0;
559
- border-bottom: 1px solid var(--border);
560
- }
561
-
562
- .step-timeline li:last-child {
563
- border-bottom: none;
564
- }
565
-
566
- .step-marker {
567
- width: 1.5rem;
568
- text-align: center;
569
- font-weight: 700;
570
- }
571
-
572
- .step-marker.succeeded {
573
- color: var(--green);
574
- }
575
-
576
- .step-marker.running {
577
- color: var(--yellow);
578
- }
579
-
580
- .step-marker.pending {
581
- color: var(--muted);
582
- }
583
-
584
- .step-marker.failed {
585
- color: var(--red);
586
- }
587
-
588
- .step-name {
589
- flex: 1;
590
- }
591
-
592
- .step-duration {
593
- color: var(--muted);
594
- font-size: 11px;
595
- }
596
-
597
- .output-block {
598
- background: var(--bg);
599
- border: 1px solid var(--border);
600
- border-radius: 4px;
601
- padding: 0.5rem 0.75rem;
602
- white-space: pre-wrap;
603
- word-break: break-all;
604
- max-height: 240px;
605
- overflow-y: auto;
606
- font-size: 12px;
607
- }
608
-
609
- .output-preview {
610
- margin: 0.5rem 0;
611
- }
612
-
613
- .output-preview summary {
614
- cursor: pointer;
615
- color: var(--link);
616
- font-size: 12px;
617
- margin-bottom: 0.25rem;
618
- }
619
-
620
- .error-preview summary {
621
- color: var(--red);
622
- }
623
-
624
- .artifact-list {
625
- list-style: none;
626
- margin: 0;
627
- padding: 0;
628
- }
629
-
630
- .artifact-list li {
631
- padding: 0.25rem 0;
632
- }
633
-
634
- .artifact-list a {
635
- color: var(--link);
636
- text-decoration: none;
637
- }
638
-
639
- .artifact-list a:hover {
640
- text-decoration: underline;
641
- }
642
-
643
- .empty {
644
- color: var(--muted);
645
- font-style: italic;
646
- }
647
-
648
- .link {
649
- color: var(--link);
650
- text-decoration: none;
651
- }
652
-
653
- .link:hover {
654
- text-decoration: underline;
655
- }
656
-
657
- [hidden] {
658
- display: none !important;
659
- }
660
-
661
- .dag-active-node {
662
- background: rgba(210, 153, 34, 0.18);
663
- }
664
-
665
- .dag-error-node {
666
- background: rgba(248, 81, 73, 0.12);
667
- }
668
-
669
- tbody tr.dag-active-node:hover {
670
- background: rgba(210, 153, 34, 0.25);
671
- }
672
-
673
- tbody tr.dag-error-node:hover {
674
- background: rgba(248, 81, 73, 0.18);
675
- }
676
-
677
- .dag-output-node {
678
- margin: 0.5rem 0 0.25rem;
679
- font-size: 12px;
680
- color: var(--muted);
681
- }
682
-
683
- .step-link {
684
- margin-left: 0.5rem;
685
- color: var(--link);
686
- text-decoration: none;
687
- font-size: 11px;
688
- }
689
-
690
- .step-link:hover {
691
- text-decoration: underline;
692
- }
693
-
694
- .inbox-link,
695
- .artifact-link {
696
- color: var(--link);
697
- text-decoration: none;
698
- font-size: 11px;
699
- }
700
-
701
- .inbox-link:hover,
702
- .artifact-link:hover {
703
- text-decoration: underline;
704
- }
705
-
706
- .artifact-links {
707
- display: inline-flex;
708
- flex-wrap: wrap;
709
- gap: 6px;
710
- }
711
-
712
- .artifact-links .muted {
713
- color: var(--muted);
714
- }
715
-
716
- #failures-list .row-failed {
717
- border-left: 3px solid var(--red, #e06c75);
718
- }
719
-
720
- #failures-list .row-blocked {
721
- border-left: 3px solid var(--yellow, #d19a66);
722
- }
1
+ :root {
2
+ --bg: #0d1117;
3
+ --surface: #161b22;
4
+ --surface-raised: #1c2128;
5
+ --border: #30363d;
6
+ --text: #c9d1d9;
7
+ --muted: #8b949e;
8
+ --link: #58a6ff;
9
+ --green: #3fb950;
10
+ --red: #f85149;
11
+ --yellow: #d29922;
12
+ --blue: #58a6ff;
13
+ --gray-red: #8b3a3a;
14
+ --space-1: 4px;
15
+ --space-2: 8px;
16
+ --space-3: 12px;
17
+ --space-4: 16px;
18
+ --space-6: 24px;
19
+ --space-8: 32px;
20
+ --radius-sm: 4px;
21
+ --radius-md: 8px;
22
+ --radius-lg: 12px;
23
+ --shadow-1: 0 4px 18px rgba(0, 0, 0, 0.2);
24
+ --transition-fast: 140ms ease;
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ margin: 0;
33
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
34
+ font-size: 13px;
35
+ line-height: 1.5;
36
+ background: var(--bg);
37
+ color: var(--text);
38
+ }
39
+
40
+ code,
41
+ pre,
42
+ .output-block,
43
+ .rank-node-id,
44
+ .dag-node-label,
45
+ .dag-node-meta,
46
+ .dag-node-status,
47
+ td:first-child,
48
+ .meta-item dd {
49
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
50
+ }
51
+
52
+ #app {
53
+ max-width: 1200px;
54
+ margin: 0 auto;
55
+ padding: 1rem 1.5rem 2rem;
56
+ }
57
+
58
+ .site-header {
59
+ display: flex;
60
+ flex-wrap: wrap;
61
+ align-items: center;
62
+ gap: 1rem 1.5rem;
63
+ margin-bottom: 1.5rem;
64
+ padding-bottom: 0.75rem;
65
+ border-bottom: 1px solid var(--border);
66
+ }
67
+
68
+ .logo {
69
+ color: var(--text);
70
+ text-decoration: none;
71
+ font-weight: 600;
72
+ }
73
+
74
+ .logo:hover {
75
+ color: var(--link);
76
+ }
77
+
78
+ .top-nav {
79
+ display: flex;
80
+ gap: 1rem;
81
+ }
82
+
83
+ .nav-link {
84
+ color: var(--muted);
85
+ text-decoration: none;
86
+ font-size: 12px;
87
+ }
88
+
89
+ .nav-link:hover {
90
+ color: var(--link);
91
+ }
92
+
93
+ .header-refresh {
94
+ margin-left: auto;
95
+ color: var(--muted);
96
+ font-size: 11px;
97
+ }
98
+
99
+ .breadcrumb {
100
+ flex-basis: 100%;
101
+ color: var(--muted);
102
+ font-size: 12px;
103
+ }
104
+
105
+ .breadcrumb a {
106
+ color: var(--link);
107
+ text-decoration: none;
108
+ }
109
+
110
+ .breadcrumb a:hover {
111
+ text-decoration: underline;
112
+ }
113
+
114
+ h2 {
115
+ margin: 0 0 0.75rem;
116
+ font-size: 1.1rem;
117
+ font-weight: 600;
118
+ }
119
+
120
+ h3,
121
+ .section-heading {
122
+ margin: 0 0 0.5rem;
123
+ font-size: 0.95rem;
124
+ font-weight: 600;
125
+ }
126
+
127
+ .panel {
128
+ background: var(--surface);
129
+ border: 1px solid var(--border);
130
+ border-radius: var(--radius-md);
131
+ padding: 0.75rem 1rem;
132
+ margin-bottom: 1rem;
133
+ }
134
+
135
+ .dag-graph-wrap {
136
+ display: grid;
137
+ gap: var(--space-2);
138
+ }
139
+
140
+ .dag-graph-toolbar {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ gap: var(--space-3);
145
+ }
146
+
147
+ .dag-graph-summary,
148
+ .dag-graph-fallback {
149
+ color: var(--muted);
150
+ font-size: 12px;
151
+ }
152
+
153
+ .dag-graph-fallback { margin: 0; }
154
+
155
+ .dag-graph-reset {
156
+ appearance: none;
157
+ border: 1px solid var(--border);
158
+ border-radius: var(--radius-sm);
159
+ background: var(--surface-raised);
160
+ color: var(--text);
161
+ padding: 6px 10px;
162
+ cursor: pointer;
163
+ }
164
+
165
+ .dag-graph-reset:hover,
166
+ .dag-graph-reset:focus-visible { border-color: var(--link); }
167
+
168
+ .dag-graph-viewport {
169
+ min-height: 220px;
170
+ max-height: 620px;
171
+ overflow: auto;
172
+ border: 1px solid var(--border);
173
+ border-radius: var(--radius-md);
174
+ background: radial-gradient(circle at 1px 1px, rgba(139, 148, 158, 0.18) 1px, transparent 0) 0 0 / 20px 20px, var(--bg);
175
+ scroll-behavior: smooth;
176
+ }
177
+
178
+ .dag-graph { display: block; }
179
+ .dag-edge { fill: none; stroke: var(--muted); stroke-width: 2; opacity: 0.75; transition: opacity var(--transition-fast); }
180
+ .dag-edge + .dag-edge { stroke-width: 1.8; }
181
+ #dag-arrow path { fill: var(--muted); }
182
+ .dag-graph-node { cursor: pointer; transition: opacity var(--transition-fast); }
183
+ .dag-graph-node rect { fill: var(--surface-raised); stroke: var(--border); stroke-width: 1.5; }
184
+ .dag-graph-node:hover rect,
185
+ .dag-graph-node:focus-visible rect { stroke: var(--link); stroke-width: 2.5; }
186
+ .dag-graph-node:focus { outline: none; }
187
+ .dag-graph-node.selected rect { stroke: var(--link); stroke-width: 3; filter: drop-shadow(0 0 6px rgba(88, 166, 255, 0.45)); }
188
+ .dag-graph-node.status-running rect,
189
+ .dag-graph-node.status-started rect { stroke: var(--yellow); }
190
+ .dag-graph-node.status-failed rect,
191
+ .dag-graph-node.status-error rect { stroke: var(--red); fill: rgba(248, 81, 73, 0.1); }
192
+ .dag-graph-node.status-skipped rect { stroke-dasharray: 5 4; }
193
+ .dag-node-label { fill: var(--text); font-size: 13px; font-weight: 700; }
194
+ .dag-node-meta { fill: var(--muted); font-size: 11px; }
195
+ .dag-node-status { fill: var(--text); font-size: 11px; }
196
+ .dag-dimmed { opacity: 0.25; }
197
+ .dag-graph-node.status-running { animation: graph-node-pulse 2s ease-in-out infinite; }
198
+
199
+ @keyframes graph-node-pulse {
200
+ 50% { filter: drop-shadow(0 0 7px rgba(210, 153, 34, 0.45)); }
201
+ }
202
+
203
+ @media (max-width: 680px) {
204
+ #app { padding-inline: var(--space-3); }
205
+ .meta-grid { grid-template-columns: 1fr; }
206
+ .dag-graph-viewport { max-height: 480px; }
207
+ table { min-width: 760px; }
208
+ .panel { overflow-x: auto; }
209
+ }
210
+
211
+ @media (prefers-reduced-motion: reduce) {
212
+ *, *::before, *::after {
213
+ animation-duration: 0.01ms !important;
214
+ animation-iteration-count: 1 !important;
215
+ scroll-behavior: auto !important;
216
+ transition-duration: 0.01ms !important;
217
+ }
218
+ }
219
+
220
+ .kpi-grid {
221
+ display: grid;
222
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
223
+ gap: 0.75rem;
224
+ padding: 0.75rem;
225
+ }
226
+
227
+ .kpi-card {
228
+ background: var(--bg);
229
+ border: 1px solid var(--border);
230
+ border-radius: 6px;
231
+ padding: 0.6rem 0.75rem;
232
+ text-align: center;
233
+ }
234
+
235
+ .kpi-card.kpi-highlight {
236
+ border-color: var(--yellow);
237
+ background: rgba(210, 153, 34, 0.08);
238
+ }
239
+
240
+ .kpi-card.kpi-danger {
241
+ border-color: var(--red);
242
+ }
243
+
244
+ .kpi-label {
245
+ color: var(--muted);
246
+ font-size: 10px;
247
+ text-transform: uppercase;
248
+ margin-bottom: 0.25rem;
249
+ }
250
+
251
+ .kpi-value {
252
+ font-size: 1.4rem;
253
+ font-weight: 700;
254
+ }
255
+
256
+ .active-dag-list {
257
+ display: flex;
258
+ flex-direction: column;
259
+ gap: 0.75rem;
260
+ }
261
+
262
+ .active-dag-card {
263
+ background: var(--bg);
264
+ border: 1px solid var(--border);
265
+ border-radius: 6px;
266
+ padding: 0.75rem 1rem;
267
+ }
268
+
269
+ .active-dag-card-running {
270
+ border-color: var(--yellow);
271
+ box-shadow: 0 0 0 1px rgba(210, 153, 34, 0.25);
272
+ animation: pulse-border 2s ease-in-out infinite;
273
+ }
274
+
275
+ @keyframes pulse-border {
276
+ 0%,
277
+ 100% {
278
+ border-color: var(--yellow);
279
+ }
280
+ 50% {
281
+ border-color: rgba(210, 153, 34, 0.5);
282
+ }
283
+ }
284
+
285
+ .active-dag-header {
286
+ display: flex;
287
+ align-items: center;
288
+ gap: 0.75rem;
289
+ margin-bottom: 0.35rem;
290
+ }
291
+
292
+ .active-dag-title {
293
+ font-weight: 600;
294
+ flex: 1;
295
+ }
296
+
297
+ .active-dag-meta {
298
+ display: flex;
299
+ flex-direction: column;
300
+ gap: 0.2rem;
301
+ color: var(--muted);
302
+ font-size: 12px;
303
+ margin-bottom: 0.5rem;
304
+ }
305
+
306
+ .active-dag-link {
307
+ color: var(--link);
308
+ text-decoration: none;
309
+ font-size: 12px;
310
+ }
311
+
312
+ .active-dag-link:hover {
313
+ text-decoration: underline;
314
+ }
315
+
316
+ .dashboard-footer {
317
+ margin-top: 0.75rem;
318
+ color: var(--muted);
319
+ font-size: 12px;
320
+ }
321
+
322
+ .meta-grid {
323
+ display: grid;
324
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
325
+ gap: 0.5rem 1.5rem;
326
+ }
327
+
328
+ .meta-item dt {
329
+ color: var(--muted);
330
+ font-size: 11px;
331
+ text-transform: uppercase;
332
+ margin: 0;
333
+ }
334
+
335
+ .meta-item dd {
336
+ margin: 0.15rem 0 0;
337
+ }
338
+
339
+ table {
340
+ width: 100%;
341
+ border-collapse: collapse;
342
+ }
343
+
344
+ th,
345
+ td {
346
+ text-align: left;
347
+ padding: 0.4rem 0.6rem;
348
+ border-bottom: 1px solid var(--border);
349
+ }
350
+
351
+ th {
352
+ color: var(--muted);
353
+ font-size: 11px;
354
+ text-transform: uppercase;
355
+ font-weight: 600;
356
+ }
357
+
358
+ tbody tr:hover {
359
+ background: rgba(255, 255, 255, 0.03);
360
+ }
361
+
362
+ tbody tr.clickable {
363
+ cursor: pointer;
364
+ }
365
+
366
+ tbody tr.dag-node-selected {
367
+ background: rgba(88, 166, 255, 0.12);
368
+ }
369
+
370
+ .timeline-node-label {
371
+ color: var(--muted);
372
+ font-size: 12px;
373
+ margin: 0 0 0.5rem;
374
+ }
375
+
376
+ .process-timeline-scroll {
377
+ max-height: 320px;
378
+ overflow-y: auto;
379
+ border: 1px solid var(--border);
380
+ border-radius: 4px;
381
+ background: var(--bg);
382
+ }
383
+
384
+ .process-timeline {
385
+ list-style: none;
386
+ margin: 0;
387
+ padding: 0.25rem 0;
388
+ }
389
+
390
+ .process-timeline-item {
391
+ display: flex;
392
+ gap: 0.75rem;
393
+ padding: 0.35rem 0.75rem;
394
+ border-bottom: 1px solid var(--border);
395
+ font-size: 12px;
396
+ }
397
+
398
+ .process-timeline-item:last-child {
399
+ border-bottom: none;
400
+ }
401
+
402
+ .process-timeline-time {
403
+ flex: 0 0 4.5rem;
404
+ color: var(--muted);
405
+ font-variant-numeric: tabular-nums;
406
+ }
407
+
408
+ .process-timeline-label {
409
+ flex: 1;
410
+ word-break: break-word;
411
+ }
412
+
413
+ tbody tr.active-task {
414
+ background: rgba(210, 153, 34, 0.12);
415
+ }
416
+
417
+ .badge {
418
+ display: inline-block;
419
+ padding: 0.1rem 0.45rem;
420
+ border-radius: 4px;
421
+ font-size: 11px;
422
+ font-weight: 600;
423
+ }
424
+
425
+ .badge-succeeded,
426
+ .badge-finished,
427
+ .badge-completed,
428
+ .badge-done {
429
+ background: rgba(63, 185, 80, 0.2);
430
+ color: var(--green);
431
+ }
432
+
433
+ .badge-failed,
434
+ .badge-error {
435
+ background: rgba(248, 81, 73, 0.2);
436
+ color: var(--red);
437
+ }
438
+
439
+ .badge-partial-failed {
440
+ background: rgba(248, 81, 73, 0.15);
441
+ color: #ff9a8b;
442
+ border: 1px solid rgba(248, 81, 73, 0.35);
443
+ }
444
+
445
+ .badge-running,
446
+ .badge-started {
447
+ background: rgba(210, 153, 34, 0.2);
448
+ color: var(--yellow);
449
+ }
450
+
451
+ .badge-skipped {
452
+ background: rgba(139, 148, 158, 0.15);
453
+ color: var(--muted);
454
+ }
455
+
456
+ .badge-stale {
457
+ background: rgba(139, 58, 58, 0.3);
458
+ color: #e06c75;
459
+ }
460
+
461
+ .badge-reused {
462
+ background: rgba(88, 166, 255, 0.2);
463
+ color: var(--blue);
464
+ }
465
+
466
+ .badge-pending,
467
+ .badge-blocked,
468
+ .badge-unknown,
469
+ .badge-queued {
470
+ background: rgba(139, 148, 158, 0.15);
471
+ color: var(--muted);
472
+ }
473
+
474
+ .liveness-active {
475
+ background: rgba(63, 185, 80, 0.2);
476
+ color: var(--green);
477
+ }
478
+
479
+ .liveness-quiet {
480
+ background: rgba(210, 153, 34, 0.2);
481
+ color: var(--yellow);
482
+ }
483
+
484
+ .liveness-stale {
485
+ background: rgba(139, 58, 58, 0.3);
486
+ color: #e06c75;
487
+ }
488
+
489
+ .liveness-timeout-risk {
490
+ background: rgba(248, 81, 73, 0.25);
491
+ color: var(--red);
492
+ }
493
+
494
+ .rank-lanes {
495
+ display: flex;
496
+ flex-direction: column;
497
+ gap: 0.75rem;
498
+ }
499
+
500
+ .rank-lane {
501
+ border-left: 3px solid var(--border);
502
+ padding-left: 0.75rem;
503
+ }
504
+
505
+ .rank-lane-label {
506
+ color: var(--muted);
507
+ font-size: 11px;
508
+ font-weight: 600;
509
+ margin-bottom: 0.35rem;
510
+ }
511
+
512
+ .rank-lane-nodes {
513
+ display: flex;
514
+ flex-wrap: wrap;
515
+ gap: 0.5rem;
516
+ }
517
+
518
+ .rank-node-chip {
519
+ display: inline-flex;
520
+ align-items: center;
521
+ gap: 0.35rem;
522
+ background: var(--bg);
523
+ border: 1px solid var(--border);
524
+ border-radius: 4px;
525
+ padding: 0.25rem 0.5rem;
526
+ font-size: 12px;
527
+ }
528
+
529
+ .rank-node-active {
530
+ border-color: var(--yellow);
531
+ background: rgba(210, 153, 34, 0.12);
532
+ }
533
+
534
+ .rank-node-error {
535
+ border-color: var(--red);
536
+ background: rgba(248, 81, 73, 0.1);
537
+ }
538
+
539
+ .rank-node-id {
540
+ font-weight: 600;
541
+ }
542
+
543
+ .rank-node-dur {
544
+ color: var(--muted);
545
+ font-size: 10px;
546
+ }
547
+
548
+ .step-timeline {
549
+ list-style: none;
550
+ margin: 0;
551
+ padding: 0;
552
+ }
553
+
554
+ .step-timeline li {
555
+ display: flex;
556
+ align-items: center;
557
+ gap: 0.75rem;
558
+ padding: 0.35rem 0;
559
+ border-bottom: 1px solid var(--border);
560
+ }
561
+
562
+ .step-timeline li:last-child {
563
+ border-bottom: none;
564
+ }
565
+
566
+ .step-marker {
567
+ width: 1.5rem;
568
+ text-align: center;
569
+ font-weight: 700;
570
+ }
571
+
572
+ .step-marker.succeeded {
573
+ color: var(--green);
574
+ }
575
+
576
+ .step-marker.running {
577
+ color: var(--yellow);
578
+ }
579
+
580
+ .step-marker.pending {
581
+ color: var(--muted);
582
+ }
583
+
584
+ .step-marker.failed {
585
+ color: var(--red);
586
+ }
587
+
588
+ .step-name {
589
+ flex: 1;
590
+ }
591
+
592
+ .step-duration {
593
+ color: var(--muted);
594
+ font-size: 11px;
595
+ }
596
+
597
+ .output-block {
598
+ background: var(--bg);
599
+ border: 1px solid var(--border);
600
+ border-radius: 4px;
601
+ padding: 0.5rem 0.75rem;
602
+ white-space: pre-wrap;
603
+ word-break: break-all;
604
+ max-height: 240px;
605
+ overflow-y: auto;
606
+ font-size: 12px;
607
+ }
608
+
609
+ .output-preview {
610
+ margin: 0.5rem 0;
611
+ }
612
+
613
+ .output-preview summary {
614
+ cursor: pointer;
615
+ color: var(--link);
616
+ font-size: 12px;
617
+ margin-bottom: 0.25rem;
618
+ }
619
+
620
+ .error-preview summary {
621
+ color: var(--red);
622
+ }
623
+
624
+ .artifact-list {
625
+ list-style: none;
626
+ margin: 0;
627
+ padding: 0;
628
+ }
629
+
630
+ .artifact-list li {
631
+ padding: 0.25rem 0;
632
+ }
633
+
634
+ .artifact-list a {
635
+ color: var(--link);
636
+ text-decoration: none;
637
+ }
638
+
639
+ .artifact-list a:hover {
640
+ text-decoration: underline;
641
+ }
642
+
643
+ .empty {
644
+ color: var(--muted);
645
+ font-style: italic;
646
+ }
647
+
648
+ .link {
649
+ color: var(--link);
650
+ text-decoration: none;
651
+ }
652
+
653
+ .link:hover {
654
+ text-decoration: underline;
655
+ }
656
+
657
+ [hidden] {
658
+ display: none !important;
659
+ }
660
+
661
+ .dag-active-node {
662
+ background: rgba(210, 153, 34, 0.18);
663
+ }
664
+
665
+ .dag-error-node {
666
+ background: rgba(248, 81, 73, 0.12);
667
+ }
668
+
669
+ tbody tr.dag-active-node:hover {
670
+ background: rgba(210, 153, 34, 0.25);
671
+ }
672
+
673
+ tbody tr.dag-error-node:hover {
674
+ background: rgba(248, 81, 73, 0.18);
675
+ }
676
+
677
+ .dag-output-node {
678
+ margin: 0.5rem 0 0.25rem;
679
+ font-size: 12px;
680
+ color: var(--muted);
681
+ }
682
+
683
+ .step-link {
684
+ margin-left: 0.5rem;
685
+ color: var(--link);
686
+ text-decoration: none;
687
+ font-size: 11px;
688
+ }
689
+
690
+ .step-link:hover {
691
+ text-decoration: underline;
692
+ }
693
+
694
+ .inbox-link,
695
+ .artifact-link {
696
+ color: var(--link);
697
+ text-decoration: none;
698
+ font-size: 11px;
699
+ }
700
+
701
+ .inbox-link:hover,
702
+ .artifact-link:hover {
703
+ text-decoration: underline;
704
+ }
705
+
706
+ .artifact-links {
707
+ display: inline-flex;
708
+ flex-wrap: wrap;
709
+ gap: 6px;
710
+ }
711
+
712
+ .artifact-links .muted {
713
+ color: var(--muted);
714
+ }
715
+
716
+ #failures-list .row-failed {
717
+ border-left: 3px solid var(--red, #e06c75);
718
+ }
719
+
720
+ #failures-list .row-blocked {
721
+ border-left: 3px solid var(--yellow, #d19a66);
722
+ }