@atolis-hq/wake 0.4.58 → 0.4.59
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.
- package/dist/src/activities/contracts/event-schema.d.ts +88 -0
- package/dist/src/activities/contracts/event-schema.js +9 -1
- package/dist/src/activities/index.d.ts +1 -0
- package/dist/src/activities/index.js +1 -0
- package/dist/src/activities/pr/activity-support.js +4 -0
- package/dist/src/activities/pr/contracts.d.ts +5 -0
- package/dist/src/activities/pr/merge-policy-denial.d.ts +19 -0
- package/dist/src/activities/pr/merge-policy-denial.js +32 -0
- package/dist/src/activities/pr/merge.d.ts +2 -0
- package/dist/src/activities/pr/merge.js +21 -3
- package/dist/src/activities/pr/policy.js +12 -3
- package/dist/src/bootstrap/integration-runtime.js +8 -0
- package/dist/src/bootstrap/surface-api-merge-policy.d.ts +19 -0
- package/dist/src/bootstrap/surface-api-merge-policy.js +52 -0
- package/dist/src/bootstrap/surface-api-work-applications.js +2 -1
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/orchestration/application/orchestration-service.d.ts +2 -0
- package/dist/src/orchestration/application/orchestration-service.js +4 -0
- package/dist/src/orchestration/application/policy-blocked-merge-recovery.d.ts +7 -0
- package/dist/src/orchestration/application/policy-blocked-merge-recovery.js +54 -0
- package/dist/src/orchestration/application/pull-request-transition-evidence.js +11 -2
- package/dist/src/orchestration/application/resource-transition-evidence.d.ts +2 -0
- package/dist/src/orchestration/application/resource-transition-matching.d.ts +3 -1
- package/dist/src/orchestration/application/resource-transition-matching.js +3 -0
- package/dist/src/surfaces/api/contracts/orchestration.d.ts +13 -0
- package/dist/src/surfaces/api/presenters/merge-policy-block.d.ts +9 -0
- package/dist/src/surfaces/api/presenters/merge-policy-block.js +23 -0
- package/dist/src/surfaces/index.d.ts +1 -0
- package/dist/src/surfaces/index.js +1 -0
- package/dist/src/surfaces/web-assets/assets/index-CC1TIxyc.js +1131 -0
- package/dist/src/surfaces/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/dist/src/surfaces/web-assets/assets/index-DXxSMyln.js +0 -1131
|
@@ -51,6 +51,14 @@ export declare const deniedOutcomeSchema: z.ZodObject<{
|
|
|
51
51
|
"blocked-path-changed": "blocked-path-changed";
|
|
52
52
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
53
53
|
}>;
|
|
54
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
55
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
56
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
61
|
+
}, z.core.$strict>>;
|
|
54
62
|
}, z.core.$strict>;
|
|
55
63
|
}, z.core.$strict>;
|
|
56
64
|
export declare function createResourceFactDraftSchemas(eventTypes: ActivityEventTypes): readonly [z.ZodObject<{
|
|
@@ -1147,6 +1155,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1147
1155
|
"blocked-path-changed": "blocked-path-changed";
|
|
1148
1156
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
1149
1157
|
}>;
|
|
1158
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
1159
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
1160
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
1161
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1162
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1163
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1164
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1165
|
+
}, z.core.$strict>>;
|
|
1150
1166
|
}, z.core.$strict>;
|
|
1151
1167
|
}, z.core.$strict>;
|
|
1152
1168
|
fact: z.ZodObject<{
|
|
@@ -1351,6 +1367,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1351
1367
|
"blocked-path-changed": "blocked-path-changed";
|
|
1352
1368
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
1353
1369
|
}>;
|
|
1370
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
1371
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
1372
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
1373
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1374
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1375
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1376
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1377
|
+
}, z.core.$strict>>;
|
|
1354
1378
|
}, z.core.$strict>;
|
|
1355
1379
|
}, z.core.$strict>;
|
|
1356
1380
|
fact: z.ZodObject<{
|
|
@@ -1457,6 +1481,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1457
1481
|
kind: "blocked";
|
|
1458
1482
|
data: {
|
|
1459
1483
|
reason: "closed" | "missing-resource" | "ambiguous-resource" | "correlation-conflict" | "stale-approval" | "checks-pending" | "checks-failing" | "untrusted-actor" | "too-many-files-changed" | "blocked-path-changed" | "changed-files-unavailable";
|
|
1484
|
+
resourceId?: string | undefined;
|
|
1485
|
+
revision?: string | undefined;
|
|
1486
|
+
policy?: {
|
|
1487
|
+
blockedPaths?: string[] | undefined;
|
|
1488
|
+
matchedPaths?: string[] | undefined;
|
|
1489
|
+
maxFilesChanged?: number | undefined;
|
|
1490
|
+
filesChanged?: number | undefined;
|
|
1491
|
+
} | undefined;
|
|
1460
1492
|
};
|
|
1461
1493
|
};
|
|
1462
1494
|
}, {
|
|
@@ -1467,6 +1499,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1467
1499
|
kind: "blocked";
|
|
1468
1500
|
data: {
|
|
1469
1501
|
reason: "closed" | "missing-resource" | "ambiguous-resource" | "correlation-conflict" | "stale-approval" | "checks-pending" | "checks-failing" | "untrusted-actor" | "too-many-files-changed" | "blocked-path-changed" | "changed-files-unavailable";
|
|
1502
|
+
resourceId?: string | undefined;
|
|
1503
|
+
revision?: string | undefined;
|
|
1504
|
+
policy?: {
|
|
1505
|
+
blockedPaths?: string[] | undefined;
|
|
1506
|
+
matchedPaths?: string[] | undefined;
|
|
1507
|
+
maxFilesChanged?: number | undefined;
|
|
1508
|
+
filesChanged?: number | undefined;
|
|
1509
|
+
} | undefined;
|
|
1470
1510
|
};
|
|
1471
1511
|
};
|
|
1472
1512
|
fact: {
|
|
@@ -1614,6 +1654,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1614
1654
|
"blocked-path-changed": "blocked-path-changed";
|
|
1615
1655
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
1616
1656
|
}>;
|
|
1657
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
1658
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
1659
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
1660
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1661
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1662
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1663
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1664
|
+
}, z.core.$strict>>;
|
|
1617
1665
|
}, z.core.$strict>;
|
|
1618
1666
|
}, z.core.$strict>;
|
|
1619
1667
|
fact: z.ZodObject<{
|
|
@@ -1828,6 +1876,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1828
1876
|
"blocked-path-changed": "blocked-path-changed";
|
|
1829
1877
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
1830
1878
|
}>;
|
|
1879
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
1880
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
1881
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
1882
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1883
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1884
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1885
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
1886
|
+
}, z.core.$strict>>;
|
|
1831
1887
|
}, z.core.$strict>;
|
|
1832
1888
|
}, z.core.$strict>;
|
|
1833
1889
|
fact: z.ZodObject<{
|
|
@@ -1934,6 +1990,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1934
1990
|
kind: "blocked";
|
|
1935
1991
|
data: {
|
|
1936
1992
|
reason: "closed" | "missing-resource" | "ambiguous-resource" | "correlation-conflict" | "stale-approval" | "checks-pending" | "checks-failing" | "untrusted-actor" | "too-many-files-changed" | "blocked-path-changed" | "changed-files-unavailable";
|
|
1993
|
+
resourceId?: string | undefined;
|
|
1994
|
+
revision?: string | undefined;
|
|
1995
|
+
policy?: {
|
|
1996
|
+
blockedPaths?: string[] | undefined;
|
|
1997
|
+
matchedPaths?: string[] | undefined;
|
|
1998
|
+
maxFilesChanged?: number | undefined;
|
|
1999
|
+
filesChanged?: number | undefined;
|
|
2000
|
+
} | undefined;
|
|
1937
2001
|
};
|
|
1938
2002
|
};
|
|
1939
2003
|
}, {
|
|
@@ -1944,6 +2008,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
1944
2008
|
kind: "blocked";
|
|
1945
2009
|
data: {
|
|
1946
2010
|
reason: "closed" | "missing-resource" | "ambiguous-resource" | "correlation-conflict" | "stale-approval" | "checks-pending" | "checks-failing" | "untrusted-actor" | "too-many-files-changed" | "blocked-path-changed" | "changed-files-unavailable";
|
|
2011
|
+
resourceId?: string | undefined;
|
|
2012
|
+
revision?: string | undefined;
|
|
2013
|
+
policy?: {
|
|
2014
|
+
blockedPaths?: string[] | undefined;
|
|
2015
|
+
matchedPaths?: string[] | undefined;
|
|
2016
|
+
maxFilesChanged?: number | undefined;
|
|
2017
|
+
filesChanged?: number | undefined;
|
|
2018
|
+
} | undefined;
|
|
1947
2019
|
};
|
|
1948
2020
|
};
|
|
1949
2021
|
fact: {
|
|
@@ -2570,6 +2642,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
2570
2642
|
"blocked-path-changed": "blocked-path-changed";
|
|
2571
2643
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
2572
2644
|
}>;
|
|
2645
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
2646
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
2647
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
2648
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2649
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2650
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
2651
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
2652
|
+
}, z.core.$strict>>;
|
|
2573
2653
|
}, z.core.$strict>;
|
|
2574
2654
|
}, z.core.$strict>;
|
|
2575
2655
|
fact: z.ZodObject<{
|
|
@@ -2732,6 +2812,14 @@ export declare function createActivityEventSchemas(eventTypes: ActivityEventType
|
|
|
2732
2812
|
"blocked-path-changed": "blocked-path-changed";
|
|
2733
2813
|
"changed-files-unavailable": "changed-files-unavailable";
|
|
2734
2814
|
}>;
|
|
2815
|
+
resourceId: z.ZodOptional<z.ZodString>;
|
|
2816
|
+
revision: z.ZodOptional<z.ZodString>;
|
|
2817
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
2818
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2819
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2820
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
2821
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
2822
|
+
}, z.core.$strict>>;
|
|
2735
2823
|
}, z.core.$strict>;
|
|
2736
2824
|
}, z.core.$strict>;
|
|
2737
2825
|
fact: z.ZodObject<{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { mergePolicyDetailsSchema } from '../pr/merge-policy-denial.js';
|
|
2
3
|
import { eventDataSchema, eventEnvelopeSchema } from '@atolis-hq/eventing';
|
|
3
4
|
import { brandedStringSchema } from '../../kernel/index.js';
|
|
4
5
|
import { ResourceStreamKind, resourceId } from '../../resources/index.js';
|
|
@@ -114,7 +115,14 @@ export const requestedOutcomeSchema = z
|
|
|
114
115
|
export const deniedOutcomeSchema = z
|
|
115
116
|
.object({
|
|
116
117
|
kind: z.literal(ActivityOutcomeKind.Blocked),
|
|
117
|
-
data: z
|
|
118
|
+
data: z
|
|
119
|
+
.object({
|
|
120
|
+
reason: denialCodeSchema,
|
|
121
|
+
resourceId: z.string().optional(),
|
|
122
|
+
revision: z.string().optional(),
|
|
123
|
+
policy: mergePolicyDetailsSchema.optional(),
|
|
124
|
+
})
|
|
125
|
+
.strict(),
|
|
118
126
|
})
|
|
119
127
|
.strict();
|
|
120
128
|
export function createResourceFactDraftSchemas(eventTypes) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ActivityFailureCode, ActivityOutcomeKind, ActivityResourceRole, IntentAppendStatus, } from '../contracts/vocabulary.js';
|
|
3
|
+
import { mergePolicyDetailsSchema } from './merge-policy-denial.js';
|
|
3
4
|
import { PullRequestDenialCode } from './vocabulary.js';
|
|
4
5
|
import { ResourceCorrelationRole, resourceId, } from '../../resources/index.js';
|
|
5
6
|
import { isPullRequestLikeResource } from './capability.js';
|
|
@@ -29,6 +30,9 @@ export const pullRequestOutcomeSchema = z.union([
|
|
|
29
30
|
kind: z.literal(ActivityOutcomeKind.Blocked),
|
|
30
31
|
data: z
|
|
31
32
|
.object({
|
|
33
|
+
resourceId: z.string().optional(),
|
|
34
|
+
revision: z.string().optional(),
|
|
35
|
+
policy: mergePolicyDetailsSchema.optional(),
|
|
32
36
|
reason: z.enum([
|
|
33
37
|
PullRequestDenialCode.MissingResource,
|
|
34
38
|
PullRequestDenialCode.AmbiguousResource,
|
|
@@ -2,6 +2,7 @@ import type { ResourceCorrelationView, ResourceId, ResourceView } from '../../re
|
|
|
2
2
|
import type { WorkItemId, WorkItemView } from '../../work/index.js';
|
|
3
3
|
import type { ActivityFailureCode, ActivityOutcomeKind, ActivityResourceRole } from '../contracts/vocabulary.js';
|
|
4
4
|
import type { ReviewActorKind, ReviewerAuthorizationEvidence } from '../review/contracts.js';
|
|
5
|
+
import type { MergePolicyDetails } from './merge-policy-denial.js';
|
|
5
6
|
import type { MergeMethod, PullRequestCheckState, PullRequestDenialCode, PullRequestState } from './vocabulary.js';
|
|
6
7
|
export interface PullRequestView {
|
|
7
8
|
readonly resourceId: ResourceId;
|
|
@@ -89,6 +90,9 @@ export type PullRequestActivityOutcome = {
|
|
|
89
90
|
readonly kind: typeof ActivityOutcomeKind.Blocked;
|
|
90
91
|
readonly data: {
|
|
91
92
|
readonly reason: PullRequestDenialCode;
|
|
93
|
+
readonly resourceId?: string | undefined;
|
|
94
|
+
readonly revision?: string | undefined;
|
|
95
|
+
readonly policy?: MergePolicyDetails | undefined;
|
|
92
96
|
};
|
|
93
97
|
} | {
|
|
94
98
|
readonly kind: typeof ActivityOutcomeKind.Failed;
|
|
@@ -130,4 +134,5 @@ export type PullRequestAuthorityDecision = {
|
|
|
130
134
|
} | {
|
|
131
135
|
readonly allowed: false;
|
|
132
136
|
readonly reason: PullRequestDenialCode;
|
|
137
|
+
readonly policy?: MergePolicyDetails;
|
|
133
138
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const mergePolicyDetailsSchema: z.ZodObject<{
|
|
3
|
+
blockedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
4
|
+
matchedPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
5
|
+
maxFilesChanged: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
filesChanged: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
export type MergePolicyDetails = z.infer<typeof mergePolicyDetailsSchema>;
|
|
9
|
+
export declare function readMergePolicyDenial(outcome: unknown): {
|
|
10
|
+
reason: "too-many-files-changed" | "blocked-path-changed" | "changed-files-unavailable";
|
|
11
|
+
resourceId?: string | undefined;
|
|
12
|
+
revision?: string | undefined;
|
|
13
|
+
policy?: {
|
|
14
|
+
blockedPaths?: string[] | undefined;
|
|
15
|
+
matchedPaths?: string[] | undefined;
|
|
16
|
+
maxFilesChanged?: number | undefined;
|
|
17
|
+
filesChanged?: number | undefined;
|
|
18
|
+
} | undefined;
|
|
19
|
+
} | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ActivityOutcomeKind } from '../contracts/vocabulary.js';
|
|
3
|
+
import { PullRequestDenialCode } from './vocabulary.js';
|
|
4
|
+
export const mergePolicyDetailsSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
blockedPaths: z.array(z.string()).optional(),
|
|
7
|
+
matchedPaths: z.array(z.string()).optional(),
|
|
8
|
+
maxFilesChanged: z.number().int().positive().optional(),
|
|
9
|
+
filesChanged: z.number().int().nonnegative().optional(),
|
|
10
|
+
})
|
|
11
|
+
.strict();
|
|
12
|
+
const denialSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
kind: z.literal(ActivityOutcomeKind.Blocked),
|
|
15
|
+
data: z
|
|
16
|
+
.object({
|
|
17
|
+
reason: z.enum([
|
|
18
|
+
PullRequestDenialCode.BlockedPathChanged,
|
|
19
|
+
PullRequestDenialCode.TooManyFilesChanged,
|
|
20
|
+
PullRequestDenialCode.ChangedFilesUnavailable,
|
|
21
|
+
]),
|
|
22
|
+
resourceId: z.string().optional(),
|
|
23
|
+
revision: z.string().optional(),
|
|
24
|
+
policy: mergePolicyDetailsSchema.optional(),
|
|
25
|
+
})
|
|
26
|
+
.strict(),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
export function readMergePolicyDenial(outcome) {
|
|
30
|
+
const parsed = denialSchema.safeParse(outcome);
|
|
31
|
+
return parsed.success ? parsed.data.data : null;
|
|
32
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import type { EventJournal } from '@atolis-hq/eventing';
|
|
2
3
|
import type { ActivityDefinition } from '../contracts/activity.js';
|
|
3
4
|
import { BuiltInActivityName } from '../contracts/vocabulary.js';
|
|
4
5
|
import type { PullRequestService } from './application.js';
|
|
5
6
|
import type { PullRequestActivityOutcome, PullRequestMergeInput } from './contracts.js';
|
|
6
7
|
import { type IntentAppender } from './intent.js';
|
|
8
|
+
export declare const pullRequestMergeInputSchema: z.ZodType<PullRequestMergeInput>;
|
|
7
9
|
export declare function createPullRequestMergeActivity(journal: EventJournal, pullRequests: PullRequestService, appender?: IntentAppender): ActivityDefinition<typeof BuiltInActivityName.PullRequestMerge, PullRequestMergeInput, PullRequestActivityOutcome>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ActivityOutcomeKind } from '../contracts/vocabulary.js';
|
|
3
|
+
import { readMergePolicyDenial } from './merge-policy-denial.js';
|
|
3
4
|
import { MergeMethod } from './vocabulary.js';
|
|
4
5
|
import { BuiltInResourceCapability, resourceStream } from '../../resources/index.js';
|
|
5
6
|
import { workItemStream } from '../../work/index.js';
|
|
@@ -10,7 +11,7 @@ import { claimAndCompleteDecision, completeDecisionClaim, readDecisionClaim, } f
|
|
|
10
11
|
import { deliveryIntentRequested, mergeDenied } from './event-data.js';
|
|
11
12
|
import { activityCommandContext, createJournalIntentAppender, } from './intent.js';
|
|
12
13
|
import { decidePullRequestAuthority } from './policy.js';
|
|
13
|
-
const
|
|
14
|
+
export const pullRequestMergeInputSchema = z
|
|
14
15
|
.object({
|
|
15
16
|
target: pullRequestTargetSchema,
|
|
16
17
|
method: z.enum([MergeMethod.Merge, MergeMethod.Squash, MergeMethod.Rebase]),
|
|
@@ -32,7 +33,7 @@ const inputSchema = z
|
|
|
32
33
|
export function createPullRequestMergeActivity(journal, pullRequests, appender = createJournalIntentAppender(journal)) {
|
|
33
34
|
return {
|
|
34
35
|
name: BuiltInActivityName.PullRequestMerge,
|
|
35
|
-
inputSchema,
|
|
36
|
+
inputSchema: pullRequestMergeInputSchema,
|
|
36
37
|
outcomeSchema: pullRequestOutcomeSchema,
|
|
37
38
|
outcomeKinds: [
|
|
38
39
|
ActivityOutcomeKind.Waiting,
|
|
@@ -83,13 +84,30 @@ async function executeMerge(journal, pullRequests, appender, invocation, context
|
|
|
83
84
|
});
|
|
84
85
|
if (!decision.allowed) {
|
|
85
86
|
const factStream = resourceStream(resource.resourceId);
|
|
87
|
+
const audit = selectedDenialAudit(authority, resource.resourceId);
|
|
88
|
+
const policyDenial = readMergePolicyDenial({
|
|
89
|
+
kind: ActivityOutcomeKind.Blocked,
|
|
90
|
+
data: { reason: decision.reason },
|
|
91
|
+
});
|
|
86
92
|
const denial = mergeDenied(decision.reason, command, {
|
|
87
93
|
...selectedDenialAudit(authority, resource.resourceId),
|
|
88
94
|
method: invocation.input.method,
|
|
89
95
|
});
|
|
90
96
|
return claimAndCompleteDecision(journal, appender, invocation.activationId, MergeMethod.Merge, {
|
|
91
97
|
decisionKind: 'denied',
|
|
92
|
-
outcome: {
|
|
98
|
+
outcome: {
|
|
99
|
+
kind: ActivityOutcomeKind.Blocked,
|
|
100
|
+
data: {
|
|
101
|
+
reason: decision.reason,
|
|
102
|
+
...(policyDenial === null
|
|
103
|
+
? {}
|
|
104
|
+
: {
|
|
105
|
+
resourceId: resource.resourceId,
|
|
106
|
+
...(audit.revision === null ? {} : { revision: audit.revision }),
|
|
107
|
+
}),
|
|
108
|
+
...(decision.policy === undefined ? {} : { policy: decision.policy }),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
93
111
|
fact: denial,
|
|
94
112
|
factStream,
|
|
95
113
|
});
|
|
@@ -123,9 +123,18 @@ function changedFilesAuthority(pullRequest, resource, policy) {
|
|
|
123
123
|
if (changedFiles === undefined)
|
|
124
124
|
return denied(PullRequestDenialCode.ChangedFilesUnavailable);
|
|
125
125
|
if (policy.maxFilesChanged !== undefined && changedFiles.length > policy.maxFilesChanged)
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
return {
|
|
127
|
+
allowed: false,
|
|
128
|
+
reason: PullRequestDenialCode.TooManyFilesChanged,
|
|
129
|
+
policy: { maxFilesChanged: policy.maxFilesChanged, filesChanged: changedFiles.length },
|
|
130
|
+
};
|
|
131
|
+
const matchedPaths = policy.blockedPaths.filter((blocked) => changedFiles.includes(blocked));
|
|
132
|
+
return matchedPaths.length > 0
|
|
133
|
+
? {
|
|
134
|
+
allowed: false,
|
|
135
|
+
reason: PullRequestDenialCode.BlockedPathChanged,
|
|
136
|
+
policy: { blockedPaths: [...policy.blockedPaths], matchedPaths },
|
|
137
|
+
}
|
|
129
138
|
: null;
|
|
130
139
|
}
|
|
131
140
|
function checkAuthority(pullRequest, allowPendingChecks) {
|
|
@@ -105,8 +105,15 @@ export async function composeIntegrationRuntime(input) {
|
|
|
105
105
|
],
|
|
106
106
|
});
|
|
107
107
|
const resourceTransitions = createResourceTransitionReactor(input.orchestration, resourceTransitionEvidence);
|
|
108
|
+
const recoverMergedWaits = () => input.orchestration.recoverPolicyBlockedMerges(resourceTransitionEvidence, {
|
|
109
|
+
commandId: input.ids.next('command'),
|
|
110
|
+
correlationId: 'merge-recovery',
|
|
111
|
+
occurredAt: input.clock.now().toISOString(),
|
|
112
|
+
actor: { kind: EventActorKind.System, id: 'merge-recovery' },
|
|
113
|
+
});
|
|
108
114
|
input.orchestration.setAcceptSignalOperationCoordinator(async (operation) => {
|
|
109
115
|
await input.processorRuntime.catchUpThrough('resource-transition signal', await input.journal.latestGlobalPosition(), [resourceTransitions.processor]);
|
|
116
|
+
await recoverMergedWaits();
|
|
110
117
|
return operation();
|
|
111
118
|
});
|
|
112
119
|
const outcomes = new DeliveryOutcomeReactor(input.journal, input.orchestration, input.projections, input.processorState, input.conversations, input.subscriptionRunSerialiser);
|
|
@@ -139,6 +146,7 @@ export async function composeIntegrationRuntime(input) {
|
|
|
139
146
|
});
|
|
140
147
|
}),
|
|
141
148
|
maintain: () => observeMemory(memoryProfile, 'runner.maintenance', async () => {
|
|
149
|
+
await recoverMergedWaits();
|
|
142
150
|
await watchReconciler.reconcileOnce();
|
|
143
151
|
await artifacts.reconcileOnce();
|
|
144
152
|
await outcomes.reconcileOnce();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type WorkflowInstanceView } from '../orchestration/index.js';
|
|
2
|
+
import type { CompositionRoot } from './composition-root.js';
|
|
3
|
+
export declare function mergePolicyBlockForWork(root: CompositionRoot, workflow: WorkflowInstanceView): Promise<import("../surfaces/index.js").MergePolicyBlockResponse | undefined>;
|
|
4
|
+
export declare function presentPrimaryWorkflow(root: CompositionRoot, workflow: WorkflowInstanceView | null): Promise<{
|
|
5
|
+
mergePolicyBlock?: import("../surfaces/index.js").MergePolicyBlockResponse;
|
|
6
|
+
workflowInstanceId: string;
|
|
7
|
+
workItemKey: string;
|
|
8
|
+
workflowName: string;
|
|
9
|
+
orchestrationGroupId: string;
|
|
10
|
+
parentWorkflowInstanceId?: string;
|
|
11
|
+
status: string;
|
|
12
|
+
currentStage: string;
|
|
13
|
+
blockReason?: string;
|
|
14
|
+
retryEligible?: boolean;
|
|
15
|
+
extendEligible?: boolean;
|
|
16
|
+
waitingFor?: {
|
|
17
|
+
readonly signalKind: string;
|
|
18
|
+
};
|
|
19
|
+
} | null>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BuiltInActivityName, PullRequestDenialCode, pullRequestMergeInputSchema, readMergePolicyDenial, selectPrimaryPullRequest, } from '../activities/index.js';
|
|
2
|
+
import { WorkflowStatus } from '../orchestration/index.js';
|
|
3
|
+
import { presentMergePolicyBlock, presentWorkflowInstance } from '../surfaces/index.js';
|
|
4
|
+
export async function mergePolicyBlockForWork(root, workflow) {
|
|
5
|
+
const denial = readMergePolicyDenial(workflow.lastOutcome);
|
|
6
|
+
if (denial === null ||
|
|
7
|
+
workflow.status !== WorkflowStatus.Waiting ||
|
|
8
|
+
workflow.pendingActivation?.activity !== BuiltInActivityName.PullRequestMerge)
|
|
9
|
+
return undefined;
|
|
10
|
+
const selected = selectPrimaryPullRequest(await root.pullRequests.authorityInput(workflow.workItemId), workflow.workItemId);
|
|
11
|
+
if (selected === null ||
|
|
12
|
+
(denial.resourceId !== undefined && denial.resourceId !== selected.pullRequest.resourceId))
|
|
13
|
+
return undefined;
|
|
14
|
+
const policy = denial.policy ??
|
|
15
|
+
(await legacyPolicy(root, workflow, denial.reason, selected.pullRequest.changedFiles));
|
|
16
|
+
return presentMergePolicyBlock({
|
|
17
|
+
workflow,
|
|
18
|
+
pullRequest: selected.pullRequest,
|
|
19
|
+
policy,
|
|
20
|
+
href: root.resolveResourceLink(selected.resource.resource.externalKey) ?? undefined,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async function legacyPolicy(root, workflow, reason, files) {
|
|
24
|
+
const definition = await root.orchestration.definitionFor(workflow).catch(() => null);
|
|
25
|
+
const parsed = pullRequestMergeInputSchema.safeParse(definition?.stages[workflow.currentStage]?.with);
|
|
26
|
+
if (!parsed.success)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (reason === PullRequestDenialCode.BlockedPathChanged)
|
|
29
|
+
return {
|
|
30
|
+
blockedPaths: [...parsed.data.blockedPaths],
|
|
31
|
+
...(files === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { matchedPaths: parsed.data.blockedPaths.filter((path) => files.includes(path)) }),
|
|
34
|
+
};
|
|
35
|
+
if (reason === PullRequestDenialCode.TooManyFilesChanged)
|
|
36
|
+
return {
|
|
37
|
+
...(parsed.data.maxFilesChanged === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: { maxFilesChanged: parsed.data.maxFilesChanged }),
|
|
40
|
+
...(files === undefined ? {} : { filesChanged: files.length }),
|
|
41
|
+
};
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
export async function presentPrimaryWorkflow(root, workflow) {
|
|
45
|
+
if (workflow === null)
|
|
46
|
+
return null;
|
|
47
|
+
const mergePolicyBlock = await mergePolicyBlockForWork(root, workflow);
|
|
48
|
+
return {
|
|
49
|
+
...presentWorkflowInstance(workflow),
|
|
50
|
+
...(mergePolicyBlock === undefined ? {} : { mergePolicyBlock }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -7,6 +7,7 @@ import { workCorrelationsProjection, } from '../resources/index.js';
|
|
|
7
7
|
import { ApiCommandStatus, fromWorkItemKey, presentResource, presentRun, presentWorkflowInstance, presentWorkItem, toWorkItemKey, } from '../surfaces/index.js';
|
|
8
8
|
import { workItemId, WorkStatus } from '../work/index.js';
|
|
9
9
|
import { primaryExternalRef } from './external-ref.js';
|
|
10
|
+
import { presentPrimaryWorkflow } from './surface-api-merge-policy.js';
|
|
10
11
|
import { projectionMeta } from './surface-api-metadata.js';
|
|
11
12
|
import { projectionPage } from './surface-api-projection-pages.js';
|
|
12
13
|
import { enrichRun } from './surface-api-run-context.js';
|
|
@@ -200,7 +201,7 @@ async function workDetail(root, key, now) {
|
|
|
200
201
|
work: presentDetailWork(work, externalRef, runs),
|
|
201
202
|
resources: resources.map(presentResource(root.resolveResourceLink)),
|
|
202
203
|
orchestration: {
|
|
203
|
-
primary:
|
|
204
|
+
primary: await presentPrimaryWorkflow(root, primary),
|
|
204
205
|
children: workflows
|
|
205
206
|
.filter((value) => value.parentWorkflowInstanceId !== undefined)
|
|
206
207
|
.map(presentWorkflowInstance),
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.59";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResourceTransitionEvidence } from './resource-transition-evidence.js';
|
|
1
2
|
import type { CommandContext, EventJournal, ProjectionStore } from '@atolis-hq/eventing';
|
|
2
3
|
import type { ActivationId, ActivityOutcome } from '../../activities/index.js';
|
|
3
4
|
import { type WorkItemId, type WorkService } from '../../work/index.js';
|
|
@@ -91,6 +92,7 @@ export declare class OrchestrationService {
|
|
|
91
92
|
parent: WorkflowInstanceView;
|
|
92
93
|
watch: Readonly<import("../contracts/config.js").CompiledWatch>;
|
|
93
94
|
}[]>;
|
|
95
|
+
recoverPolicyBlockedMerges(evidence: ResourceTransitionEvidence, context: CommandContext): Promise<void>;
|
|
94
96
|
listResourceTransitionMatches(event: Parameters<AdvanceWorkflow['listResourceTransitionMatches']>[0]): Promise<readonly import("./resource-transition-matching.js").ResourceTransitionMatch[]>;
|
|
95
97
|
applyResourceTransition(workflowInstanceId: WorkflowInstanceId, target: TransitionTarget, evidenceId: string, context: CommandContext): Promise<WorkflowInstanceView | null>;
|
|
96
98
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { recoverPolicyBlockedMerges } from './policy-blocked-merge-recovery.js';
|
|
1
2
|
import { WorkStatus } from '../../work/index.js';
|
|
2
3
|
import { selectOrchestrationEvent } from '../contracts/event-decoder.js';
|
|
3
4
|
import { OrchestrationEventType } from '../contracts/events.js';
|
|
@@ -228,6 +229,9 @@ export class OrchestrationService {
|
|
|
228
229
|
listWatchMatches(event, context) {
|
|
229
230
|
return this.advanceWorkflow.listWatchMatches(event, context);
|
|
230
231
|
}
|
|
232
|
+
recoverPolicyBlockedMerges(evidence, context) {
|
|
233
|
+
return recoverPolicyBlockedMerges(new OrchestrationRepository(this.journal), this.startWorkflow, evidence, context);
|
|
234
|
+
}
|
|
231
235
|
listResourceTransitionMatches(event) {
|
|
232
236
|
return this.advanceWorkflow.listResourceTransitionMatches(event);
|
|
233
237
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandContext } from '@atolis-hq/eventing';
|
|
2
|
+
import type { WorkflowInstanceView } from '../contracts/views.js';
|
|
3
|
+
import type { OrchestrationRepository } from './orchestration-repository.js';
|
|
4
|
+
import type { ResourceTransitionEvidence } from './resource-transition-evidence.js';
|
|
5
|
+
import type { StartWorkflow } from './start-workflow.js';
|
|
6
|
+
export declare function recoverPolicyBlockedMerges(repository: OrchestrationRepository, workflows: StartWorkflow, evidence: ResourceTransitionEvidence, context: CommandContext): Promise<void>;
|
|
7
|
+
export declare function isPolicyBlockedMergeWait(view: WorkflowInstanceView): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ActivityEventType, ActivityOutcomeKind, BuiltInActivityName, PullRequestState, readMergePolicyDenial, } from '../../activities/index.js';
|
|
2
|
+
import { TransitionTargetKind, WorkflowStatus } from '../contracts/vocabulary.js';
|
|
3
|
+
import { acceptResourceTransition } from './resource-transition-matching.js';
|
|
4
|
+
export async function recoverPolicyBlockedMerges(repository, workflows, evidence, context) {
|
|
5
|
+
for (const loaded of await repository.list()) {
|
|
6
|
+
const view = loaded.view;
|
|
7
|
+
if (view === null || !isPolicyBlockedMergeWait(view))
|
|
8
|
+
continue;
|
|
9
|
+
const definition = await workflows.definitionForOperation(view, loaded.sequence, context);
|
|
10
|
+
if (definition === null)
|
|
11
|
+
continue;
|
|
12
|
+
const stage = definition.stages[view.currentStage];
|
|
13
|
+
if (!isBlockedRouteWait(stage, view))
|
|
14
|
+
continue;
|
|
15
|
+
// The pinned definition supplies the completion destination, never current config.
|
|
16
|
+
const transitions = mergedTransitions(stage);
|
|
17
|
+
const activationId = view.acceptedOutcomes.at(-1);
|
|
18
|
+
if (transitions === undefined || transitions.length !== 1 || activationId === undefined)
|
|
19
|
+
continue;
|
|
20
|
+
const resolved = await evidence.resolve({
|
|
21
|
+
workItemId: view.workItemId,
|
|
22
|
+
transitions,
|
|
23
|
+
mergeDenialActivationId: activationId,
|
|
24
|
+
});
|
|
25
|
+
if (resolved === null)
|
|
26
|
+
continue;
|
|
27
|
+
await acceptResourceTransition(repository, workflows, view.workflowInstanceId, resolved.transition.target, resolved.evidenceId, { ...context, expectedSequence: loaded.sequence });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function isPolicyBlockedMergeWait(view) {
|
|
31
|
+
return (view.status === WorkflowStatus.Waiting &&
|
|
32
|
+
view.parentWorkflowInstanceId === undefined &&
|
|
33
|
+
view.waitingFor !== undefined &&
|
|
34
|
+
view.waitingFor.intentEventId === undefined &&
|
|
35
|
+
view.pendingActivation?.activity === BuiltInActivityName.PullRequestMerge &&
|
|
36
|
+
readMergePolicyDenial(view.lastOutcome) !== null);
|
|
37
|
+
}
|
|
38
|
+
function isBlockedRouteWait(stage, view) {
|
|
39
|
+
const blocked = stage?.on[ActivityOutcomeKind.Blocked];
|
|
40
|
+
return (stage?.activity === BuiltInActivityName.PullRequestMerge &&
|
|
41
|
+
blocked?.target.kind === TransitionTargetKind.AwaitSignal &&
|
|
42
|
+
blocked.target.signal === view.waitingFor?.signalKind);
|
|
43
|
+
}
|
|
44
|
+
function isMergedTransition(transition) {
|
|
45
|
+
return (transition.event === ActivityEventType.PrStateChanged &&
|
|
46
|
+
transition.where !== undefined &&
|
|
47
|
+
'state' in transition.where &&
|
|
48
|
+
transition.where.state === PullRequestState.Merged &&
|
|
49
|
+
(transition.target.kind === TransitionTargetKind.Stage ||
|
|
50
|
+
transition.target.kind === TransitionTargetKind.Complete));
|
|
51
|
+
}
|
|
52
|
+
function mergedTransitions(stage) {
|
|
53
|
+
return stage?.on[ActivityOutcomeKind.Done]?.resourceTransitions?.filter(isMergedTransition);
|
|
54
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActivityEventType, ActivityResourceRole, decidePullRequestAuthority, PullRequestCheckState, PullRequestState, selectActivityEvent, selectPrimaryPullRequest, } from '../../activities/index.js';
|
|
1
|
+
import { ActivityEventType, ActivityResourceRole, decidePullRequestAuthority, PullRequestCheckState, PullRequestDenialCode, PullRequestState, selectActivityEvent, selectPrimaryPullRequest, } from '../../activities/index.js';
|
|
2
2
|
const triggers = [
|
|
3
3
|
ActivityEventType.PrReviewAccepted,
|
|
4
4
|
ActivityEventType.PrStateChanged,
|
|
@@ -7,12 +7,21 @@ const triggers = [
|
|
|
7
7
|
export function createPullRequestTransitionEvidence(pullRequests) {
|
|
8
8
|
return {
|
|
9
9
|
triggers,
|
|
10
|
-
async resolve({ workItemId, transitions, fact, }) {
|
|
10
|
+
async resolve({ workItemId, transitions, fact, mergeDenialActivationId, }) {
|
|
11
11
|
const input = await pullRequests.authorityInput(workItemId);
|
|
12
12
|
const selected = selectPrimaryPullRequest(input, workItemId);
|
|
13
13
|
if (selected === null)
|
|
14
14
|
return null;
|
|
15
15
|
const resourceId = selected.pullRequest.resourceId;
|
|
16
|
+
if (mergeDenialActivationId !== undefined) {
|
|
17
|
+
const denied = (await pullRequests.factsFor(resourceId)).some((entry) => entry.event.eventType === ActivityEventType.PrMergeDenied &&
|
|
18
|
+
entry.event.causationId === mergeDenialActivationId &&
|
|
19
|
+
(entry.event.payload.reason === PullRequestDenialCode.BlockedPathChanged ||
|
|
20
|
+
entry.event.payload.reason === PullRequestDenialCode.TooManyFilesChanged ||
|
|
21
|
+
entry.event.payload.reason === PullRequestDenialCode.ChangedFilesUnavailable));
|
|
22
|
+
if (!denied)
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
16
25
|
const candidates = fact === undefined ? await pullRequests.factsFor(resourceId) : scoped(fact, resourceId);
|
|
17
26
|
if (candidates.length === 0)
|
|
18
27
|
return null;
|
|
@@ -6,6 +6,8 @@ export interface ResourceTransitionEvidenceInput {
|
|
|
6
6
|
readonly workItemId: WorkItemId;
|
|
7
7
|
readonly transitions: readonly CompiledResourceTransition[];
|
|
8
8
|
readonly fact?: ResourceTransitionFact;
|
|
9
|
+
/** Requires a policy denial on the selected PR from this exact activation. */
|
|
10
|
+
readonly mergeDenialActivationId?: string;
|
|
9
11
|
}
|
|
10
12
|
export interface ResourceTransitionEvidenceResolution {
|
|
11
13
|
readonly transition: CompiledResourceTransition;
|