@shipfox/api-workflows 12.2.0 → 12.3.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 (104) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +20 -0
  4. package/dist/config.d.ts +11 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +56 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/core/errors.d.ts +2 -1
  9. package/dist/core/errors.d.ts.map +1 -1
  10. package/dist/core/errors.js +3 -2
  11. package/dist/core/errors.js.map +1 -1
  12. package/dist/core/step-config/materialize-workflow-model.d.ts.map +1 -1
  13. package/dist/core/step-config/materialize-workflow-model.js +6 -4
  14. package/dist/core/step-config/materialize-workflow-model.js.map +1 -1
  15. package/dist/db/index.d.ts +2 -2
  16. package/dist/db/index.d.ts.map +1 -1
  17. package/dist/db/index.js +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/db/workflow-runs/jobs.d.ts.map +1 -1
  20. package/dist/db/workflow-runs/jobs.js +4 -0
  21. package/dist/db/workflow-runs/jobs.js.map +1 -1
  22. package/dist/db/workflow-runs/outbox.d.ts +1 -0
  23. package/dist/db/workflow-runs/outbox.d.ts.map +1 -1
  24. package/dist/db/workflow-runs/outbox.js +1 -0
  25. package/dist/db/workflow-runs/outbox.js.map +1 -1
  26. package/dist/db/workflow-runs/steps.d.ts +28 -0
  27. package/dist/db/workflow-runs/steps.d.ts.map +1 -1
  28. package/dist/db/workflow-runs/steps.js +49 -1
  29. package/dist/db/workflow-runs/steps.js.map +1 -1
  30. package/dist/db/workflow-runs.d.ts +2 -2
  31. package/dist/db/workflow-runs.d.ts.map +1 -1
  32. package/dist/db/workflow-runs.js +1 -1
  33. package/dist/db/workflow-runs.js.map +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +4 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/metrics/instance.d.ts +1 -0
  38. package/dist/metrics/instance.d.ts.map +1 -1
  39. package/dist/metrics/instance.js +8 -0
  40. package/dist/metrics/instance.js.map +1 -1
  41. package/dist/presentation/dto/evaluation-trace.d.ts +4 -0
  42. package/dist/presentation/dto/evaluation-trace.d.ts.map +1 -0
  43. package/dist/presentation/dto/evaluation-trace.js +39 -0
  44. package/dist/presentation/dto/evaluation-trace.js.map +1 -0
  45. package/dist/presentation/dto/index.d.ts +2 -1
  46. package/dist/presentation/dto/index.d.ts.map +1 -1
  47. package/dist/presentation/dto/index.js +2 -1
  48. package/dist/presentation/dto/index.js.map +1 -1
  49. package/dist/presentation/dto/job.d.ts.map +1 -1
  50. package/dist/presentation/dto/job.js +6 -0
  51. package/dist/presentation/dto/job.js.map +1 -1
  52. package/dist/presentation/dto/step.d.ts +2 -1
  53. package/dist/presentation/dto/step.d.ts.map +1 -1
  54. package/dist/presentation/dto/step.js +12 -0
  55. package/dist/presentation/dto/step.js.map +1 -1
  56. package/dist/presentation/index.d.ts +1 -1
  57. package/dist/presentation/index.d.ts.map +1 -1
  58. package/dist/presentation/index.js +1 -1
  59. package/dist/presentation/index.js.map +1 -1
  60. package/dist/presentation/routes/get-step-attempt-detail.d.ts +3 -0
  61. package/dist/presentation/routes/get-step-attempt-detail.d.ts.map +1 -0
  62. package/dist/presentation/routes/get-step-attempt-detail.js +38 -0
  63. package/dist/presentation/routes/get-step-attempt-detail.js.map +1 -0
  64. package/dist/presentation/routes/index.d.ts.map +1 -1
  65. package/dist/presentation/routes/index.js +2 -0
  66. package/dist/presentation/routes/index.js.map +1 -1
  67. package/dist/presentation/subscribers/index.d.ts +1 -0
  68. package/dist/presentation/subscribers/index.d.ts.map +1 -1
  69. package/dist/presentation/subscribers/index.js +1 -0
  70. package/dist/presentation/subscribers/index.js.map +1 -1
  71. package/dist/presentation/subscribers/on-failure-annotations.d.ts +5 -0
  72. package/dist/presentation/subscribers/on-failure-annotations.d.ts.map +1 -0
  73. package/dist/presentation/subscribers/on-failure-annotations.js +160 -0
  74. package/dist/presentation/subscribers/on-failure-annotations.js.map +1 -0
  75. package/dist/tsconfig.test.tsbuildinfo +1 -1
  76. package/package.json +10 -8
  77. package/src/config.test.ts +85 -0
  78. package/src/config.ts +71 -0
  79. package/src/core/errors.ts +9 -2
  80. package/src/core/job-execution.test.ts +1 -0
  81. package/src/core/step-config/materialize-workflow-model.catalog.test.ts +117 -0
  82. package/src/core/step-config/materialize-workflow-model.ts +5 -3
  83. package/src/db/index.ts +4 -0
  84. package/src/db/workflow-runs/job-status.test.ts +10 -3
  85. package/src/db/workflow-runs/jobs.ts +7 -0
  86. package/src/db/workflow-runs/outbox.ts +7 -1
  87. package/src/db/workflow-runs/steps.test.ts +148 -0
  88. package/src/db/workflow-runs/steps.ts +105 -1
  89. package/src/db/workflow-runs.ts +4 -0
  90. package/src/index.ts +9 -0
  91. package/src/metrics/instance.ts +10 -0
  92. package/src/presentation/dto/evaluation-trace.ts +27 -0
  93. package/src/presentation/dto/index.ts +2 -1
  94. package/src/presentation/dto/job.ts +6 -0
  95. package/src/presentation/dto/step.test.ts +96 -1
  96. package/src/presentation/dto/step.ts +17 -0
  97. package/src/presentation/index.ts +2 -0
  98. package/src/presentation/routes/get-step-attempt-detail.test.ts +159 -0
  99. package/src/presentation/routes/get-step-attempt-detail.ts +33 -0
  100. package/src/presentation/routes/index.ts +2 -0
  101. package/src/presentation/subscribers/index.ts +4 -0
  102. package/src/presentation/subscribers/on-failure-annotations.test.ts +510 -0
  103. package/src/presentation/subscribers/on-failure-annotations.ts +237 -0
  104. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/subscribers/index.ts"],"sourcesContent":["export {onJobEventDelivered} from './on-job-event-delivered.js';\nexport {onJobStepsSettled} from './on-job-steps-settled.js';\nexport {onRunnerJobClaimed} from './on-runner-job-claimed.js';\nexport {onRunnerJobLeaseExpired} from './on-runner-job-lease-expired.js';\nexport {onRunnerJobQueued} from './on-runner-job-queued.js';\nexport {onWorkflowRunAttemptCreated} from './on-workflow-run-attempt-created.js';\nexport {onWorkflowRunCancelled} from './on-workflow-run-cancelled.js';\n"],"names":["onJobEventDelivered","onJobStepsSettled","onRunnerJobClaimed","onRunnerJobLeaseExpired","onRunnerJobQueued","onWorkflowRunAttemptCreated","onWorkflowRunCancelled"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,8BAA8B;AAChE,SAAQC,iBAAiB,QAAO,4BAA4B;AAC5D,SAAQC,kBAAkB,QAAO,6BAA6B;AAC9D,SAAQC,uBAAuB,QAAO,mCAAmC;AACzE,SAAQC,iBAAiB,QAAO,4BAA4B;AAC5D,SAAQC,2BAA2B,QAAO,uCAAuC;AACjF,SAAQC,sBAAsB,QAAO,iCAAiC"}
1
+ {"version":3,"sources":["../../../src/presentation/subscribers/index.ts"],"sourcesContent":["export {\n onJobTerminatedFailureAnnotation,\n onStepAttemptTerminatedFailureAnnotation,\n} from './on-failure-annotations.js';\nexport {onJobEventDelivered} from './on-job-event-delivered.js';\nexport {onJobStepsSettled} from './on-job-steps-settled.js';\nexport {onRunnerJobClaimed} from './on-runner-job-claimed.js';\nexport {onRunnerJobLeaseExpired} from './on-runner-job-lease-expired.js';\nexport {onRunnerJobQueued} from './on-runner-job-queued.js';\nexport {onWorkflowRunAttemptCreated} from './on-workflow-run-attempt-created.js';\nexport {onWorkflowRunCancelled} from './on-workflow-run-cancelled.js';\n"],"names":["onJobTerminatedFailureAnnotation","onStepAttemptTerminatedFailureAnnotation","onJobEventDelivered","onJobStepsSettled","onRunnerJobClaimed","onRunnerJobLeaseExpired","onRunnerJobQueued","onWorkflowRunAttemptCreated","onWorkflowRunCancelled"],"mappings":"AAAA,SACEA,gCAAgC,EAChCC,wCAAwC,QACnC,8BAA8B;AACrC,SAAQC,mBAAmB,QAAO,8BAA8B;AAChE,SAAQC,iBAAiB,QAAO,4BAA4B;AAC5D,SAAQC,kBAAkB,QAAO,6BAA6B;AAC9D,SAAQC,uBAAuB,QAAO,mCAAmC;AACzE,SAAQC,iBAAiB,QAAO,4BAA4B;AAC5D,SAAQC,2BAA2B,QAAO,uCAAuC;AACjF,SAAQC,sBAAsB,QAAO,iCAAiC"}
@@ -0,0 +1,5 @@
1
+ import { type AnnotationsInterModuleClient } from '@shipfox/annotations-dto/inter-module';
2
+ import type { WorkflowsJobTerminatedEventDto, WorkflowsStepAttemptTerminatedEventDto } from '@shipfox/api-workflows-dto';
3
+ export declare function onStepAttemptTerminatedFailureAnnotation(annotations: AnnotationsInterModuleClient): (payload: WorkflowsStepAttemptTerminatedEventDto) => Promise<void>;
4
+ export declare function onJobTerminatedFailureAnnotation(annotations: AnnotationsInterModuleClient): (payload: WorkflowsJobTerminatedEventDto) => Promise<void>;
5
+ //# sourceMappingURL=on-failure-annotations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"on-failure-annotations.d.ts","sourceRoot":"","sources":["../../../src/presentation/subscribers/on-failure-annotations.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,4BAA4B,EAElC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EACV,8BAA8B,EAC9B,sCAAsC,EACvC,MAAM,4BAA4B,CAAC;AAapC,wBAAgB,wCAAwC,CACtD,WAAW,EAAE,4BAA4B,aAElB,sCAAsC,KAAG,OAAO,CAAC,IAAI,CAAC,CA8D9E;AAED,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,4BAA4B,aACjE,8BAA8B,KAAG,OAAO,CAAC,IAAI,CAAC,CA2CtE"}
@@ -0,0 +1,160 @@
1
+ import { annotationsInterModuleContract } from '@shipfox/annotations-dto/inter-module';
2
+ import { isInterModuleKnownError } from '@shipfox/inter-module';
3
+ import { logger } from '@shipfox/node-opentelemetry';
4
+ import { getJobExecutionFailureOrigin, getJobScope, getStepAttemptDetail, getWorkflowRunAttemptById } from '#db/index.js';
5
+ import { recordWorkflowFailureAnnotationFailed } from '#metrics/instance.js';
6
+ const JOB_FAILURE_ANNOTATION_REASONS = new Set([
7
+ 'timed_out',
8
+ 'runner_lost',
9
+ 'condition_errored'
10
+ ]);
11
+ export function onStepAttemptTerminatedFailureAnnotation(annotations) {
12
+ return async (payload)=>{
13
+ // A first successful/cancelled attempt cannot have a stale failure annotation.
14
+ // Keep later terminal attempts on the lookup path so recovery removes the
15
+ // annotation created by an earlier failed attempt.
16
+ if (payload.status !== undefined && payload.status !== 'failed' && payload.attempt === 1) {
17
+ return;
18
+ }
19
+ try {
20
+ const initialDetail = await getStepAttemptDetail({
21
+ stepId: payload.stepId,
22
+ attempt: payload.attempt
23
+ });
24
+ if (!initialDetail) return;
25
+ // The detail query joins the requested attempt to the step's current projection. A
26
+ // delayed event can therefore return an old attempt alongside a newer step status. Read
27
+ // the canonical current attempt before deciding whether to replace or remove the card.
28
+ const detail = initialDetail.attempt.attempt === initialDetail.step.currentAttempt ? initialDetail : await getStepAttemptDetail({
29
+ stepId: initialDetail.step.id,
30
+ attempt: initialDetail.step.currentAttempt
31
+ });
32
+ if (!detail || detail.attempt.attempt !== detail.step.currentAttempt) return;
33
+ const runAttempt = await getWorkflowRunAttemptById(payload.workflowRunAttemptId);
34
+ if (!runAttempt) return;
35
+ await writeFailureAnnotation({
36
+ annotations,
37
+ target: {
38
+ workspaceId: payload.workspaceId,
39
+ projectId: payload.projectId,
40
+ workflowRunId: payload.workflowRunId,
41
+ workflowRunAttempt: runAttempt.attempt,
42
+ workflowRunAttemptId: payload.workflowRunAttemptId,
43
+ jobId: payload.jobId,
44
+ jobExecutionId: detail.step.jobExecutionId,
45
+ originStepId: detail.step.id,
46
+ originStepAttempt: detail.attempt.attempt
47
+ },
48
+ context: failureContext('step', detail.step.id),
49
+ failed: detail.step.status === 'failed' || detail.attempt.status === 'failed' && detail.step.status !== 'succeeded' && detail.step.status !== 'cancelled',
50
+ body: stepFailureBody(detail.step.name, detail.attempt.error ?? detail.step.error, detail.attempt.exitCode)
51
+ });
52
+ } catch (error) {
53
+ recordFailureAnnotationFailure(error, 'lookup', {
54
+ stepId: payload.stepId,
55
+ jobId: payload.jobId
56
+ });
57
+ }
58
+ };
59
+ }
60
+ export function onJobTerminatedFailureAnnotation(annotations) {
61
+ return async (payload)=>{
62
+ // Step failures already have a step-scoped annotation. Job-scoped annotations
63
+ // are reserved for terminal causes where no step-level failure card exists.
64
+ const isConditionEvaluationFailure = payload.status === 'skipped' && payload.statusReason === 'condition_errored';
65
+ if (payload.status !== 'failed' && !isConditionEvaluationFailure || !JOB_FAILURE_ANNOTATION_REASONS.has(payload.statusReason ?? '')) {
66
+ return;
67
+ }
68
+ try {
69
+ const [scope, runAttempt] = await Promise.all([
70
+ getJobScope(payload.jobId),
71
+ getWorkflowRunAttemptById(payload.workflowRunAttemptId)
72
+ ]);
73
+ if (!scope || !runAttempt || !payload.jobExecutionId) return;
74
+ const origin = await getJobExecutionFailureOrigin(payload.jobExecutionId);
75
+ if (!origin) return;
76
+ await writeFailureAnnotation({
77
+ annotations,
78
+ target: {
79
+ workspaceId: scope.workspaceId,
80
+ projectId: scope.projectId,
81
+ workflowRunId: payload.workflowRunId,
82
+ workflowRunAttempt: runAttempt.attempt,
83
+ workflowRunAttemptId: payload.workflowRunAttemptId,
84
+ jobId: payload.jobId,
85
+ jobExecutionId: origin.jobExecutionId,
86
+ originStepId: origin.stepId,
87
+ originStepAttempt: origin.stepAttempt
88
+ },
89
+ context: failureContext('job', payload.jobId),
90
+ failed: true,
91
+ body: jobFailureBody(payload.statusReason, origin)
92
+ });
93
+ } catch (error) {
94
+ recordFailureAnnotationFailure(error, 'lookup', {
95
+ jobId: payload.jobId
96
+ });
97
+ }
98
+ };
99
+ }
100
+ /**
101
+ * Failure annotations are a best-effort projection. The workflow terminal fact is authoritative;
102
+ * projection lookup and writes are swallowed so they cannot change the workflow outcome. Every
103
+ * swallowed error emits a reason-labelled metric and a structured warning for operations.
104
+ */ async function writeFailureAnnotation(params) {
105
+ try {
106
+ await params.annotations.replaceOrRemoveAnnotation({
107
+ ...params.target,
108
+ context: params.context,
109
+ annotation: params.failed ? {
110
+ op: 'replace',
111
+ style: 'error',
112
+ body: params.body
113
+ } : {
114
+ op: 'remove'
115
+ }
116
+ });
117
+ } catch (error) {
118
+ const reason = isInterModuleKnownError(annotationsInterModuleContract.methods.replaceOrRemoveAnnotation, error) ? 'budget' : 'write';
119
+ recordFailureAnnotationFailure(error, reason, params.target);
120
+ }
121
+ }
122
+ function failureContext(kind, id) {
123
+ return `failure:${kind}:${id}`;
124
+ }
125
+ function stepFailureBody(name, error, exitCode) {
126
+ const reason = typeof error?.reason === 'string' ? error.reason : 'unknown';
127
+ const message = typeof error?.message === 'string' && error.message.trim() ? error.message : 'No failure message was recorded.';
128
+ return [
129
+ `**${name} failed**`,
130
+ '',
131
+ `Reason: \`${reason}\``,
132
+ `Exit code: \`${exitCode ?? 'none'}\``,
133
+ '',
134
+ message
135
+ ].join('\n');
136
+ }
137
+ function jobFailureBody(reason, origin) {
138
+ const progress = origin.attemptStatus ? `The job stopped while processing **${origin.stepName}**.` : `The job stopped before **${origin.stepName}** started.`;
139
+ const error = origin.attemptError ?? origin.stepError;
140
+ const message = typeof error?.message === 'string' && error.message.trim() ? error.message : null;
141
+ const exitCode = origin.attemptExitCode === null ? null : `Exit code: \`${origin.attemptExitCode}\``;
142
+ return [
143
+ `**Job failed before completion**`,
144
+ '',
145
+ progress,
146
+ `Reason: \`${reason ?? 'unknown'}\``,
147
+ message ? `Failure: ${message}` : null,
148
+ exitCode
149
+ ].filter(Boolean).join('\n');
150
+ }
151
+ function recordFailureAnnotationFailure(error, reason, context) {
152
+ recordWorkflowFailureAnnotationFailed(reason);
153
+ logger().warn({
154
+ error,
155
+ reason,
156
+ ...context
157
+ }, 'Failed to project workflow failure annotation');
158
+ }
159
+
160
+ //# sourceMappingURL=on-failure-annotations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/subscribers/on-failure-annotations.ts"],"sourcesContent":["import {\n type AnnotationsInterModuleClient,\n annotationsInterModuleContract,\n} from '@shipfox/annotations-dto/inter-module';\nimport type {\n WorkflowsJobTerminatedEventDto,\n WorkflowsStepAttemptTerminatedEventDto,\n} from '@shipfox/api-workflows-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {\n getJobExecutionFailureOrigin,\n getJobScope,\n getStepAttemptDetail,\n getWorkflowRunAttemptById,\n} from '#db/index.js';\nimport {recordWorkflowFailureAnnotationFailed} from '#metrics/instance.js';\n\nconst JOB_FAILURE_ANNOTATION_REASONS = new Set(['timed_out', 'runner_lost', 'condition_errored']);\n\nexport function onStepAttemptTerminatedFailureAnnotation(\n annotations: AnnotationsInterModuleClient,\n) {\n return async (payload: WorkflowsStepAttemptTerminatedEventDto): Promise<void> => {\n // A first successful/cancelled attempt cannot have a stale failure annotation.\n // Keep later terminal attempts on the lookup path so recovery removes the\n // annotation created by an earlier failed attempt.\n if (payload.status !== undefined && payload.status !== 'failed' && payload.attempt === 1) {\n return;\n }\n\n try {\n const initialDetail = await getStepAttemptDetail({\n stepId: payload.stepId,\n attempt: payload.attempt,\n });\n if (!initialDetail) return;\n\n // The detail query joins the requested attempt to the step's current projection. A\n // delayed event can therefore return an old attempt alongside a newer step status. Read\n // the canonical current attempt before deciding whether to replace or remove the card.\n const detail =\n initialDetail.attempt.attempt === initialDetail.step.currentAttempt\n ? initialDetail\n : await getStepAttemptDetail({\n stepId: initialDetail.step.id,\n attempt: initialDetail.step.currentAttempt,\n });\n if (!detail || detail.attempt.attempt !== detail.step.currentAttempt) return;\n\n const runAttempt = await getWorkflowRunAttemptById(payload.workflowRunAttemptId);\n if (!runAttempt) return;\n\n await writeFailureAnnotation({\n annotations,\n target: {\n workspaceId: payload.workspaceId,\n projectId: payload.projectId,\n workflowRunId: payload.workflowRunId,\n workflowRunAttempt: runAttempt.attempt,\n workflowRunAttemptId: payload.workflowRunAttemptId,\n jobId: payload.jobId,\n jobExecutionId: detail.step.jobExecutionId,\n originStepId: detail.step.id,\n originStepAttempt: detail.attempt.attempt,\n },\n context: failureContext('step', detail.step.id),\n failed:\n detail.step.status === 'failed' ||\n (detail.attempt.status === 'failed' &&\n detail.step.status !== 'succeeded' &&\n detail.step.status !== 'cancelled'),\n body: stepFailureBody(\n detail.step.name,\n detail.attempt.error ?? detail.step.error,\n detail.attempt.exitCode,\n ),\n });\n } catch (error) {\n recordFailureAnnotationFailure(error, 'lookup', {\n stepId: payload.stepId,\n jobId: payload.jobId,\n });\n }\n };\n}\n\nexport function onJobTerminatedFailureAnnotation(annotations: AnnotationsInterModuleClient) {\n return async (payload: WorkflowsJobTerminatedEventDto): Promise<void> => {\n // Step failures already have a step-scoped annotation. Job-scoped annotations\n // are reserved for terminal causes where no step-level failure card exists.\n const isConditionEvaluationFailure =\n payload.status === 'skipped' && payload.statusReason === 'condition_errored';\n if (\n (payload.status !== 'failed' && !isConditionEvaluationFailure) ||\n !JOB_FAILURE_ANNOTATION_REASONS.has(payload.statusReason ?? '')\n ) {\n return;\n }\n\n try {\n const [scope, runAttempt] = await Promise.all([\n getJobScope(payload.jobId),\n getWorkflowRunAttemptById(payload.workflowRunAttemptId),\n ]);\n if (!scope || !runAttempt || !payload.jobExecutionId) return;\n\n const origin = await getJobExecutionFailureOrigin(payload.jobExecutionId);\n if (!origin) return;\n\n await writeFailureAnnotation({\n annotations,\n target: {\n workspaceId: scope.workspaceId,\n projectId: scope.projectId,\n workflowRunId: payload.workflowRunId,\n workflowRunAttempt: runAttempt.attempt,\n workflowRunAttemptId: payload.workflowRunAttemptId,\n jobId: payload.jobId,\n jobExecutionId: origin.jobExecutionId,\n originStepId: origin.stepId,\n originStepAttempt: origin.stepAttempt,\n },\n context: failureContext('job', payload.jobId),\n failed: true,\n body: jobFailureBody(payload.statusReason, origin),\n });\n } catch (error) {\n recordFailureAnnotationFailure(error, 'lookup', {jobId: payload.jobId});\n }\n };\n}\n\ntype FailureAnnotationTarget = {\n workspaceId: string;\n projectId: string;\n workflowRunId: string;\n workflowRunAttempt: number;\n workflowRunAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n originStepId: string;\n originStepAttempt: number;\n};\n\n/**\n * Failure annotations are a best-effort projection. The workflow terminal fact is authoritative;\n * projection lookup and writes are swallowed so they cannot change the workflow outcome. Every\n * swallowed error emits a reason-labelled metric and a structured warning for operations.\n */\nasync function writeFailureAnnotation(params: {\n annotations: AnnotationsInterModuleClient;\n target: FailureAnnotationTarget;\n context: string;\n failed: boolean;\n body: string;\n}): Promise<void> {\n try {\n await params.annotations.replaceOrRemoveAnnotation({\n ...params.target,\n context: params.context,\n annotation: params.failed\n ? {op: 'replace', style: 'error', body: params.body}\n : {op: 'remove'},\n });\n } catch (error) {\n const reason = isInterModuleKnownError(\n annotationsInterModuleContract.methods.replaceOrRemoveAnnotation,\n error,\n )\n ? 'budget'\n : 'write';\n recordFailureAnnotationFailure(error, reason, params.target);\n }\n}\n\nfunction failureContext(kind: 'job' | 'step', id: string): string {\n return `failure:${kind}:${id}`;\n}\n\nfunction stepFailureBody(\n name: string,\n error: Record<string, unknown> | null,\n exitCode: number | null,\n): string {\n const reason = typeof error?.reason === 'string' ? error.reason : 'unknown';\n const message =\n typeof error?.message === 'string' && error.message.trim()\n ? error.message\n : 'No failure message was recorded.';\n return [\n `**${name} failed**`,\n '',\n `Reason: \\`${reason}\\``,\n `Exit code: \\`${exitCode ?? 'none'}\\``,\n '',\n message,\n ].join('\\n');\n}\n\nfunction jobFailureBody(\n reason: string | null,\n origin: {\n stepName: string;\n attemptStatus: string | null;\n stepError: Record<string, unknown> | null;\n attemptError: Record<string, unknown> | null;\n attemptExitCode: number | null;\n },\n): string {\n const progress = origin.attemptStatus\n ? `The job stopped while processing **${origin.stepName}**.`\n : `The job stopped before **${origin.stepName}** started.`;\n const error = origin.attemptError ?? origin.stepError;\n const message = typeof error?.message === 'string' && error.message.trim() ? error.message : null;\n const exitCode =\n origin.attemptExitCode === null ? null : `Exit code: \\`${origin.attemptExitCode}\\``;\n return [\n `**Job failed before completion**`,\n '',\n progress,\n `Reason: \\`${reason ?? 'unknown'}\\``,\n message ? `Failure: ${message}` : null,\n exitCode,\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction recordFailureAnnotationFailure(\n error: unknown,\n reason: 'lookup' | 'budget' | 'write',\n context: Record<string, string | number>,\n): void {\n recordWorkflowFailureAnnotationFailed(reason);\n logger().warn({error, reason, ...context}, 'Failed to project workflow failure annotation');\n}\n"],"names":["annotationsInterModuleContract","isInterModuleKnownError","logger","getJobExecutionFailureOrigin","getJobScope","getStepAttemptDetail","getWorkflowRunAttemptById","recordWorkflowFailureAnnotationFailed","JOB_FAILURE_ANNOTATION_REASONS","Set","onStepAttemptTerminatedFailureAnnotation","annotations","payload","status","undefined","attempt","initialDetail","stepId","detail","step","currentAttempt","id","runAttempt","workflowRunAttemptId","writeFailureAnnotation","target","workspaceId","projectId","workflowRunId","workflowRunAttempt","jobId","jobExecutionId","originStepId","originStepAttempt","context","failureContext","failed","body","stepFailureBody","name","error","exitCode","recordFailureAnnotationFailure","onJobTerminatedFailureAnnotation","isConditionEvaluationFailure","statusReason","has","scope","Promise","all","origin","stepAttempt","jobFailureBody","params","replaceOrRemoveAnnotation","annotation","op","style","reason","methods","kind","message","trim","join","progress","attemptStatus","stepName","attemptError","stepError","attemptExitCode","filter","Boolean","warn"],"mappings":"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"}