patchrelay 0.68.2 → 0.68.4

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.
@@ -7,6 +7,7 @@ import { isDelegatedToPatchRelay, resolveDelegationTruth } from "./delegation-tr
7
7
  import { syncIssueDependencies } from "./issue-dependency-sync.js";
8
8
  import { resolveLinkedPrAdoption } from "./linked-pr-adoption.js";
9
9
  import { buildOperatorRetryEvent } from "../operator-retry-event.js";
10
+ import { resolveIssueUpdatePlan } from "./issue-update-plan.js";
10
11
  export class DesiredStageRecorder {
11
12
  db;
12
13
  linearProvider;
@@ -143,6 +144,21 @@ export class DesiredStageRecorder {
143
144
  delegated,
144
145
  });
145
146
  const terminalRunRelease = effectiveRunRelease.release && terminal;
147
+ const resolvedPlan = resolveIssueUpdatePlan({
148
+ existingIssue: Boolean(existingIssue),
149
+ delegated,
150
+ incomingAgentSessionId,
151
+ startupResume,
152
+ desiredStage,
153
+ terminalRunRelease,
154
+ blockerPausedImplementation,
155
+ undelegation,
156
+ clearPending,
157
+ effectiveRunRelease,
158
+ shouldEnterOrchestrationSettle,
159
+ agentSessionId,
160
+ computeOrchestrationSettleUntil,
161
+ });
146
162
  const commitIssueUpdate = () => {
147
163
  const record = this.db.issues.upsertIssue({
148
164
  projectId: params.project.id,
@@ -161,24 +177,7 @@ export class DesiredStageRecorder {
161
177
  ...(hydratedIssue.stateType ? { currentLinearStateType: hydratedIssue.stateType } : {}),
162
178
  ...(linkedPrAdoption?.issueUpdates ?? {}),
163
179
  delegatedToPatchRelay: delegated,
164
- ...(!existingIssue && !delegated && incomingAgentSessionId ? { factoryState: "awaiting_input" } : {}),
165
- ...(startupResume.factoryState ? { factoryState: startupResume.factoryState } : {}),
166
- ...(startupResume.pendingRunType !== undefined
167
- ? {
168
- pendingRunType: null,
169
- pendingRunContextJson: startupResume.pendingRunContext
170
- ? JSON.stringify(startupResume.pendingRunContext)
171
- : null,
172
- }
173
- : {}),
174
- ...(!startupResume.factoryState && desiredStage ? { pendingRunType: null, pendingRunContextJson: null, factoryState: "delegated" } : {}),
175
- ...(clearPending ? { pendingRunType: null, pendingRunContextJson: null } : {}),
176
- ...(agentSessionId !== undefined ? { agentSessionId } : {}),
177
- ...(effectiveRunRelease.release ? { activeRunId: null } : {}),
178
- ...(terminalRunRelease ? { factoryState: "done", pendingRunType: null, pendingRunContextJson: null } : {}),
179
- ...(blockerPausedImplementation ? { factoryState: "delegated" } : {}),
180
- ...(undelegation.factoryState ? { factoryState: undelegation.factoryState } : {}),
181
- ...(shouldEnterOrchestrationSettle ? { orchestrationSettleUntil: computeOrchestrationSettleUntil() } : {}),
180
+ ...resolvedPlan,
182
181
  });
183
182
  if (effectiveRunRelease.release && activeRun && effectiveRunRelease.reason) {
184
183
  this.db.runs.finishRun(activeRun.id, { status: "released", failureReason: effectiveRunRelease.reason });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Picks the single factoryState the caller should write. Priority is the
3
+ * inverse of the previous spread order — the LAST spread wins in JS, so we
4
+ * reverse that into a TOP-DOWN check:
5
+ *
6
+ * 1. explicit undelegation
7
+ * 2. blocker-paused implementation (force back to `delegated`)
8
+ * 3. terminal run release (mark `done`)
9
+ * 4. fresh `desiredStage` decision (mark `delegated`)
10
+ * — but only if startupResume didn't already pick a state
11
+ * 5. startup resume override
12
+ * 6. new undelegated + agent-session fallback → `awaiting_input`
13
+ * 7. no change
14
+ */
15
+ export function resolveFactoryState(input) {
16
+ if (input.undelegation.factoryState)
17
+ return input.undelegation.factoryState;
18
+ if (input.blockerPausedImplementation)
19
+ return "delegated";
20
+ if (input.terminalRunRelease)
21
+ return "done";
22
+ if (input.desiredStage && !input.startupResume.factoryState)
23
+ return "delegated";
24
+ if (input.startupResume.factoryState)
25
+ return input.startupResume.factoryState;
26
+ if (!input.existingIssue && !input.delegated && input.incomingAgentSessionId) {
27
+ return "awaiting_input";
28
+ }
29
+ return undefined;
30
+ }
31
+ /**
32
+ * Should we clear `pendingRunType` + `pendingRunContextJson`? Several upstream
33
+ * conditions can independently request this; the resolver folds them into one
34
+ * predicate.
35
+ */
36
+ function shouldClearPending(input) {
37
+ if (input.terminalRunRelease)
38
+ return true;
39
+ if (input.clearPending)
40
+ return true;
41
+ if (input.desiredStage && !input.startupResume.factoryState)
42
+ return true;
43
+ if (input.startupResume.pendingRunType !== undefined)
44
+ return true;
45
+ return false;
46
+ }
47
+ function buildStartupResumeContextJson(input) {
48
+ if (input.startupResume.pendingRunType === undefined)
49
+ return undefined;
50
+ return input.startupResume.pendingRunContext
51
+ ? JSON.stringify(input.startupResume.pendingRunContext)
52
+ : null;
53
+ }
54
+ export function resolveIssueUpdatePlan(input) {
55
+ const resolved = {};
56
+ const factoryState = resolveFactoryState(input);
57
+ if (factoryState) {
58
+ resolved.factoryState = factoryState;
59
+ }
60
+ if (shouldClearPending(input)) {
61
+ resolved.pendingRunType = null;
62
+ resolved.pendingRunContextJson = buildStartupResumeContextJson(input) ?? null;
63
+ }
64
+ if (input.effectiveRunRelease.release) {
65
+ resolved.activeRunId = null;
66
+ }
67
+ if (input.agentSessionId !== undefined) {
68
+ resolved.agentSessionId = input.agentSessionId;
69
+ }
70
+ if (input.shouldEnterOrchestrationSettle) {
71
+ resolved.orchestrationSettleUntil = input.computeOrchestrationSettleUntil();
72
+ }
73
+ return resolved;
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.68.2",
3
+ "version": "0.68.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {