@zq-silk/yui 0.14.0 → 0.14.2

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 (179) hide show
  1. package/ARCHITECTURE.md +64 -35
  2. package/README.md +258 -125
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  5. package/dist/cli/commandCatalog.js +74 -109
  6. package/dist/cli/helpRenderer.js +1 -2
  7. package/dist/cli/interactionCandidates.js +10 -0
  8. package/dist/cli/interactionPolicy.js +76 -37
  9. package/dist/cli/roleWizard.js +5 -6
  10. package/dist/cli/updateOrchestrator.js +10 -8
  11. package/dist/cli/updatePorts.js +17 -13
  12. package/dist/cli/upgradeCommand.js +2 -5
  13. package/dist/cli.js +268 -231
  14. package/dist/commands/agentCommands.js +13 -0
  15. package/dist/commands/configCommands.js +7 -19
  16. package/dist/commands/configOverview.js +3 -2
  17. package/dist/commands/durableJobCommands.js +1 -1
  18. package/dist/commands/executionAuditCommands.js +4 -9
  19. package/dist/commands/globalRoleCommands.js +17 -0
  20. package/dist/commands/profileCommands.js +200 -30
  21. package/dist/commands/sessionCommands.js +1 -1
  22. package/dist/commands/taskActor.js +29 -127
  23. package/dist/commands/taskChangeSetCommands.js +1 -3
  24. package/dist/commands/taskCommands.js +1771 -2107
  25. package/dist/commands/taskCompletionGate.js +27 -103
  26. package/dist/commands/taskContextCommand.js +47 -80
  27. package/dist/commands/taskExecutionCommands.js +9 -27
  28. package/dist/commands/taskInputCommands.js +10 -10
  29. package/dist/commands/taskIntegrationCommands.js +69 -61
  30. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  31. package/dist/commands/taskNextActionCommand.js +8 -29
  32. package/dist/commands/taskOverviewCommand.js +25 -3
  33. package/dist/commands/taskPublicationCommands.js +151 -85
  34. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  35. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  36. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  37. package/dist/commands/taskUpstreamCommands.js +79 -95
  38. package/dist/config/configCatalog.js +1 -1
  39. package/dist/context/contextSnapshot.js +17 -2
  40. package/dist/context/sessionBootstrapManifest.js +18 -1
  41. package/dist/context/sourceTurnContext.js +30 -0
  42. package/dist/context/turnContextPack.js +107 -48
  43. package/dist/context/turnInputContract.js +11 -4
  44. package/dist/context/wakeNotification.js +84 -22
  45. package/dist/controller/agentRuntimeObserver.js +9 -12
  46. package/dist/controller/clientRuntime.js +13 -13
  47. package/dist/controller/controller.js +11 -11
  48. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  49. package/dist/controller/jobControl.js +11 -10
  50. package/dist/controller/resourceInventory.js +6 -6
  51. package/dist/controller/resourceInventoryLinux.js +2 -2
  52. package/dist/controller/runtime.js +18 -61
  53. package/dist/controller/runtimeEventInbox.js +57 -34
  54. package/dist/controller/runtimeEventProcessor.js +36 -96
  55. package/dist/controller/runtimeHookTurnFence.js +33 -57
  56. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  57. package/dist/controller/runtimeObservationHook.js +18 -12
  58. package/dist/controller/sessionNotify.js +43 -28
  59. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  60. package/dist/controller/structuredProviderObservation.js +13 -16
  61. package/dist/coordination/workMailboxQueue.js +132 -0
  62. package/dist/core/controllerServer.js +1 -0
  63. package/dist/domain/agentResultTransport.js +60 -0
  64. package/dist/execution/executionHealth.js +111 -118
  65. package/dist/execution/reviewMainTurn.js +161 -0
  66. package/dist/execution/workItemExecution.js +454 -0
  67. package/dist/execution/workItemExecutionProjection.js +309 -0
  68. package/dist/execution/workItemMainTurn.js +164 -0
  69. package/dist/executor/agentExecutor.js +19 -19
  70. package/dist/executor/executorRegistry.js +7 -28
  71. package/dist/executor/fileRoleLaunchPlanner.js +51 -83
  72. package/dist/external/githubPublicationVerifier.js +114 -0
  73. package/dist/external/gitlabPublicationVerifier.js +158 -0
  74. package/dist/external/pinnedCommandRunner.js +73 -0
  75. package/dist/integration/changeSetManifest.js +0 -3
  76. package/dist/integration/deliveryObligation.js +43 -6
  77. package/dist/integration/gitIntegrationService.js +284 -101
  78. package/dist/integration/integrationAttempt.js +64 -14
  79. package/dist/integration/integrationQueueEntry.js +5 -35
  80. package/dist/integration/integrationQueueService.js +65 -194
  81. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  82. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  83. package/dist/message/message.js +16 -0
  84. package/dist/observability/executionAudit.js +4 -25
  85. package/dist/observability/faultClassification.js +58 -70
  86. package/dist/observability/orchestrationMetrics.js +8 -50
  87. package/dist/profile/agentProfile.js +60 -15
  88. package/dist/profile/agentProfileRuntime.js +100 -0
  89. package/dist/release/releaseWorkflowPorts.js +5 -68
  90. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  91. package/dist/repository/gitWorkspace.js +9 -67
  92. package/dist/repository/taskBaseFreshness.js +3 -1
  93. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  94. package/dist/repository/taskWorkspacePreparer.js +252 -411
  95. package/dist/resources/liveReferences.js +6 -55
  96. package/dist/resources/resourceDiscovery.js +1 -30
  97. package/dist/resources/resourceRegistrar.js +0 -23
  98. package/dist/review/deltaRecheck.js +8 -65
  99. package/dist/review/reviewAcceptance.js +22 -19
  100. package/dist/review/reviewConfig.js +0 -10
  101. package/dist/review/reviewDecision.js +2 -2
  102. package/dist/review/reviewRound.js +147 -273
  103. package/dist/review/reviewerAvailability.js +13 -23
  104. package/dist/role/role.js +3 -0
  105. package/dist/runtime/agentDriverObservation.js +1 -1
  106. package/dist/runtime/agentHost.js +51 -51
  107. package/dist/runtime/builtinAgentDrivers.js +9 -2
  108. package/dist/runtime/codexAppServerRuntime.js +23 -5
  109. package/dist/runtime/exactControlPlane.js +2 -267
  110. package/dist/runtime/firstProgressAdvisory.js +1 -1
  111. package/dist/runtime/index.js +1 -1
  112. package/dist/runtime/launchBroker.js +11 -11
  113. package/dist/runtime/lifecycleReservation.js +2 -2
  114. package/dist/runtime/managedCaller.js +111 -0
  115. package/dist/runtime/ports.js +16 -3
  116. package/dist/runtime/preallocatedNativeSession.js +2 -2
  117. package/dist/runtime/processExitObservation.js +3 -2
  118. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  119. package/dist/runtime/runtimeBinding.js +1 -1
  120. package/dist/runtime/runtimeObservation.js +33 -16
  121. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  122. package/dist/runtime/runtimeStopReceipt.js +11 -11
  123. package/dist/runtime/sessionLaunchRequest.js +2 -2
  124. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  125. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  126. package/dist/runtime/sessionReconciliation.js +4 -4
  127. package/dist/runtime/sessionTerminationGuard.js +9 -9
  128. package/dist/runtime/sessionTokenMetrics.js +5 -7
  129. package/dist/runtime/structuredProviderHost.js +5 -5
  130. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  131. package/dist/runtime/tmuxAdapters.js +32 -30
  132. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  133. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  134. package/dist/scheduler/roleTurnLiveness.js +6 -6
  135. package/dist/scheduler/roleTurnStall.js +8 -9
  136. package/dist/scheduler/taskExecutionProjection.js +19 -41
  137. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  138. package/dist/storage/sqliteSchema.js +1 -21
  139. package/dist/storage/sqliteStore.js +62 -47
  140. package/dist/storage/storageSchema.js +1 -8
  141. package/dist/storage/storageVersions.js +7 -4
  142. package/dist/storage/taskStore.js +8 -8
  143. package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
  144. package/dist/task/completionReadiness.js +17 -98
  145. package/dist/task/deliveryGuard.js +9 -11
  146. package/dist/task/draftPlan.js +142 -0
  147. package/dist/task/nextAction.js +179 -477
  148. package/dist/task/publicationVerification.js +1 -0
  149. package/dist/task/remoteDelivery.js +233 -0
  150. package/dist/task/task.js +108 -4
  151. package/dist/task/taskRecordReference.js +0 -1
  152. package/dist/turn/turn.js +109 -16
  153. package/dist/verification/verificationGateService.js +0 -26
  154. package/dist/web/assets/client/components.js +99 -121
  155. package/dist/web/assets/client/i18n.js +136 -58
  156. package/dist/web/assets/client/view.js +44 -0
  157. package/dist/web/assets/styles/cards.js +3 -15
  158. package/dist/web/assets/styles/widgets.js +4 -0
  159. package/dist/web/webSnapshot.js +29 -14
  160. package/dist/workItem/dependencyGate.js +34 -0
  161. package/dist/workItem/workItem.js +99 -266
  162. package/dist/workspace/workItemChangeSetManager.js +15 -59
  163. package/docs/task-local-identity.md +1 -1
  164. package/i18n/README.zh-CN.md +165 -59
  165. package/package.json +1 -1
  166. package/skills/yui-leader/SKILL.md +272 -782
  167. package/skills/yui-operator/SKILL.md +190 -365
  168. package/skills/yui-reviewer/SKILL.md +40 -33
  169. package/skills/yui-runtime/SKILL.md +27 -6
  170. package/skills/yui-worker/SKILL.md +19 -6
  171. package/dist/execution/candidateConvergence.js +0 -623
  172. package/dist/execution/executionGroup.js +0 -854
  173. package/dist/execution/resourceBroker.js +0 -426
  174. package/dist/executor/launchPlan.js +0 -16
  175. package/dist/review/reviewFinding.js +0 -240
  176. package/dist/review/reviewFindingLedger.js +0 -550
  177. package/dist/review/reviewOutcomeClassifier.js +0 -255
  178. package/dist/scheduler/resourceQueueProjection.js +0 -71
  179. package/dist/task/repairWave.js +0 -150
@@ -4,13 +4,6 @@ import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.j
4
4
  import { projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
5
5
  import { DEFAULT_RUNTIME_HEALTH_POLICY } from "../runtime/runtimeHealthPolicy.js";
6
6
  import { runOwnsBlockingProviderContinuation } from "../runtime/runtimeContinuationProjection.js";
7
- import { summarizeExecutionGroup } from "./executionGroup.js";
8
- import { executionStageSpendClosed, observedExecutionResourceUsage, projectExecutionStageResources } from "./resourceBroker.js";
9
- /**
10
- * Fold existing Turn, runtime, Session, process-exit, and stall facts into the
11
- * four Lane health states. This is a read model only: it never advances a
12
- * Lane, terminalizes a Turn, or guesses that silence means death.
13
- */
14
7
  export function projectExecutionGroupHealth(input) {
15
8
  const policy = input.policy ?? DEFAULT_RUNTIME_HEALTH_POLICY;
16
9
  const lanes = input.group.lanes.map((lane) => projectExecutionLaneHealth(lane, input, policy));
@@ -21,79 +14,69 @@ export function projectExecutionGroupHealth(input) {
21
14
  silentLaneCount: countHealth(lanes, "silent"),
22
15
  suspectedStalledLaneCount: countHealth(lanes, "suspected-stalled"),
23
16
  confirmedDeadLaneCount: countHealth(lanes, "confirmed-dead"),
24
- reusableLaneIds: lanes.filter(({ resultReusable }) => resultReusable)
25
- .map(({ laneId }) => laneId),
26
- retryableLaneIds: lanes.filter(({ recovery }) => recovery === "retry-new-turn")
27
- .map(({ laneId }) => laneId)
17
+ reusableLaneIds: lanes.filter(({ resultReusable }) => resultReusable).map(({ laneId }) => laneId),
18
+ retryableLaneIds: lanes.filter(({ recovery }) => recovery === "retry-new-turn").map(({ laneId }) => laneId)
28
19
  });
29
20
  }
30
- /** Structural Group summary plus the health/recovery projection used by CLI/Web reads. */
31
21
  export function summarizeExecutionGroupHealth(input) {
32
- const summary = summarizeExecutionGroup(input.group);
33
- const projection = projectExecutionGroupHealth(input);
34
- const resources = input.group.stage === undefined
35
- ? undefined
36
- : projectExecutionStageResources({
37
- group: input.group,
38
- ...(input.stageGroups === undefined ? {} : { stageGroups: input.stageGroups }),
39
- usage: observedExecutionResourceUsage({
40
- group: input.group,
41
- ...(input.stageGroups === undefined ? {} : { stageGroups: input.stageGroups }),
42
- turns: input.turns,
43
- events: input.events
44
- }),
45
- now: input.now
22
+ const health = projectExecutionGroupHealth(input);
23
+ const healthByLane = new Map(health.lanes.map((lane) => [lane.laneId, lane]));
24
+ const laneSummaries = input.group.lanes.map((lane) => {
25
+ const projected = healthByLane.get(lane.id);
26
+ const turn = exactLaneTurn(input, lane);
27
+ return Object.freeze({
28
+ roleName: lane.roleName,
29
+ ordinal: lane.ordinal,
30
+ ...(lane.currentTurnId === undefined ? {} : { turnId: lane.currentTurnId }),
31
+ ...(lane.successfulTurnId === undefined
32
+ ? {}
33
+ : { successfulTurnId: lane.successfulTurnId }),
34
+ status: projectedStatus(lane, turn),
35
+ ...(lane.effective === undefined ? {} : { effective: lane.effective }),
36
+ ...projected
46
37
  });
47
- const healthByLane = new Map(projection.lanes.map((lane) => [lane.laneId, lane]));
38
+ });
48
39
  return Object.freeze({
49
- ...summary,
50
- laneSummaries: summary.laneSummaries.map((lane) => Object.freeze({
51
- ...lane,
52
- ...healthByLane.get(lane.laneId)
53
- })),
40
+ groupId: input.group.id,
41
+ purpose: "reviewRoundId" in input.group.assignment ? "review" : "execution",
42
+ kind: "replicated",
43
+ laneCount: input.group.lanes.length,
44
+ activeLaneCount: laneSummaries.filter(({ status }) => status === "running").length,
45
+ terminalLaneCount: input.group.lanes.filter(({ disposition }) => disposition !== "open").length,
46
+ succeededLaneCount: input.group.lanes.filter(({ disposition }) => disposition === "succeeded").length,
47
+ failedLaneCount: input.group.lanes.filter(({ disposition }) => disposition === "failed").length,
48
+ laneSummaries,
54
49
  health: Object.freeze({
55
- activeLaneCount: projection.activeLaneCount,
56
- silentLaneCount: projection.silentLaneCount,
57
- suspectedStalledLaneCount: projection.suspectedStalledLaneCount,
58
- confirmedDeadLaneCount: projection.confirmedDeadLaneCount,
59
- reusableLaneIds: projection.reusableLaneIds,
60
- retryableLaneIds: projection.retryableLaneIds
61
- }),
62
- ...(resources === undefined ? {} : { resources })
50
+ activeLaneCount: health.activeLaneCount,
51
+ silentLaneCount: health.silentLaneCount,
52
+ suspectedStalledLaneCount: health.suspectedStalledLaneCount,
53
+ confirmedDeadLaneCount: health.confirmedDeadLaneCount,
54
+ reusableLaneIds: health.reusableLaneIds,
55
+ retryableLaneIds: health.retryableLaneIds
56
+ })
63
57
  });
64
58
  }
65
- /** Unresolved Lane recovery in deterministic operational priority order. */
66
59
  export function actionableExecutionLaneRecoveries(groups) {
67
- return groups
68
- .filter(({ resolution }) => resolution === undefined)
69
- .flatMap((group) => group.laneSummaries.flatMap((lane) => {
70
- if (lane.recovery === "retry-new-turn"
71
- && group.resources !== undefined
72
- && executionStageSpendClosed(group.resources))
73
- return [];
74
- if (lane.recovery !== "retry-new-turn")
75
- return [];
76
- return [{
60
+ return groups.flatMap((group) => group.laneSummaries.flatMap((lane) => lane.recovery !== "retry-new-turn"
61
+ ? []
62
+ : [{
77
63
  groupId: group.groupId,
78
64
  laneId: lane.laneId,
79
65
  ...(lane.turnId === undefined ? {} : { turnId: lane.turnId }),
80
66
  ...(lane.runtimeHealth === undefined ? {} : { runtimeHealth: lane.runtimeHealth }),
81
- recovery: lane.recovery
82
- }];
83
- }));
67
+ recovery: "retry-new-turn"
68
+ }]));
84
69
  }
85
70
  function projectExecutionLaneHealth(lane, input, policy) {
86
- const run = lane.turnId === undefined
87
- ? undefined
88
- : input.turns.find((candidate) => exactLaneTurn(candidate, input.group, lane));
89
- const continuationAgentId = run?.effective.agentId ?? lane.effective?.agentId;
90
- if (lane.status !== "completed"
91
- && lane.turnId !== undefined
71
+ const turn = exactLaneTurn(input, lane);
72
+ const continuationAgentId = turn?.effective.agentId ?? lane.effective?.agentId;
73
+ if (lane.disposition === "open"
74
+ && lane.currentTurnId !== undefined
92
75
  && continuationAgentId !== undefined
93
76
  && runOwnsBlockingProviderContinuation(input.events, {
94
77
  taskId: input.group.taskId,
95
78
  roleName: lane.roleName,
96
- turnId: lane.turnId,
79
+ turnId: lane.currentTurnId,
97
80
  agentId: continuationAgentId
98
81
  })) {
99
82
  return projection(lane, {
@@ -104,71 +87,63 @@ function projectExecutionLaneHealth(lane, input, policy) {
104
87
  evidence: ["runtime-continuation-writer-owned"]
105
88
  });
106
89
  }
107
- if (lane.status === "completed") {
90
+ if (lane.disposition === "succeeded") {
108
91
  return projection(lane, {
109
92
  recovery: "reuse-result",
110
93
  resultReusable: true,
111
- reason: "the terminal Lane result is durable and must be reused",
112
- evidence: ["execution-lane-result"]
94
+ reason: "the successful Producer result is durable and immutable",
95
+ evidence: ["execution-lane-success"]
113
96
  });
114
97
  }
115
- if (lane.status === "failed") {
98
+ if (lane.disposition === "failed") {
116
99
  return projection(lane, {
117
100
  runtimeHealth: "confirmed-dead",
118
- recovery: "retry-new-turn",
119
- resultReusable: false,
120
- reason: "the exact Lane attempt is durably failed; a retry must create a new Turn",
121
- evidence: ["execution-lane-terminal-failure"]
122
- });
123
- }
124
- if (lane.status === "skipped") {
125
- return projection(lane, {
126
101
  recovery: "none",
127
102
  resultReusable: false,
128
- reason: "the Lane was never started after sufficient stage evidence made further spend unnecessary",
129
- evidence: ["execution-lane-resource-skip"]
103
+ reason: "the logical Producer Lane was explicitly settled as failed",
104
+ evidence: ["execution-lane-settled-failure"]
130
105
  });
131
106
  }
132
- if (lane.status === "pending") {
107
+ if (lane.currentTurnId === undefined) {
133
108
  return projection(lane, {
134
109
  recovery: "none",
135
110
  resultReusable: false,
136
- reason: "the Lane has not started",
111
+ reason: "the Producer Lane has not been dispatched",
137
112
  evidence: []
138
113
  });
139
114
  }
140
- if (run === undefined) {
115
+ if (turn === undefined) {
141
116
  return projection(lane, {
142
117
  runtimeHealth: "suspected-stalled",
143
118
  recovery: "inspect",
144
119
  resultReusable: false,
145
- reason: "the running Lane has no exact Turn record",
120
+ reason: "the open Lane has no exact current Turn",
146
121
  evidence: ["execution-lineage-missing"]
147
122
  });
148
123
  }
149
- if (run.status === "failed") {
124
+ if (turn.status === "failed") {
150
125
  return projection(lane, {
151
126
  runtimeHealth: "confirmed-dead",
152
127
  recovery: "retry-new-turn",
153
128
  resultReusable: false,
154
- reason: "the exact Turn is durably failed",
129
+ reason: "the exact current Turn is durably failed",
155
130
  evidence: ["turn-terminal-failure"]
156
131
  });
157
132
  }
158
- if (run.status !== "active") {
133
+ if (turn.status === "completed") {
159
134
  return projection(lane, {
160
135
  runtimeHealth: "suspected-stalled",
161
136
  recovery: "inspect",
162
137
  resultReusable: false,
163
- reason: "the Lane is running but its exact Turn is terminal without a Lane result",
138
+ reason: "the current Turn completed without settling its logical Lane",
164
139
  evidence: ["execution-lineage-inconsistent"]
165
140
  });
166
141
  }
167
- const session = input.sessions.find((candidate) => (candidate.roleName === run.roleName
168
- && candidate.agentId === run.effective.agentId
169
- && candidate.adapterId === run.effective.adapterId));
170
- const observations = exactTurnObservations(input.events, run, session);
171
- const runtime = runtimeProjection(observations, input.events, run);
142
+ const session = input.sessions.find((candidate) => (candidate.roleName === turn.roleName
143
+ && candidate.agentId === turn.effective.agentId
144
+ && candidate.adapterId === turn.effective.adapterId));
145
+ const observations = exactTurnObservations(input.events, turn, session);
146
+ const runtime = runtimeProjection(observations, input.events, turn);
172
147
  const unsettledContinuation = runtime !== null
173
148
  && Object.values(runtime.continuations).some((continuation) => (continuation.execution === "active"
174
149
  || continuation.execution === "unknown"
@@ -203,9 +178,8 @@ function projectExecutionLaneHealth(lane, input, policy) {
203
178
  evidence: runtimeTerminalEvidence
204
179
  });
205
180
  }
206
- const exit = latestExactProcessExit(input.events, run, session);
207
- const sessionDead = session?.status === "ended";
208
- if (sessionDead
181
+ const exit = latestExactProcessExit(input.events, turn, session);
182
+ if (session?.status === "ended"
209
183
  && exit !== null
210
184
  && isAbnormalExit(exit.classification)
211
185
  && !unsettledChildWork) {
@@ -217,20 +191,20 @@ function projectExecutionLaneHealth(lane, input, policy) {
217
191
  evidence: ["native-session-terminal", `process-exit:${exit.classification}`]
218
192
  });
219
193
  }
220
- if (isRoleTurnStalled(input.events, run.id)) {
194
+ if (isRoleTurnStalled(input.events, turn.id)) {
221
195
  return projection(lane, {
222
196
  runtimeHealth: "suspected-stalled",
223
197
  recovery: "inspect",
224
198
  resultReusable: false,
225
- reason: `the durable progress clock has not advanced since ${latestStallProgressAt(input.events, run.id) ?? run.updatedAt}; no death proof exists`,
199
+ reason: `the durable progress clock has not advanced since ${latestStallProgressAt(input.events, turn.id) ?? turn.updatedAt}; no death proof exists`,
226
200
  evidence: ["turn-stalled"]
227
201
  });
228
202
  }
229
203
  const activeOperation = runtime !== null
230
204
  && (Object.keys(runtime.operations).length > 0 || unsettledContinuation);
231
205
  const lastActivityAt = runtime?.lastRuntimeActivityAt
232
- ?? run.updatedAt
233
- ?? run.createdAt;
206
+ ?? turn.updatedAt
207
+ ?? turn.createdAt;
234
208
  const recentActivity = input.now.getTime() - Date.parse(lastActivityAt) < policy.quietAfterMs;
235
209
  if (activeOperation || recentActivity) {
236
210
  return projection(lane, {
@@ -244,7 +218,9 @@ function projectExecutionLaneHealth(lane, input, policy) {
244
218
  : "the exact Turn has recent structured runtime activity",
245
219
  evidence: unsettledContinuation
246
220
  ? ["runtime-continuation-unsettled"]
247
- : activeOperation ? ["runtime-operation-active"] : ["runtime-activity-recent"]
221
+ : activeOperation
222
+ ? ["runtime-operation-active"]
223
+ : ["runtime-activity-recent"]
248
224
  });
249
225
  }
250
226
  return projection(lane, {
@@ -255,43 +231,42 @@ function projectExecutionLaneHealth(lane, input, policy) {
255
231
  evidence: ["turn-active"]
256
232
  });
257
233
  }
258
- function projection(lane, value) {
259
- return Object.freeze({ laneId: lane.id, ...value });
260
- }
261
- function exactLaneTurn(run, group, lane) {
262
- return run.id === lane.turnId
263
- && run.taskId === group.taskId
264
- && run.roleName === lane.roleName
265
- && run.executionGroupId === group.id
266
- && run.executionLaneId === lane.id;
234
+ function exactLaneTurn(input, lane) {
235
+ return lane.currentTurnId === undefined
236
+ ? undefined
237
+ : input.turns.find((turn) => (turn.taskId === input.group.taskId
238
+ && turn.id === lane.currentTurnId
239
+ && turn.executionGroupId === input.group.id
240
+ && turn.executionLaneId === lane.id
241
+ && turn.roleName === lane.roleName));
267
242
  }
268
- function exactTurnObservations(events, run, session) {
243
+ function exactTurnObservations(events, turn, session) {
269
244
  return events.map(runtimeObservationFromTaskEvent)
270
245
  .filter((observation) => (observation !== null
271
- && observation.fence.taskId === run.taskId
272
- && observation.fence.turnId === run.id
273
- && observation.fence.roleName === run.roleName
274
- && observation.fence.agentId === run.effective.agentId
275
- && (session?.launchId === undefined || observation.fence.launchId === session.launchId)
246
+ && observation.fence.taskId === turn.taskId
247
+ && observation.fence.turnId === turn.id
248
+ && observation.fence.roleName === turn.roleName
249
+ && observation.fence.agentId === turn.effective.agentId
250
+ && (session?.runtimeGenerationId === undefined || observation.fence.runtimeGenerationId === session.runtimeGenerationId)
276
251
  && (session?.nativeSessionId === undefined
277
252
  || observation.fence.nativeSessionId === session.nativeSessionId)));
278
253
  }
279
- function runtimeProjection(observations, events, run) {
254
+ function runtimeProjection(observations, events, turn) {
280
255
  const first = observations[0];
281
256
  return first === undefined
282
257
  ? null
283
- : projectRuntimeTaskEvents(first.fence, run.createdAt, events);
258
+ : projectRuntimeTaskEvents(first.fence, turn.createdAt, events);
284
259
  }
285
- function latestExactProcessExit(events, run, session) {
260
+ function latestExactProcessExit(events, turn, session) {
286
261
  const matching = events.flatMap((event) => {
287
262
  if (event.type !== "runtime.process-exit-observed")
288
263
  return [];
289
264
  try {
290
265
  const observation = validateRuntimeProcessExitObservation(JSON.parse(event.payload.observation ?? ""));
291
- if (observation.taskId !== run.taskId
292
- || observation.turnId !== run.id
293
- || observation.roleName !== run.roleName
294
- || (session?.launchId !== undefined && observation.launchId !== session.launchId)
266
+ if (observation.taskId !== turn.taskId
267
+ || observation.turnId !== turn.id
268
+ || observation.roleName !== turn.roleName
269
+ || (session?.runtimeGenerationId !== undefined && observation.runtimeGenerationId !== session.runtimeGenerationId)
295
270
  || (session?.nativeSessionId !== undefined
296
271
  && observation.nativeSessionId !== session.nativeSessionId))
297
272
  return [];
@@ -309,6 +284,24 @@ function latestExactProcessExit(events, run, session) {
309
284
  function isAbnormalExit(classification) {
310
285
  return classification === "host-abnormal" || classification === "provider-turn-failed";
311
286
  }
287
+ function projectedStatus(lane, turn) {
288
+ if (lane.disposition === "succeeded")
289
+ return "succeeded";
290
+ if (lane.disposition === "failed")
291
+ return "failed";
292
+ if (lane.currentTurnId === undefined)
293
+ return "pending";
294
+ if (turn === undefined)
295
+ return "unknown";
296
+ if (turn.status === "active")
297
+ return "running";
298
+ if (turn.status === "failed")
299
+ return "needs-attention";
300
+ return "unknown";
301
+ }
302
+ function projection(lane, fields) {
303
+ return Object.freeze({ laneId: lane.id, ...fields });
304
+ }
312
305
  function countHealth(lanes, health) {
313
306
  return lanes.filter(({ runtimeHealth }) => runtimeHealth === health).length;
314
307
  }
@@ -0,0 +1,161 @@
1
+ import { enqueueRoleTurnDispatch } from "../coordination/workMailboxQueue.js";
2
+ import { createTurnInput } from "../context/turnInputContract.js";
3
+ import { contextSnapshotDeltaRefIds, freezeTurnContextSnapshot } from "../context/turnContextPack.js";
4
+ import { contextSnapshotRef } from "../context/contextSnapshot.js";
5
+ import { roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
6
+ import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
7
+ import { createTaskEvent } from "../event/taskEvent.js";
8
+ import { finishReviewRound, startReviewRound } from "../review/reviewRound.js";
9
+ import { createTurn } from "../turn/turn.js";
10
+ import { executionGroupSettled, MINIMUM_SYNTHESIS_RESULTS } from "./workItemExecution.js";
11
+ export function successfulReviewSynthesisProducers(store, round, group) {
12
+ if (!executionGroupSettled(group)) {
13
+ throw new Error(`Review ExecutionGroup is not settled: ${round.id}/${group.id}.`);
14
+ }
15
+ return [...group.lanes]
16
+ .sort((left, right) => left.ordinal - right.ordinal || left.id.localeCompare(right.id))
17
+ .flatMap((lane) => {
18
+ if (lane.disposition !== "succeeded")
19
+ return [];
20
+ const turn = lane.successfulTurnId === undefined
21
+ ? null
22
+ : store.getTurn(round.taskId, lane.successfulTurnId);
23
+ if (turn === null
24
+ || turn.status !== "completed"
25
+ || turn.purpose !== "review"
26
+ || turn.reviewRoundId !== round.id
27
+ || turn.executionGroupId !== group.id
28
+ || turn.executionLaneId !== lane.id
29
+ || turn.result === undefined) {
30
+ throw new Error(`Successful Review ExecutionLane has no exact Producer result: `
31
+ + `${group.id}/${lane.id}.`);
32
+ }
33
+ return [{
34
+ laneId: lane.id,
35
+ roleName: lane.roleName,
36
+ turnId: turn.id
37
+ }];
38
+ });
39
+ }
40
+ /**
41
+ * Projects a settled replicated Review into exactly one initial main Reviewer
42
+ * synthesis Turn. Explicit Turn retry may create later main attempts with the
43
+ * same source Group; successful producer Lanes remain immutable.
44
+ */
45
+ export function reconcileReviewMainTurns(store, taskId, now) {
46
+ const task = store.getTask(taskId);
47
+ if (task === null || task.status !== "active") {
48
+ return { createdTurns: [], failedReviewRoundIds: [] };
49
+ }
50
+ const createdTurns = [];
51
+ const failedReviewRoundIds = [];
52
+ const rounds = [...store.listReviewRounds(taskId)]
53
+ .sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id));
54
+ for (const round of rounds) {
55
+ const group = round.executionGroup;
56
+ if (round.status !== "running"
57
+ || group === undefined
58
+ || round.reviewerTurnId !== undefined
59
+ || !executionGroupSettled(group))
60
+ continue;
61
+ const producers = successfulReviewSynthesisProducers(store, round, group);
62
+ if (producers.length < MINIMUM_SYNTHESIS_RESULTS) {
63
+ const summary = `Review ExecutionGroup ${group.id} settled with ${producers.length} `
64
+ + `successful Producer result${producers.length === 1 ? "" : "s"}; at least `
65
+ + `${MINIMUM_SYNTHESIS_RESULTS} are required.`;
66
+ store.saveReviewRound(taskId, finishReviewRound(round, "failed", now, { kind: "quorum", message: summary }));
67
+ store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, "review.execution-group-failed", {
68
+ reviewRoundId: round.id,
69
+ executionGroupId: group.id,
70
+ successfulProducerCount: String(producers.length),
71
+ requiredProducerCount: String(MINIMUM_SYNTHESIS_RESULTS)
72
+ }, now));
73
+ failedReviewRoundIds.push(round.id);
74
+ continue;
75
+ }
76
+ const existing = store.listTurns(taskId).filter((turn) => (turn.purpose === "review"
77
+ && turn.reviewRoundId === round.id
78
+ && turn.sourceExecutionGroupId === group.id));
79
+ if (existing.some(({ status }) => status !== "failed"))
80
+ continue;
81
+ const role = store.getRole(taskId, round.reviewerRoleName);
82
+ if (role === null) {
83
+ throw new Error(`Review main Role is missing: ${taskId}/${round.reviewerRoleName}.`);
84
+ }
85
+ if (store.getActiveTurn(taskId, role.name) !== null)
86
+ continue;
87
+ const workspace = store.getReviewRoundWorkspace(taskId, round.id);
88
+ if (workspace === null) {
89
+ throw new Error(`Review main workspace is missing: ${taskId}/${round.id}.`);
90
+ }
91
+ const effective = resolveEffectiveLaunch({
92
+ role,
93
+ purpose: "review",
94
+ workspace,
95
+ reviewRoundId: round.id,
96
+ reviewBaseCommit: round.reviewBaseCommit
97
+ });
98
+ const snapshot = freezeTurnContextSnapshot(store, {
99
+ taskId,
100
+ roleName: role.name,
101
+ purpose: "review",
102
+ ...(round.workItemId === undefined ? {} : { workItemId: round.workItemId }),
103
+ reviewRoundId: round.id,
104
+ sourceExecutionGroupId: group.id
105
+ }, now, "controller", group.assignment.contextSnapshotRef);
106
+ const turn = createTurn(store.nextTurnId(taskId), taskId, role.name, roleAgentSessionResumeMode(store.getTaskRoleSessionSet(taskId, role.name), effective.agentId, effective), createTurnInput({
107
+ source: {
108
+ type: "yui",
109
+ channel: round.workItemId === undefined ? "task-dispatch" : "workitem-dispatch"
110
+ },
111
+ directive: synthesisDirective(group, producers),
112
+ contextSnapshotRef: contextSnapshotRef(snapshot),
113
+ deltaRefIds: contextSnapshotDeltaRefIds(store, snapshot)
114
+ }), now, {
115
+ ...(round.workItemId === undefined ? {} : { workItemId: round.workItemId }),
116
+ purpose: "review",
117
+ reviewRoundId: round.id,
118
+ sourceExecutionGroupId: group.id,
119
+ workspace,
120
+ effective
121
+ });
122
+ store.saveTurn(turn);
123
+ store.saveReviewRound(taskId, startReviewRound(round, turn.id));
124
+ store.saveActiveTurn(turn);
125
+ enqueueRoleTurnDispatch(store, {
126
+ taskId,
127
+ roleName: role.name,
128
+ turnId: turn.id,
129
+ reason: "review-synthesis-ready",
130
+ occurredAt: now
131
+ });
132
+ store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, "turn.review-dispatched", {
133
+ turnId: turn.id,
134
+ role: turn.roleName,
135
+ purpose: turn.purpose,
136
+ mode: turn.mode,
137
+ agent: `${turn.effective.agentId}/${turn.effective.adapterId}`,
138
+ effectiveRevision: String(turn.effective.sourceDesiredRevision),
139
+ profileAccess: turn.effective.profileAccess,
140
+ effectivePermission: turn.effective.permission.strategy,
141
+ writeProjectIds: turn.effective.writeProjectIds.join(",") || "none",
142
+ reviewRoundId: round.id,
143
+ sourceExecutionGroupId: group.id
144
+ }, now));
145
+ createdTurns.push(turn);
146
+ }
147
+ return { createdTurns, failedReviewRoundIds };
148
+ }
149
+ function synthesisDirective(group, producers) {
150
+ return [
151
+ "Act as the main Reviewer over every frozen successful Producer result in stable Lane order.",
152
+ "Expand each exact source Turn from the frozen Context Snapshot and consume its original result text plus Core-authored system evidence.",
153
+ "Resolve disagreements through review judgment against the frozen candidate. Do not rerun, retry, append, select, or abandon Lanes.",
154
+ "Return one complete original review result for the Leader; Yui Core does not parse or validate its semantic structure.",
155
+ JSON.stringify({
156
+ schemaVersion: 1,
157
+ sourceExecutionGroupId: group.id,
158
+ producers
159
+ }, null, 2)
160
+ ].join("\n\n");
161
+ }