agents-relay 2.0.38 → 2.0.39
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/reconciler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { storySizeTimeoutMs, chatGptStartAckTimeoutMs } from './types.js';
|
|
3
|
-
import { dependencyReady, schedule,
|
|
3
|
+
import { dependencyReady, schedule, transitionTaskEvent } from './scheduler.js';
|
|
4
4
|
import { applyTaskCancelAction } from './task-actions.js';
|
|
5
5
|
import { LaunchFailure, StartAckTimeout } from './adapters.js';
|
|
6
6
|
import { eventFor } from './events.js';
|
|
@@ -13,7 +13,6 @@ import { executeManualPlan } from './manual-plan.js';
|
|
|
13
13
|
import { executionEventSource, withExecutionBoundary } from './reconciler-runtime.js';
|
|
14
14
|
export { executionEventSource } from './reconciler-runtime.js';
|
|
15
15
|
import { CallbackWorkflowEffectHandler } from './workflow/effects.js';
|
|
16
|
-
import { applyHistoricalJobCompatibilityRepair } from './workflow/runtime.js';
|
|
17
16
|
import { pendingTroubleshooterTasks } from './troubleshooter.js';
|
|
18
17
|
import { prepareManagedExecutionWorktree } from './workspace.js';
|
|
19
18
|
import { createReviewChairExecutionPlan } from './review-chair.js';
|
|
@@ -119,23 +118,10 @@ export class Reconciler extends ReconcilerReviewBase {
|
|
|
119
118
|
await this.invalidateStaleReview(job, headSha);
|
|
120
119
|
const now = (this.options.now ?? new Date()).getTime();
|
|
121
120
|
if (prState !== 'OPEN') {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// Compatibility repair boundary for historical records that persisted runtime status as lifecycle.
|
|
127
|
-
applyHistoricalJobCompatibilityRepair(job, terminalState);
|
|
128
|
-
await this.store.saveJob(job);
|
|
129
|
-
const outcomes = await this.workflowEffects.execute(['effect.cancelActiveTasks'], { jobId: job.id, transitionKey: `compatibility:${prState}`, data: { reason } });
|
|
130
|
-
await this.applyJobEffectOutcomes(job, outcomes);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
await transitionJobEvent(job, prState === 'MERGED' ? 'pr.merged' : 'pr.closed', prState === 'CLOSED' ? { notMerged: true } : {}, async (actionId) => {
|
|
134
|
-
const outcomes = await this.workflowEffects.execute([actionId], { jobId: job.id, transitionKey: `terminal:${prState}`, data: { reason } });
|
|
135
|
-
await this.applyJobEffectOutcomes(job, outcomes);
|
|
136
|
-
}, async () => { await this.store.saveJob(job); });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
121
|
+
await dispatchJobReconcile(job, { prState, headSha, mergeCapable: Boolean(this.store.mergePullRequest) }, async (actionId) => {
|
|
122
|
+
const outcomes = await this.workflowEffects.execute([actionId], { jobId: job.id, transitionKey: `terminal:${prState}`, data: { reason: `Pull request ${prState.toLowerCase()}` } });
|
|
123
|
+
await this.applyJobEffectOutcomes(job, outcomes);
|
|
124
|
+
}, async () => { await this.store.saveJob(job); });
|
|
139
125
|
if (prState === 'MERGED' && this.store.resolveBlockerDependents) {
|
|
140
126
|
const resolved = await this.store.resolveBlockerDependents(job.id, 'merge');
|
|
141
127
|
for (const target of resolved.unblocked)
|
|
@@ -1,19 +1,65 @@
|
|
|
1
1
|
import { decideJobLifecycle, dispatchJobLifecycle } from './lifecycle.js';
|
|
2
2
|
import { implementationComplete } from '../runtime-status.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { applyHistoricalJobCompatibilityRepair } from './runtime.js';
|
|
4
|
+
function lifecycleInput(facts) {
|
|
5
|
+
if (facts.prState === 'MERGED')
|
|
6
|
+
return { event: 'pr.merged', context: {} };
|
|
7
|
+
if (facts.prState === 'CLOSED')
|
|
8
|
+
return { event: 'pr.closed', context: { notMerged: true } };
|
|
9
|
+
return {
|
|
10
|
+
event: 'reconcile',
|
|
11
|
+
context: {
|
|
12
|
+
prOpen: true,
|
|
13
|
+
headAvailable: Boolean(facts.headSha),
|
|
14
|
+
mergeCapable: facts.mergeCapable,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function graphFacts(job, facts) {
|
|
19
|
+
const input = lifecycleInput(facts);
|
|
20
|
+
if (input.event !== 'reconcile')
|
|
21
|
+
return input.context;
|
|
22
|
+
return {
|
|
23
|
+
...input.context,
|
|
6
24
|
reviewDisabled: job.reviewEnabled === false,
|
|
7
25
|
implementationComplete: implementationComplete(job),
|
|
8
26
|
autoMerge: job.autoMerge !== false,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Historical compatibility only. Normal Job lifecycle is always decided by
|
|
31
|
+
* workflow/job-lifecycle.mmd from the PR fact-derived event.
|
|
32
|
+
*/
|
|
33
|
+
function repairHistoricalTerminalState(job, facts) {
|
|
34
|
+
if (['DRAFT', 'ACTIVE', 'COMPLETED', 'CANCELLED'].includes(job.state))
|
|
35
|
+
return null;
|
|
36
|
+
if (facts.prState === 'MERGED')
|
|
37
|
+
applyHistoricalJobCompatibilityRepair(job, 'COMPLETED');
|
|
38
|
+
else if (facts.prState === 'CLOSED')
|
|
39
|
+
applyHistoricalJobCompatibilityRepair(job, 'CANCELLED');
|
|
40
|
+
else
|
|
41
|
+
return null;
|
|
42
|
+
return job.state;
|
|
43
|
+
}
|
|
44
|
+
export function decideJobReconcile(job, facts) {
|
|
45
|
+
const input = lifecycleInput(facts);
|
|
46
|
+
return decideJobLifecycle(job, input.event, graphFacts(job, facts));
|
|
12
47
|
}
|
|
13
48
|
export async function dispatchJobReconcile(job, facts, executeEffect, persist) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
49
|
+
const repaired = repairHistoricalTerminalState(job, facts);
|
|
50
|
+
if (repaired) {
|
|
51
|
+
await persist?.();
|
|
52
|
+
await executeEffect?.('effect.cancelActiveTasks');
|
|
53
|
+
return {
|
|
54
|
+
accepted: true,
|
|
55
|
+
target: repaired,
|
|
56
|
+
guard: null,
|
|
57
|
+
actions: ['effect.cancelActiveTasks'],
|
|
58
|
+
mutations: [],
|
|
59
|
+
effects: ['effect.cancelActiveTasks'],
|
|
60
|
+
reason: 'accepted',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const input = lifecycleInput(facts);
|
|
64
|
+
return dispatchJobLifecycle(job, input.event, graphFacts(job, facts), executeEffect, persist);
|
|
19
65
|
}
|
package/package.json
CHANGED
|
@@ -37,3 +37,11 @@ A **P0 job** (alias: **self job**) is executed directly by the current active or
|
|
|
37
37
|
6. After implementation and local verification, the orchestrator merges the job itself and completes normal post-merge reconciliation.
|
|
38
38
|
|
|
39
39
|
`job create` still only creates durable job state; the P0 workflow requires the orchestrator to follow it immediately with task creation and execution.
|
|
40
|
+
|
|
41
|
+
## Merge completion vs deployment evidence
|
|
42
|
+
|
|
43
|
+
For a normal one-shot managed Job, the canonical Job lifecycle graph owns completion: a durable `pr.merged` fact transitions the Job to `COMPLETED`. Reconciler and deployment code must not invent a second completion gate after merge.
|
|
44
|
+
|
|
45
|
+
Post-merge publication and deployment activity is separate from Job lifecycle. Events or evidence such as `package.published`, published version metadata, service restart, and deployment verification may be attached after the Job is already `COMPLETED`; they must not hold the Job open, reopen it, or redefine completion. If post-merge deployment needs remediation, record that as separate deployment/recovery work rather than mutating the completed implementation Job lifecycle.
|
|
46
|
+
|
|
47
|
+
A PR closed without merge follows the same graph-owned rule through `pr.closed -> CANCELLED`.
|