@shipfox/api-definitions 11.0.0 → 12.2.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 (174) hide show
  1. package/.turbo/turbo-build.log +13 -12
  2. package/CHANGELOG.md +106 -0
  3. package/dist/core/entities/index.d.ts +1 -0
  4. package/dist/core/entities/index.d.ts.map +1 -1
  5. package/dist/core/entities/index.js +1 -0
  6. package/dist/core/entities/index.js.map +1 -1
  7. package/dist/core/entities/sync-state.d.ts +5 -1
  8. package/dist/core/entities/sync-state.d.ts.map +1 -1
  9. package/dist/core/entities/sync-state.js +10 -0
  10. package/dist/core/entities/sync-state.js.map +1 -1
  11. package/dist/core/entities/validation-warning.d.ts +6 -0
  12. package/dist/core/entities/validation-warning.d.ts.map +1 -0
  13. package/dist/core/entities/validation-warning.js +3 -0
  14. package/dist/core/entities/validation-warning.js.map +1 -0
  15. package/dist/core/errors.d.ts +2 -2
  16. package/dist/core/errors.d.ts.map +1 -1
  17. package/dist/core/integrations.d.ts +1 -1
  18. package/dist/core/parse-definition.d.ts +9 -7
  19. package/dist/core/parse-definition.d.ts.map +1 -1
  20. package/dist/core/parse-definition.js +14 -2
  21. package/dist/core/parse-definition.js.map +1 -1
  22. package/dist/core/sync-definitions.d.ts +2 -0
  23. package/dist/core/sync-definitions.d.ts.map +1 -1
  24. package/dist/core/sync-definitions.js +6 -5
  25. package/dist/core/sync-definitions.js.map +1 -1
  26. package/dist/core/validate-definition.d.ts +9 -5
  27. package/dist/core/validate-definition.d.ts.map +1 -1
  28. package/dist/core/validate-definition.js +23 -2
  29. package/dist/core/validate-definition.js.map +1 -1
  30. package/dist/core/workflow-model/index.d.ts +1 -1
  31. package/dist/core/workflow-model/index.d.ts.map +1 -1
  32. package/dist/core/workflow-model/index.js.map +1 -1
  33. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts +3 -1
  34. package/dist/core/workflow-model/invalid-workflow-model-error.d.ts.map +1 -1
  35. package/dist/core/workflow-model/invalid-workflow-model-error.js.map +1 -1
  36. package/dist/core/workflow-model/normalize-if-condition.d.ts +0 -1
  37. package/dist/core/workflow-model/normalize-if-condition.d.ts.map +1 -1
  38. package/dist/core/workflow-model/normalize-if-condition.js +0 -1
  39. package/dist/core/workflow-model/normalize-if-condition.js.map +1 -1
  40. package/dist/core/workflow-model/normalize-job-checkout.d.ts +15 -3
  41. package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -1
  42. package/dist/core/workflow-model/normalize-job-checkout.js +83 -4
  43. package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -1
  44. package/dist/core/workflow-model/normalize-job-listening.js +0 -1
  45. package/dist/core/workflow-model/normalize-job-listening.js.map +1 -1
  46. package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -1
  47. package/dist/core/workflow-model/normalize-job-success.js +0 -1
  48. package/dist/core/workflow-model/normalize-job-success.js.map +1 -1
  49. package/dist/core/workflow-model/normalize-jobs.d.ts.map +1 -1
  50. package/dist/core/workflow-model/normalize-jobs.js +315 -74
  51. package/dist/core/workflow-model/normalize-jobs.js.map +1 -1
  52. package/dist/core/workflow-model/normalize-step-gate.js +0 -1
  53. package/dist/core/workflow-model/normalize-step-gate.js.map +1 -1
  54. package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -1
  55. package/dist/core/workflow-model/normalize-triggers.js +0 -1
  56. package/dist/core/workflow-model/normalize-triggers.js.map +1 -1
  57. package/dist/core/workflow-model/normalize-workflow-document.d.ts +3 -0
  58. package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -1
  59. package/dist/core/workflow-model/normalize-workflow-document.js +34 -4
  60. package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -1
  61. package/dist/core/workflow-model/parse-interpolation-field.d.ts +1 -1
  62. package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -1
  63. package/dist/core/workflow-model/parse-interpolation-field.js +27 -57
  64. package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -1
  65. package/dist/core/workflow-model/validate-literal-name.d.ts +12 -0
  66. package/dist/core/workflow-model/validate-literal-name.d.ts.map +1 -0
  67. package/dist/core/workflow-model/validate-literal-name.js +36 -0
  68. package/dist/core/workflow-model/validate-literal-name.js.map +1 -0
  69. package/dist/core/workflow-model/validate-predicate-expression.d.ts +1 -2
  70. package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -1
  71. package/dist/core/workflow-model/validate-predicate-expression.js +31 -90
  72. package/dist/core/workflow-model/validate-predicate-expression.js.map +1 -1
  73. package/dist/core/workflow-model/validation-issue.d.ts +2 -1
  74. package/dist/core/workflow-model/validation-issue.d.ts.map +1 -1
  75. package/dist/core/workflow-model/validation-issue.js +8 -2
  76. package/dist/core/workflow-model/validation-issue.js.map +1 -1
  77. package/dist/core/workflow-model/workflow-field-label.d.ts.map +1 -1
  78. package/dist/core/workflow-model/workflow-field-label.js +16 -2
  79. package/dist/core/workflow-model/workflow-field-label.js.map +1 -1
  80. package/dist/core/workflow-yaml/invalid-workflow-yaml-error.d.ts.map +1 -1
  81. package/dist/db/db.d.ts +48 -10
  82. package/dist/db/db.d.ts.map +1 -1
  83. package/dist/db/schema/definitions.d.ts +1 -1
  84. package/dist/db/schema/outbox.d.ts +1 -1
  85. package/dist/db/schema/sync-states.d.ts +22 -3
  86. package/dist/db/schema/sync-states.d.ts.map +1 -1
  87. package/dist/db/schema/sync-states.js +3 -1
  88. package/dist/db/schema/sync-states.js.map +1 -1
  89. package/dist/db/sync-states.d.ts +2 -1
  90. package/dist/db/sync-states.d.ts.map +1 -1
  91. package/dist/db/sync-states.js +4 -0
  92. package/dist/db/sync-states.js.map +1 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +3 -1
  95. package/dist/index.js.map +1 -1
  96. package/dist/presentation/dto/definition.d.ts.map +1 -1
  97. package/dist/presentation/dto/definition.js +2 -1
  98. package/dist/presentation/dto/definition.js.map +1 -1
  99. package/dist/presentation/routes/project-access.d.ts +1 -0
  100. package/dist/presentation/routes/project-access.d.ts.map +1 -1
  101. package/dist/presentation/routes/validate-definition.d.ts.map +1 -1
  102. package/dist/presentation/routes/validate-definition.js +5 -3
  103. package/dist/presentation/routes/validate-definition.js.map +1 -1
  104. package/dist/presentation/subscribers/index.d.ts +1 -0
  105. package/dist/presentation/subscribers/index.d.ts.map +1 -1
  106. package/dist/presentation/subscribers/index.js +1 -0
  107. package/dist/presentation/subscribers/index.js.map +1 -1
  108. package/dist/presentation/subscribers/on-integration-connection-available.d.ts +5 -0
  109. package/dist/presentation/subscribers/on-integration-connection-available.d.ts.map +1 -0
  110. package/dist/presentation/subscribers/on-integration-connection-available.js +32 -0
  111. package/dist/presentation/subscribers/on-integration-connection-available.js.map +1 -0
  112. package/dist/presentation/subscribers/start-definition-sync.d.ts +1 -0
  113. package/dist/presentation/subscribers/start-definition-sync.d.ts.map +1 -1
  114. package/dist/presentation/subscribers/start-definition-sync.js +30 -17
  115. package/dist/presentation/subscribers/start-definition-sync.js.map +1 -1
  116. package/dist/temporal/activities/sync-activities.d.ts +6 -2
  117. package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
  118. package/dist/temporal/activities/sync-activities.js +8 -1
  119. package/dist/temporal/activities/sync-activities.js.map +1 -1
  120. package/dist/temporal/workflows/definition-sync.js +4 -1
  121. package/dist/temporal/workflows/definition-sync.js.map +1 -1
  122. package/dist/temporal/workflows/index.bundle.js +9029 -3
  123. package/dist/tsconfig.test.tsbuildinfo +1 -1
  124. package/drizzle/0002_lumpy_ben_parker.sql +1 -0
  125. package/drizzle/meta/0002_snapshot.json +534 -0
  126. package/drizzle/meta/_journal.json +7 -0
  127. package/package.json +17 -17
  128. package/src/core/entities/index.ts +1 -0
  129. package/src/core/entities/sync-state.ts +23 -0
  130. package/src/core/entities/validation-warning.ts +5 -0
  131. package/src/core/parse-definition-warnings.test.ts +33 -0
  132. package/src/core/parse-definition.test.ts +7 -1
  133. package/src/core/parse-definition.ts +28 -10
  134. package/src/core/sync-definitions.test.ts +39 -0
  135. package/src/core/sync-definitions.ts +14 -4
  136. package/src/core/validate-definition.test.ts +128 -0
  137. package/src/core/validate-definition.ts +39 -8
  138. package/src/core/workflow-model/README.md +2 -2
  139. package/src/core/workflow-model/index.ts +1 -0
  140. package/src/core/workflow-model/invalid-workflow-model-error.ts +8 -3
  141. package/src/core/workflow-model/normalize-if-condition.ts +0 -2
  142. package/src/core/workflow-model/normalize-job-checkout.ts +105 -8
  143. package/src/core/workflow-model/normalize-job-listening.ts +0 -1
  144. package/src/core/workflow-model/normalize-job-success.ts +0 -1
  145. package/src/core/workflow-model/normalize-jobs.ts +335 -90
  146. package/src/core/workflow-model/normalize-step-gate.ts +0 -1
  147. package/src/core/workflow-model/normalize-triggers.ts +0 -1
  148. package/src/core/workflow-model/normalize-workflow-document.test.ts +876 -202
  149. package/src/core/workflow-model/normalize-workflow-document.ts +29 -4
  150. package/src/core/workflow-model/parse-interpolation-field.ts +50 -79
  151. package/src/core/workflow-model/validate-literal-name.ts +64 -0
  152. package/src/core/workflow-model/validate-predicate-expression.test.ts +153 -15
  153. package/src/core/workflow-model/validate-predicate-expression.ts +52 -120
  154. package/src/core/workflow-model/validation-issue.ts +4 -0
  155. package/src/core/workflow-model/workflow-field-label.ts +16 -2
  156. package/src/core/workflow-yaml/README.md +2 -2
  157. package/src/db/schema/sync-states.ts +4 -1
  158. package/src/db/sync-states.test.ts +57 -0
  159. package/src/db/sync-states.ts +6 -0
  160. package/src/index.ts +12 -1
  161. package/src/presentation/dto/definition.ts +1 -0
  162. package/src/presentation/routes/list-definitions.test.ts +1 -0
  163. package/src/presentation/routes/validate-definition.test.ts +1 -0
  164. package/src/presentation/routes/validate-definition.ts +7 -1
  165. package/src/presentation/subscribers/index.ts +1 -0
  166. package/src/presentation/subscribers/on-integration-connection-available.test.ts +132 -0
  167. package/src/presentation/subscribers/on-integration-connection-available.ts +44 -0
  168. package/src/presentation/subscribers/start-definition-sync.ts +35 -19
  169. package/src/temporal/activities/sync-activities.test.ts +16 -0
  170. package/src/temporal/activities/sync-activities.ts +18 -3
  171. package/src/temporal/workflows/definition-sync.ts +1 -1
  172. package/test/agent-validation-catalog.ts +17 -0
  173. package/test/fixtures/valid-listening-job.yml +2 -1
  174. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,132 @@
1
+ import type {IntegrationConnectionAvailableEvent} from '@shipfox/api-integration-core-dto';
2
+ import type {DomainEvent} from '@shipfox/node-outbox';
3
+ import {createOnIntegrationConnectionAvailable} from './on-integration-connection-available.js';
4
+
5
+ const startMock = vi.fn();
6
+
7
+ vi.mock('@shipfox/node-temporal', () => ({
8
+ temporalClient: () => ({workflow: {start: startMock}}),
9
+ }));
10
+
11
+ function project(name: string, workspaceId: string) {
12
+ return {
13
+ id: crypto.randomUUID(),
14
+ workspaceId,
15
+ sourceConnectionId: crypto.randomUUID(),
16
+ sourceExternalRepositoryId: `github:${name}`,
17
+ sourceRepositoryOwner: 'shipfox',
18
+ name,
19
+ };
20
+ }
21
+
22
+ describe('onIntegrationConnectionAvailable', () => {
23
+ beforeEach(() => {
24
+ startMock.mockReset();
25
+ startMock.mockResolvedValue({});
26
+ });
27
+
28
+ it('paginates workspace projects and starts an event-keyed sync for each one', async () => {
29
+ const workspaceId = crypto.randomUUID();
30
+ const first = project('first', workspaceId);
31
+ const second = project('second', workspaceId);
32
+ const cursor = {createdAt: '2026-08-05T12:00:00.000Z', id: first.id};
33
+ const listProjectsByWorkspace = vi
34
+ .fn()
35
+ .mockResolvedValueOnce({projects: [first], nextCursor: cursor})
36
+ .mockResolvedValueOnce({projects: [second], nextCursor: null});
37
+ const handler = createOnIntegrationConnectionAvailable({listProjectsByWorkspace} as never);
38
+ const payload: IntegrationConnectionAvailableEvent = {
39
+ provider: 'linear',
40
+ workspaceId: first.workspaceId,
41
+ connectionId: crypto.randomUUID(),
42
+ slug: 'linear_shipfox',
43
+ };
44
+ const event: DomainEvent<IntegrationConnectionAvailableEvent> = {
45
+ id: crypto.randomUUID(),
46
+ type: 'integrations.connection.available',
47
+ payload,
48
+ createdAt: new Date(),
49
+ };
50
+
51
+ await handler(payload, event);
52
+
53
+ expect(listProjectsByWorkspace).toHaveBeenNthCalledWith(1, {
54
+ workspaceId: payload.workspaceId,
55
+ limit: 100,
56
+ });
57
+ expect(listProjectsByWorkspace).toHaveBeenNthCalledWith(2, {
58
+ workspaceId: payload.workspaceId,
59
+ limit: 100,
60
+ cursor,
61
+ });
62
+ expect(startMock).toHaveBeenCalledTimes(2);
63
+ for (const candidate of [first, second]) {
64
+ expect(startMock).toHaveBeenCalledWith('definitionSyncWorkflow', {
65
+ taskQueue: 'definitions-sync',
66
+ workflowId: `definition-sync:${candidate.id}:integration:${event.id}`,
67
+ workflowIdConflictPolicy: 'USE_EXISTING',
68
+ workflowIdReusePolicy: 'REJECT_DUPLICATE',
69
+ args: [
70
+ {
71
+ projectId: candidate.id,
72
+ workspaceId: candidate.workspaceId,
73
+ sourceConnectionId: candidate.sourceConnectionId,
74
+ sourceExternalRepositoryId: candidate.sourceExternalRepositoryId,
75
+ sourceRef: undefined,
76
+ sourceCommitSha: undefined,
77
+ },
78
+ ],
79
+ });
80
+ }
81
+ });
82
+
83
+ it('treats an already-started event-keyed sync as successfully delivered', async () => {
84
+ const workspaceId = crypto.randomUUID();
85
+ const candidate = project('first', workspaceId);
86
+ const alreadyStarted = new Error('Workflow execution already started');
87
+ alreadyStarted.name = 'WorkflowExecutionAlreadyStartedError';
88
+ startMock.mockRejectedValue(alreadyStarted);
89
+ const handler = createOnIntegrationConnectionAvailable({
90
+ listProjectsByWorkspace: vi.fn(() =>
91
+ Promise.resolve({projects: [candidate], nextCursor: null}),
92
+ ),
93
+ } as never);
94
+ const payload: IntegrationConnectionAvailableEvent = {
95
+ provider: 'linear',
96
+ workspaceId,
97
+ connectionId: crypto.randomUUID(),
98
+ slug: 'linear_shipfox',
99
+ };
100
+
101
+ await expect(
102
+ handler(payload, {
103
+ id: crypto.randomUUID(),
104
+ type: 'integrations.connection.available',
105
+ payload,
106
+ createdAt: new Date(),
107
+ }),
108
+ ).resolves.toBeUndefined();
109
+ });
110
+
111
+ it('rethrows project lookup failures so the outbox event can retry', async () => {
112
+ const failure = new Error('projects unavailable');
113
+ const handler = createOnIntegrationConnectionAvailable({
114
+ listProjectsByWorkspace: vi.fn(() => Promise.reject(failure)),
115
+ } as never);
116
+ const payload: IntegrationConnectionAvailableEvent = {
117
+ provider: 'linear',
118
+ workspaceId: crypto.randomUUID(),
119
+ connectionId: crypto.randomUUID(),
120
+ slug: 'linear_shipfox',
121
+ };
122
+
123
+ await expect(
124
+ handler(payload, {
125
+ id: crypto.randomUUID(),
126
+ type: 'integrations.connection.available',
127
+ payload,
128
+ createdAt: new Date(),
129
+ }),
130
+ ).rejects.toBe(failure);
131
+ });
132
+ });
@@ -0,0 +1,44 @@
1
+ import type {IntegrationConnectionAvailableEvent} from '@shipfox/api-integration-core-dto';
2
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
3
+ import {boundedMap} from '@shipfox/node-module';
4
+ import type {DomainEvent} from '@shipfox/node-outbox';
5
+ import {startDefinitionSync} from './start-definition-sync.js';
6
+
7
+ const PROJECT_PAGE_SIZE = 100;
8
+ const SYNC_START_CONCURRENCY = 10;
9
+
10
+ export function createOnIntegrationConnectionAvailable(
11
+ projects: Pick<ProjectsModuleClient, 'listProjectsByWorkspace'>,
12
+ ) {
13
+ return async function onIntegrationConnectionAvailable(
14
+ payload: IntegrationConnectionAvailableEvent,
15
+ event: DomainEvent<IntegrationConnectionAvailableEvent>,
16
+ ): Promise<void> {
17
+ let cursor: {createdAt: string; id: string} | undefined;
18
+
19
+ do {
20
+ const page = await projects.listProjectsByWorkspace({
21
+ workspaceId: payload.workspaceId,
22
+ limit: PROJECT_PAGE_SIZE,
23
+ ...(cursor ? {cursor} : {}),
24
+ });
25
+
26
+ await boundedMap(
27
+ page.projects,
28
+ SYNC_START_CONCURRENCY,
29
+ async (project) => {
30
+ await startDefinitionSync({
31
+ projectId: project.id,
32
+ workspaceId: project.workspaceId,
33
+ sourceConnectionId: project.sourceConnectionId,
34
+ externalRepositoryId: project.sourceExternalRepositoryId,
35
+ requestId: `integration:${event.id}`,
36
+ });
37
+ },
38
+ {stopOnError: true},
39
+ );
40
+
41
+ cursor = page.nextCursor ?? undefined;
42
+ } while (cursor);
43
+ };
44
+ }
@@ -8,31 +8,47 @@ export interface StartDefinitionSyncParams {
8
8
  externalRepositoryId: string;
9
9
  sourceRef?: string | undefined;
10
10
  sourceCommitSha?: string | undefined;
11
+ requestId?: string | undefined;
11
12
  }
12
13
 
13
14
  export async function startDefinitionSync(params: StartDefinitionSyncParams): Promise<void> {
14
15
  const workflowId = buildWorkflowId(params);
15
16
 
16
- await temporalClient().workflow.start(DEFINITION_SYNC_WORKFLOW, {
17
- taskQueue: DEFINITIONS_TASK_QUEUE,
18
- workflowId,
19
- workflowIdConflictPolicy: 'USE_EXISTING',
20
- workflowIdReusePolicy: 'ALLOW_DUPLICATE',
21
- args: [
22
- {
23
- projectId: params.projectId,
24
- workspaceId: params.workspaceId,
25
- sourceConnectionId: params.sourceConnectionId,
26
- sourceExternalRepositoryId: params.externalRepositoryId,
27
- sourceRef: params.sourceRef,
28
- sourceCommitSha: params.sourceCommitSha,
29
- },
30
- ],
31
- });
17
+ try {
18
+ await temporalClient().workflow.start(DEFINITION_SYNC_WORKFLOW, {
19
+ taskQueue: DEFINITIONS_TASK_QUEUE,
20
+ workflowId,
21
+ workflowIdConflictPolicy: 'USE_EXISTING',
22
+ workflowIdReusePolicy: params.requestId ? 'REJECT_DUPLICATE' : 'ALLOW_DUPLICATE',
23
+ args: [
24
+ {
25
+ projectId: params.projectId,
26
+ workspaceId: params.workspaceId,
27
+ sourceConnectionId: params.sourceConnectionId,
28
+ sourceExternalRepositoryId: params.externalRepositoryId,
29
+ sourceRef: params.sourceRef,
30
+ sourceCommitSha: params.sourceCommitSha,
31
+ },
32
+ ],
33
+ });
34
+ } catch (error) {
35
+ if (
36
+ params.requestId &&
37
+ error instanceof Error &&
38
+ error.name === 'WorkflowExecutionAlreadyStartedError'
39
+ ) {
40
+ return;
41
+ }
42
+ throw error;
43
+ }
32
44
  }
33
45
 
34
46
  function buildWorkflowId(params: StartDefinitionSyncParams): string {
35
- return params.sourceCommitSha
36
- ? `definition-sync:${params.projectId}:${params.sourceCommitSha}`
37
- : `definition-sync:${params.projectId}:bind`;
47
+ if (params.sourceCommitSha) {
48
+ return `definition-sync:${params.projectId}:${params.sourceCommitSha}`;
49
+ }
50
+ if (params.requestId) {
51
+ return `definition-sync:${params.projectId}:${params.requestId}`;
52
+ }
53
+ return `definition-sync:${params.projectId}:bind`;
38
54
  }
@@ -224,6 +224,7 @@ describe('definition sync activities', () => {
224
224
 
225
225
  expect(result.appliedCount).toBe(1);
226
226
  expect(result.deletedCount).toBe(0);
227
+ expect(result.warnings).toEqual([]);
227
228
  });
228
229
 
229
230
  it('translates DefinitionSyncPermanentError into a non-retryable ApplicationFailure', async () => {
@@ -308,6 +309,7 @@ describe('definition sync activities', () => {
308
309
  expect(rows[0]?.status).toBe('failed');
309
310
  expect(rows[0]?.lastErrorCode).toBe('invalid-definition');
310
311
  expect(rows[0]?.lastErrorMessage).toBe('Invalid workflow at .shipfox/workflows/bad.yml');
312
+ expect(rows[0]?.warnings).toEqual([]);
311
313
  expect(rows[0]?.finishedAt).not.toBeNull();
312
314
  });
313
315
 
@@ -362,6 +364,13 @@ describe('definition sync activities', () => {
362
364
  sourceConnectionId,
363
365
  sourceExternalRepositoryId: 'gitea:gitea-owner/platform',
364
366
  sourceRef: 'main',
367
+ warnings: [
368
+ {
369
+ code: 're-evaluating-command',
370
+ message: 'Workflow data is re-executed as shell code.',
371
+ path: 'jobs.build.steps.0.run',
372
+ },
373
+ ],
365
374
  });
366
375
  await result;
367
376
 
@@ -372,6 +381,13 @@ describe('definition sync activities', () => {
372
381
  expect(rows[0]?.status).toBe('succeeded');
373
382
  expect(rows[0]?.lastErrorCode).toBeNull();
374
383
  expect(rows[0]?.lastErrorMessage).toBeNull();
384
+ expect(rows[0]?.warnings).toEqual([
385
+ {
386
+ code: 're-evaluating-command',
387
+ message: 'Workflow data is re-executed as shell code.',
388
+ path: 'jobs.build.steps.0.run',
389
+ },
390
+ ]);
375
391
  });
376
392
  });
377
393
  });
@@ -3,7 +3,7 @@ import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/i
3
3
  import {markErrorReported} from '@shipfox/node-error-monitoring';
4
4
  import {Context} from '@temporalio/activity';
5
5
  import {ApplicationFailure} from '@temporalio/common';
6
- import type {DefinitionSyncErrorCode} from '#core/entities/sync-state.js';
6
+ import type {DefinitionSyncErrorCode, DefinitionSyncWarning} from '#core/entities/sync-state.js';
7
7
  import {
8
8
  classifySyncFailure,
9
9
  discoverWorkflowFiles,
@@ -50,6 +50,11 @@ export interface DiscoverWorkflowsActivityResult {
50
50
  export interface FetchAndApplyActivityResult {
51
51
  appliedCount: number;
52
52
  deletedCount: number;
53
+ warnings: DefinitionSyncWarning[];
54
+ }
55
+
56
+ export interface MarkSyncSucceededActivityInput extends SyncRefScopedInput {
57
+ warnings?: readonly DefinitionSyncWarning[] | undefined;
53
58
  }
54
59
 
55
60
  export interface DefinitionSyncActivityOptions {
@@ -93,6 +98,7 @@ function createPrepareDefinitionSyncActivity(sourceControl: DefinitionsSourceCon
93
98
  status: 'syncing',
94
99
  lastErrorCode: null,
95
100
  lastErrorMessage: null,
101
+ warnings: [],
96
102
  startedAt: new Date(),
97
103
  finishedAt: null,
98
104
  });
@@ -147,7 +153,7 @@ function createFetchAndApplyActivity(
147
153
  },
148
154
  });
149
155
 
150
- return await applyVcsDefinitionsBatch({
156
+ const result = await applyVcsDefinitionsBatch({
151
157
  projectId: input.projectId,
152
158
  workspaceId: input.workspaceId,
153
159
  ref: input.sourceRef,
@@ -160,12 +166,19 @@ function createFetchAndApplyActivity(
160
166
  contentHash: entry.contentHash,
161
167
  })),
162
168
  });
169
+
170
+ return {
171
+ ...result,
172
+ warnings: definitions.flatMap((entry) => entry.warnings),
173
+ };
163
174
  });
164
175
  };
165
176
  }
166
177
 
167
178
  function createMarkSyncSucceededActivity() {
168
- return async function markDefinitionSyncSucceeded(input: SyncRefScopedInput): Promise<void> {
179
+ return async function markDefinitionSyncSucceeded(
180
+ input: MarkSyncSucceededActivityInput,
181
+ ): Promise<void> {
169
182
  await markDefinitionSyncState({
170
183
  projectId: input.projectId,
171
184
  sourceConnectionId: input.sourceConnectionId,
@@ -174,6 +187,7 @@ function createMarkSyncSucceededActivity() {
174
187
  status: 'succeeded',
175
188
  lastErrorCode: null,
176
189
  lastErrorMessage: null,
190
+ warnings: input.warnings ?? [],
177
191
  finishedAt: new Date(),
178
192
  });
179
193
  };
@@ -191,6 +205,7 @@ function createMarkSyncFailedActivity() {
191
205
  status: 'failed',
192
206
  lastErrorCode: input.code,
193
207
  lastErrorMessage: input.message,
208
+ warnings: [],
194
209
  finishedAt: new Date(),
195
210
  });
196
211
  };
@@ -65,7 +65,7 @@ export async function definitionSyncWorkflow(
65
65
  const {paths} = await discoverDefinitionWorkflows(source);
66
66
  const applied = await fetchAndApplyDefinitionWorkflows({...source, paths});
67
67
 
68
- await markDefinitionSyncSucceeded(source);
68
+ await markDefinitionSyncSucceeded({...source, warnings: applied.warnings});
69
69
 
70
70
  return {
71
71
  sourceRef,
@@ -7,6 +7,21 @@ import {
7
7
  } from '@shipfox/api-agent-dto';
8
8
  import type {AgentValidationCatalog} from '@shipfox/api-agent-dto/inter-module';
9
9
 
10
+ const piModelIdsByProvider = Object.fromEntries(
11
+ MODEL_PROVIDER_CATALOG_SEED.filter((entry) => entry.support_status === 'supported').map(
12
+ (entry) => [
13
+ entry.id,
14
+ entry.id === 'anthropic'
15
+ ? ['claude-opus-4-8']
16
+ : entry.id === 'openai'
17
+ ? ['gpt-4.1', 'gpt-5.5-pro']
18
+ : entry.default_model === null
19
+ ? []
20
+ : [entry.default_model],
21
+ ],
22
+ ),
23
+ );
24
+
10
25
  export const agentValidationCatalog: AgentValidationCatalog = {
11
26
  version: 1,
12
27
  providers: MODEL_PROVIDER_IDS.map((id) => ({
@@ -17,6 +32,8 @@ export const agentValidationCatalog: AgentValidationCatalog = {
17
32
  harnesses: listHarnessDescriptors().map((harness) => ({
18
33
  id: harness.id,
19
34
  supported_provider_ids: [...harness.supportedProviderIds],
35
+ model_ids_by_provider:
36
+ harness.id === 'pi' ? piModelIdsByProvider : {anthropic: ['claude-opus-4-8']},
20
37
  thinking_levels: [...harness.thinkingLevels],
21
38
  effective_tools: listEnabledHarnessTools(
22
39
  harness.id,
@@ -2,7 +2,8 @@ name: Listening workflow
2
2
  runner: ubuntu-latest
3
3
  jobs:
4
4
  review:
5
- name: Review batch ${{ execution.index }}
5
+ name: Review batch
6
+ execution_name: Review batch ${{ execution.index }}
6
7
  listening:
7
8
  on:
8
9
  - source: github