@atolis-hq/wake 0.3.79 → 0.3.80
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.
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { ResourceCorrelationRole } from '../resources/index.js';
|
|
2
2
|
export function createCapabilityResourceTransitionEvidence(input) {
|
|
3
|
+
const registrationFor = (capabilities) => input.policies.find((registration) => registration.capabilities.some((capability) => capabilities.includes(capability)));
|
|
3
4
|
return {
|
|
4
5
|
triggers: [...new Set(input.policies.flatMap(({ policy }) => policy.triggers))],
|
|
5
6
|
async resolve(evidence) {
|
|
7
|
+
// A work item may hold several primary correlations (e.g. its
|
|
8
|
+
// originating issue and an implementation PR). Only the correlation
|
|
9
|
+
// whose resource matches a registered policy's capabilities is
|
|
10
|
+
// relevant here; the rest drop out rather than blocking resolution.
|
|
6
11
|
const correlations = await input.resources.correlationsForWork(evidence.workItemId);
|
|
7
12
|
const primaries = correlations.filter(({ role }) => role === ResourceCorrelationRole.Primary);
|
|
8
|
-
|
|
13
|
+
const registrations = [];
|
|
14
|
+
for (const primary of primaries) {
|
|
15
|
+
const resource = await input.resources.get(primary.resourceId);
|
|
16
|
+
if (resource === null)
|
|
17
|
+
continue;
|
|
18
|
+
const registration = registrationFor(resource.capabilities);
|
|
19
|
+
if (registration !== undefined)
|
|
20
|
+
registrations.push(registration);
|
|
21
|
+
}
|
|
22
|
+
if (registrations.length !== 1)
|
|
9
23
|
return null;
|
|
10
|
-
|
|
11
|
-
if (resource === null)
|
|
12
|
-
return null;
|
|
13
|
-
const registration = input.policies.find(({ capabilities }) => capabilities.some((capability) => resource.capabilities.includes(capability)));
|
|
14
|
-
return registration?.policy.resolve(evidence) ?? null;
|
|
24
|
+
return registrations[0].policy.resolve(evidence);
|
|
15
25
|
},
|
|
16
26
|
};
|
|
17
27
|
}
|
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
import { ActivityOutcomeKind, BuiltInActivityName } from '../../activities/index.js';
|
|
2
2
|
import { OrchestrationEventType, WatchGateVerdictSignal } from '../contracts/events.js';
|
|
3
|
-
import { stageName, watchId } from '../contracts/identifiers.js';
|
|
3
|
+
import { signalName, stageName, watchId } from '../contracts/identifiers.js';
|
|
4
4
|
import { ActivityActivationStatus, ApprovalAuthorityKind, WorkflowStatus, } from '../contracts/vocabulary.js';
|
|
5
5
|
import { activation, nextOrdinal, stateDraft } from './decision-events.js';
|
|
6
|
+
// `then: await-human` on a `failed` route compiles its wait to this literal
|
|
7
|
+
// signal name (see compileTarget); a workflow parked there has exhausted
|
|
8
|
+
// whatever automatic retries it had and is durably equivalent to a blocked
|
|
9
|
+
// failed stage for operator-retry purposes.
|
|
10
|
+
const FailedOutcomeSignal = signalName(ActivityOutcomeKind.Failed);
|
|
6
11
|
export function isOperatorRetryEligible(view) {
|
|
7
12
|
const pending = view.pendingActivation;
|
|
8
|
-
const eligibleActivation =
|
|
9
|
-
pending !== undefined &&
|
|
13
|
+
const eligibleActivation = pending !== undefined &&
|
|
10
14
|
pending.status === ActivityActivationStatus.Completed &&
|
|
11
15
|
pending.supplemental !== true &&
|
|
12
16
|
pending.followOnIndex === undefined &&
|
|
13
17
|
view.acceptedOutcomes.includes(pending.activationId);
|
|
14
18
|
if (!eligibleActivation || pending === undefined)
|
|
15
19
|
return false;
|
|
20
|
+
if (view.status === WorkflowStatus.Blocked)
|
|
21
|
+
return isRetryEligibleBlock(view, pending);
|
|
22
|
+
return isRetryEligibleFailedWait(view);
|
|
23
|
+
}
|
|
24
|
+
function isRetryEligibleBlock(view, pending) {
|
|
16
25
|
return ((view.blockReason === 'unconfigured outcome failed' &&
|
|
17
26
|
view.lastOutcome?.kind === ActivityOutcomeKind.Failed) ||
|
|
18
27
|
view.executionFailure?.activationId === pending.activationId);
|
|
19
28
|
}
|
|
29
|
+
function isRetryEligibleFailedWait(view) {
|
|
30
|
+
return (view.status === WorkflowStatus.Waiting &&
|
|
31
|
+
view.waitingFor?.signalKind === FailedOutcomeSignal &&
|
|
32
|
+
view.lastOutcome?.kind === ActivityOutcomeKind.Failed);
|
|
33
|
+
}
|
|
20
34
|
export function selectOperatorRetryTarget(workflows) {
|
|
21
35
|
const primary = workflows.find((workflow) => workflow.parentWorkflowInstanceId === undefined);
|
|
22
36
|
if (primary === undefined)
|
|
@@ -52,7 +66,7 @@ export function requestOperatorRetry(definition, state, input) {
|
|
|
52
66
|
if (!isOperatorRetryEligible(state))
|
|
53
67
|
return {
|
|
54
68
|
kind: 'ignored',
|
|
55
|
-
reason: 'workflow is not
|
|
69
|
+
reason: 'workflow is not in a retryable failed-stage state',
|
|
56
70
|
};
|
|
57
71
|
const stage = definition.stages[stageName(state.currentStage)];
|
|
58
72
|
const events = [
|