@zq-silk/yui 0.14.1 → 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 (177) hide show
  1. package/ARCHITECTURE.md +64 -35
  2. package/README.md +254 -124
  3. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  4. package/dist/cli/commandCatalog.js +74 -109
  5. package/dist/cli/helpRenderer.js +1 -2
  6. package/dist/cli/interactionCandidates.js +10 -0
  7. package/dist/cli/interactionPolicy.js +76 -37
  8. package/dist/cli/roleWizard.js +5 -6
  9. package/dist/cli/updateOrchestrator.js +10 -8
  10. package/dist/cli/updatePorts.js +17 -13
  11. package/dist/cli/upgradeCommand.js +2 -5
  12. package/dist/cli.js +268 -231
  13. package/dist/commands/agentCommands.js +13 -0
  14. package/dist/commands/configCommands.js +7 -19
  15. package/dist/commands/configOverview.js +3 -2
  16. package/dist/commands/durableJobCommands.js +1 -1
  17. package/dist/commands/executionAuditCommands.js +4 -9
  18. package/dist/commands/globalRoleCommands.js +17 -0
  19. package/dist/commands/profileCommands.js +200 -30
  20. package/dist/commands/sessionCommands.js +1 -1
  21. package/dist/commands/taskActor.js +29 -127
  22. package/dist/commands/taskChangeSetCommands.js +1 -3
  23. package/dist/commands/taskCommands.js +1771 -2107
  24. package/dist/commands/taskCompletionGate.js +27 -103
  25. package/dist/commands/taskContextCommand.js +47 -80
  26. package/dist/commands/taskExecutionCommands.js +9 -27
  27. package/dist/commands/taskInputCommands.js +10 -10
  28. package/dist/commands/taskIntegrationCommands.js +69 -61
  29. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  30. package/dist/commands/taskNextActionCommand.js +8 -29
  31. package/dist/commands/taskOverviewCommand.js +25 -3
  32. package/dist/commands/taskPublicationCommands.js +151 -85
  33. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  34. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  35. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  36. package/dist/commands/taskUpstreamCommands.js +79 -95
  37. package/dist/config/configCatalog.js +1 -1
  38. package/dist/context/contextSnapshot.js +17 -2
  39. package/dist/context/sourceTurnContext.js +30 -0
  40. package/dist/context/turnContextPack.js +107 -48
  41. package/dist/context/turnInputContract.js +11 -4
  42. package/dist/context/wakeNotification.js +84 -22
  43. package/dist/controller/agentRuntimeObserver.js +9 -12
  44. package/dist/controller/clientRuntime.js +13 -13
  45. package/dist/controller/controller.js +11 -11
  46. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  47. package/dist/controller/jobControl.js +11 -10
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +2 -2
  50. package/dist/controller/runtime.js +18 -61
  51. package/dist/controller/runtimeEventInbox.js +57 -34
  52. package/dist/controller/runtimeEventProcessor.js +36 -96
  53. package/dist/controller/runtimeHookTurnFence.js +33 -57
  54. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  55. package/dist/controller/runtimeObservationHook.js +18 -12
  56. package/dist/controller/sessionNotify.js +43 -28
  57. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  58. package/dist/controller/structuredProviderObservation.js +13 -16
  59. package/dist/coordination/workMailboxQueue.js +132 -0
  60. package/dist/core/controllerServer.js +1 -0
  61. package/dist/domain/agentResultTransport.js +60 -0
  62. package/dist/execution/executionHealth.js +111 -118
  63. package/dist/execution/reviewMainTurn.js +161 -0
  64. package/dist/execution/workItemExecution.js +454 -0
  65. package/dist/execution/workItemExecutionProjection.js +309 -0
  66. package/dist/execution/workItemMainTurn.js +164 -0
  67. package/dist/executor/agentExecutor.js +19 -19
  68. package/dist/executor/executorRegistry.js +7 -28
  69. package/dist/executor/fileRoleLaunchPlanner.js +16 -82
  70. package/dist/external/githubPublicationVerifier.js +114 -0
  71. package/dist/external/gitlabPublicationVerifier.js +158 -0
  72. package/dist/external/pinnedCommandRunner.js +73 -0
  73. package/dist/integration/changeSetManifest.js +0 -3
  74. package/dist/integration/deliveryObligation.js +43 -6
  75. package/dist/integration/gitIntegrationService.js +284 -101
  76. package/dist/integration/integrationAttempt.js +64 -14
  77. package/dist/integration/integrationQueueEntry.js +5 -35
  78. package/dist/integration/integrationQueueService.js +65 -194
  79. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  80. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  81. package/dist/message/message.js +16 -0
  82. package/dist/observability/executionAudit.js +4 -25
  83. package/dist/observability/faultClassification.js +58 -70
  84. package/dist/observability/orchestrationMetrics.js +8 -50
  85. package/dist/profile/agentProfile.js +60 -15
  86. package/dist/profile/agentProfileRuntime.js +100 -0
  87. package/dist/release/releaseWorkflowPorts.js +5 -68
  88. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  89. package/dist/repository/gitWorkspace.js +9 -67
  90. package/dist/repository/taskBaseFreshness.js +3 -1
  91. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  92. package/dist/repository/taskWorkspacePreparer.js +252 -411
  93. package/dist/resources/liveReferences.js +6 -55
  94. package/dist/resources/resourceDiscovery.js +1 -30
  95. package/dist/resources/resourceRegistrar.js +0 -23
  96. package/dist/review/deltaRecheck.js +8 -65
  97. package/dist/review/reviewAcceptance.js +22 -19
  98. package/dist/review/reviewConfig.js +0 -10
  99. package/dist/review/reviewDecision.js +2 -2
  100. package/dist/review/reviewRound.js +147 -273
  101. package/dist/review/reviewerAvailability.js +13 -23
  102. package/dist/role/role.js +3 -0
  103. package/dist/runtime/agentDriverObservation.js +1 -1
  104. package/dist/runtime/agentHost.js +51 -51
  105. package/dist/runtime/builtinAgentDrivers.js +9 -2
  106. package/dist/runtime/codexAppServerRuntime.js +23 -5
  107. package/dist/runtime/exactControlPlane.js +2 -267
  108. package/dist/runtime/firstProgressAdvisory.js +1 -1
  109. package/dist/runtime/index.js +1 -1
  110. package/dist/runtime/launchBroker.js +11 -11
  111. package/dist/runtime/lifecycleReservation.js +2 -2
  112. package/dist/runtime/managedCaller.js +111 -0
  113. package/dist/runtime/ports.js +16 -3
  114. package/dist/runtime/preallocatedNativeSession.js +2 -2
  115. package/dist/runtime/processExitObservation.js +3 -2
  116. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  117. package/dist/runtime/runtimeBinding.js +1 -1
  118. package/dist/runtime/runtimeObservation.js +33 -16
  119. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  120. package/dist/runtime/runtimeStopReceipt.js +11 -11
  121. package/dist/runtime/sessionLaunchRequest.js +2 -2
  122. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  123. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  124. package/dist/runtime/sessionReconciliation.js +4 -4
  125. package/dist/runtime/sessionTerminationGuard.js +9 -9
  126. package/dist/runtime/sessionTokenMetrics.js +5 -7
  127. package/dist/runtime/structuredProviderHost.js +5 -5
  128. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  129. package/dist/runtime/tmuxAdapters.js +32 -30
  130. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  131. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  132. package/dist/scheduler/roleTurnLiveness.js +6 -6
  133. package/dist/scheduler/roleTurnStall.js +8 -9
  134. package/dist/scheduler/taskExecutionProjection.js +19 -41
  135. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  136. package/dist/storage/sqliteSchema.js +1 -21
  137. package/dist/storage/sqliteStore.js +62 -47
  138. package/dist/storage/storageSchema.js +1 -8
  139. package/dist/storage/storageVersions.js +7 -4
  140. package/dist/storage/taskStore.js +8 -8
  141. package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
  142. package/dist/task/completionReadiness.js +17 -98
  143. package/dist/task/deliveryGuard.js +9 -11
  144. package/dist/task/draftPlan.js +142 -0
  145. package/dist/task/nextAction.js +179 -477
  146. package/dist/task/publicationVerification.js +1 -0
  147. package/dist/task/remoteDelivery.js +233 -0
  148. package/dist/task/task.js +108 -4
  149. package/dist/task/taskRecordReference.js +0 -1
  150. package/dist/turn/turn.js +109 -16
  151. package/dist/verification/verificationGateService.js +0 -26
  152. package/dist/web/assets/client/components.js +99 -121
  153. package/dist/web/assets/client/i18n.js +136 -58
  154. package/dist/web/assets/client/view.js +44 -0
  155. package/dist/web/assets/styles/cards.js +3 -15
  156. package/dist/web/assets/styles/widgets.js +4 -0
  157. package/dist/web/webSnapshot.js +29 -14
  158. package/dist/workItem/dependencyGate.js +34 -0
  159. package/dist/workItem/workItem.js +99 -266
  160. package/dist/workspace/workItemChangeSetManager.js +15 -59
  161. package/docs/task-local-identity.md +1 -1
  162. package/i18n/README.zh-CN.md +164 -58
  163. package/package.json +1 -1
  164. package/skills/yui-leader/SKILL.md +272 -782
  165. package/skills/yui-operator/SKILL.md +190 -365
  166. package/skills/yui-reviewer/SKILL.md +40 -33
  167. package/skills/yui-runtime/SKILL.md +22 -0
  168. package/skills/yui-worker/SKILL.md +19 -6
  169. package/dist/execution/candidateConvergence.js +0 -623
  170. package/dist/execution/executionGroup.js +0 -854
  171. package/dist/execution/resourceBroker.js +0 -426
  172. package/dist/executor/launchPlan.js +0 -16
  173. package/dist/review/reviewFinding.js +0 -240
  174. package/dist/review/reviewFindingLedger.js +0 -550
  175. package/dist/review/reviewOutcomeClassifier.js +0 -255
  176. package/dist/scheduler/resourceQueueProjection.js +0 -71
  177. package/dist/task/repairWave.js +0 -150
@@ -18,7 +18,7 @@ const TURN_SCOPED_PHASES = new Set([
18
18
  /**
19
19
  * Phases that assert durable provider truth about a Turn. Every one must carry
20
20
  * the complete turn-scoped identity — turnId AND the generation fences
21
- * (nativeSessionId + launchId) AND, where the transport owns it, the receiptId —
21
+ * (nativeSessionId + runtimeGenerationId) AND, where the transport owns it, the receiptId —
22
22
  * because a missing or wrong fence must fail closed rather than advance a Turn or
23
23
  * its successor.
24
24
  */
@@ -86,8 +86,8 @@ export function createCanonicalLifecycleEvent(input) {
86
86
  if (fence.nativeSessionId === undefined) {
87
87
  throw new CanonicalLifecycleError(`Phase ${input.phase} requires a nativeSessionId fence.`);
88
88
  }
89
- if (fence.launchId === undefined) {
90
- throw new CanonicalLifecycleError(`Phase ${input.phase} requires a launchId fence.`);
89
+ if (fence.runtimeGenerationId === undefined) {
90
+ throw new CanonicalLifecycleError(`Phase ${input.phase} requires a runtimeGenerationId fence.`);
91
91
  }
92
92
  }
93
93
  if (input.phase === "prompt-pushed" && fence.receiptId === undefined) {
@@ -156,8 +156,8 @@ export function foldCanonicalLifecycleEvent(event, expectation) {
156
156
  // exact launch fence and only when none is bound yet.
157
157
  if (expectation.boundNativeSessionId === undefined
158
158
  && event.fence.nativeSessionId !== undefined
159
- && event.fence.launchId !== undefined
160
- && event.fence.launchId === expectation.fence.launchId) {
159
+ && event.fence.runtimeGenerationId !== undefined
160
+ && event.fence.runtimeGenerationId === expectation.fence.runtimeGenerationId) {
161
161
  return { outcome: "bind-native-session", nativeSessionId: event.fence.nativeSessionId };
162
162
  }
163
163
  return { outcome: "mark-session-started" };
@@ -222,7 +222,7 @@ export function foldCanonicalLifecycleEvent(event, expectation) {
222
222
  * Returns null when the event belongs to the expected generation/Turn, or a
223
223
  * mismatch reason otherwise. Owner/adapter and turn-scoped turnId are always
224
224
  * compared. Provider-truth phases (acceptance/progress/terminal) require the
225
- * COMPLETE generation fence — nativeSessionId, launchId, and (for acceptance)
225
+ * COMPLETE generation fence — nativeSessionId, runtimeGenerationId, and (for acceptance)
226
226
  * receiptId must be present and equal, so a missing or wrong generation/receipt
227
227
  * fails closed rather than advancing a Turn or its successor. Where a native id
228
228
  * is already bound, every turn-scoped fact must match it.
@@ -256,10 +256,11 @@ function matchesFence(expectation, event) {
256
256
  if (PROVIDER_TRUTH_PHASES.has(phase)) {
257
257
  if (actual.nativeSessionId === undefined)
258
258
  return "fence-mismatch:missing-native-session";
259
- if (actual.launchId === undefined)
260
- return "fence-mismatch:missing-launch";
261
- if (expected.launchId !== undefined && actual.launchId !== expected.launchId) {
262
- return "fence-mismatch:launchId";
259
+ if (actual.runtimeGenerationId === undefined) {
260
+ return "fence-mismatch:missing-runtime-generation";
261
+ }
262
+ if (expected.runtimeGenerationId !== undefined && actual.runtimeGenerationId !== expected.runtimeGenerationId) {
263
+ return "fence-mismatch:runtimeGenerationId";
263
264
  }
264
265
  // Once a native id is bound, every provider-truth fact must match it. Before
265
266
  // binding (discovery still open) the expected fence's own nativeSessionId, if
@@ -290,10 +291,10 @@ function matchesFence(expectation, event) {
290
291
  && actual.nativeSessionId !== expected.nativeSessionId) {
291
292
  return "fence-mismatch:nativeSessionId";
292
293
  }
293
- if (actual.launchId !== undefined
294
- && expected.launchId !== undefined
295
- && actual.launchId !== expected.launchId) {
296
- return "fence-mismatch:launchId";
294
+ if (actual.runtimeGenerationId !== undefined
295
+ && expected.runtimeGenerationId !== undefined
296
+ && actual.runtimeGenerationId !== expected.runtimeGenerationId) {
297
+ return "fence-mismatch:runtimeGenerationId";
297
298
  }
298
299
  if (phase === "prompt-pushed"
299
300
  && actual.receiptId !== undefined
@@ -319,7 +320,7 @@ function normalizeFence(fence) {
319
320
  ...(fence.nativeSessionId === undefined
320
321
  ? {}
321
322
  : { nativeSessionId: requireFenceText(fence.nativeSessionId, "nativeSessionId") }),
322
- ...(fence.launchId === undefined ? {} : { launchId: requireFenceText(fence.launchId, "launchId") }),
323
+ ...(fence.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: requireFenceText(fence.runtimeGenerationId, "runtimeGenerationId") }),
323
324
  ...(fence.receiptId === undefined ? {} : { receiptId: requireFenceText(fence.receiptId, "receiptId") })
324
325
  });
325
326
  }
@@ -1,9 +1,12 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
2
  import { completeProcessing } from "../coordination/workMailbox.js";
3
+ import { captureRoleTurnDispatch, settleRoleTurnDispatch } from "../coordination/workMailboxQueue.js";
3
4
  import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewRound.js";
4
- import { reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
5
5
  import { completeTurn, failTurn } from "../turn/turn.js";
6
- import { recordExecutionLaneResult, resolveExecutionGroup } from "../execution/executionGroup.js";
6
+ import { boundedTurnFailureDiagnostic } from "../domain/agentResultTransport.js";
7
+ import { updateExecutionLane, updateWorkItemExecutionLane } from "../execution/workItemExecution.js";
8
+ import { reconcileWorkItemMainTurns } from "../execution/workItemMainTurn.js";
9
+ import { reconcileReviewMainTurns } from "../execution/reviewMainTurn.js";
7
10
  import { isRuntimeLaunchReservation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
8
11
  import { managedProviderTurnId } from "../runtime/providerRuntimeIdentity.js";
9
12
  import { latestTurnDurableProgressAt } from "../scheduler/roleTurnStall.js";
@@ -30,7 +33,8 @@ export function validateExactTurnReviewRound(store, turn, options = {}) {
30
33
  return { disposition: "obsolete", round, reason: "review-round-terminal" };
31
34
  }
32
35
  const lane = round.executionGroup?.lanes.find(({ id }) => id === turn.executionLaneId);
33
- const exactReviewerTurn = round.reviewerTurnId === turn.id || lane?.turnId === turn.id;
36
+ const exactReviewerTurn = round.reviewerTurnId === turn.id
37
+ || lane?.currentTurnId === turn.id;
34
38
  const exactReviewerRole = round.reviewerRoleName === turn.roleName || lane?.roleName === turn.roleName;
35
39
  if (!exactReviewerTurn
36
40
  || !exactReviewerRole
@@ -69,7 +73,7 @@ export function validateExactTurnReviewRound(store, turn, options = {}) {
69
73
  }
70
74
  if (turn.workspace.owner.type === "execution-lane") {
71
75
  if (lane === undefined
72
- || lane.turnId !== turn.id
76
+ || lane.currentTurnId !== turn.id
73
77
  || lane.roleName !== turn.roleName
74
78
  || lane.workspace?.root !== turn.workspace.root
75
79
  || lane.workspace.writableProjectIds.length !== turn.workspace.entries.length
@@ -138,66 +142,16 @@ export function terminalizeExactTurnReviewRound(store, input, now) {
138
142
  return validation;
139
143
  }
140
144
  const reviewRound = validation.round;
141
- let groupedRound = input.turn.executionGroupId !== undefined
142
- && input.turn.executionLaneId !== undefined
143
- && reviewRound.executionGroup !== undefined
144
- ? updateReviewExecutionGroup(reviewRound, recordExecutionLaneResult(reviewRound.executionGroup, input.turn.executionLaneId, {
145
- summary: input.outcome.summary,
146
- ...(input.reviewResult?.report === undefined ? {} : { report: input.reviewResult.report }),
147
- ...(input.reviewResult?.checks === undefined
148
- ? {}
149
- : {
150
- checks: input.reviewResult.checks.map(({ name, outcome, details }) => ({
151
- name,
152
- outcome,
153
- ...(details === undefined ? {} : { details })
154
- }))
155
- }),
156
- ...(input.reviewResult?.findings === undefined
157
- ? {}
158
- : { findings: input.reviewResult.findings }),
159
- ...(input.reviewResult?.evidence === undefined
160
- ? {}
161
- : { evidence: input.reviewResult.evidence }),
162
- ...(input.reviewResult?.evidenceCommit === undefined
163
- ? {}
164
- : { evidenceCommit: input.reviewResult.evidenceCommit }),
165
- ...(input.reviewResult?.gitSnapshot === undefined
166
- ? {}
167
- : { gitSnapshot: input.reviewResult.gitSnapshot })
168
- }, input.outcome.status, now))
169
- : reviewRound;
170
- if (input.settleFailedExecutionGroup === true
171
- && input.outcome.status === "failed"
172
- && groupedRound.executionGroup !== undefined
173
- && groupedRound.executionGroup.resolution === undefined
174
- && groupedRound.executionGroup.lanes.every((lane) => (lane.status === "completed"
175
- || lane.status === "failed"
176
- || lane.status === "skipped"))) {
177
- groupedRound = updateReviewExecutionGroup(groupedRound, resolveExecutionGroup(groupedRound.executionGroup, {
178
- decision: "blocked",
179
- summary: input.outcome.summary
180
- }, now));
181
- }
182
- const groupedMultiLane = groupedRound.executionGroup !== undefined
183
- && (groupedRound.executionGroup.lanes.length > 1
184
- || groupedRound.executionGroup.strategy.mode === "adaptive");
185
- if (groupedMultiLane
186
- && groupedRound.executionGroup !== undefined
187
- && groupedRound.executionGroup.resolution === undefined) {
188
- // A panel Lane only contributes evidence. The Leader must see every
189
- // terminal Lane and explicitly resolve the Group before this ReviewRound
190
- // can become terminal.
191
- store.saveReviewRound(input.taskId, groupedRound);
192
- return { disposition: "applied", round: groupedRound };
193
- }
194
- const terminal = finishReviewRound(groupedRound, input.outcome.status, input.outcome.summary, now, input.reviewResult);
145
+ if (input.turn.executionGroupId !== undefined
146
+ && input.turn.executionLaneId !== undefined) {
147
+ // Producer Turns own only their immutable Turn result. The unified Group
148
+ // is advanced after that result is validated and stored below.
149
+ return { disposition: "applied", round: reviewRound };
150
+ }
151
+ const terminal = finishReviewRound(reviewRound, input.outcome.status, now, input.outcome.status === "failed"
152
+ ? { kind: "execution", message: input.outcome.diagnostic }
153
+ : undefined);
195
154
  store.saveReviewRound(input.taskId, terminal);
196
- // Issue 06: a completed (semantic) Round feeds the finding ledger; a failed
197
- // execution attempt is skipped by the classifier and never creates findings.
198
- if (terminal.status === "completed") {
199
- reconcileReviewFindingsAfterReview(store, input.taskId, terminal.id, now);
200
- }
201
155
  return { disposition: "applied", round: terminal };
202
156
  }
203
157
  /**
@@ -248,9 +202,8 @@ export function retireExactActiveTurn(store, input, now) {
248
202
  agentId: input.agentId,
249
203
  turnId: input.turnId,
250
204
  ...(input.nativeSessionId === undefined ? {} : { nativeSessionId: input.nativeSessionId }),
251
- ...(input.launchId === undefined ? {} : { launchId: input.launchId }),
252
- settleFailedExecutionGroup: true,
253
- outcome: { status: "failed", summary: input.reason, failureReason: "missing-result" }
205
+ ...(input.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: input.runtimeGenerationId }),
206
+ outcome: { status: "failed", diagnostic: input.reason, failureReason: "missing-result" }
254
207
  };
255
208
  const session = sessions?.sessions[input.agentId];
256
209
  const providerBinding = sessions?.providerBinding;
@@ -310,24 +263,54 @@ export function terminalizeExactTaskTurn(store, input, now) {
310
263
  if (reviewValidation.disposition !== "applied") {
311
264
  return obsolete(turn, reviewValidation.reason ?? "review-round-mismatch");
312
265
  }
266
+ const requiredSnapshotProjects = [...new Set(turn.effective.writeProjectIds)].sort();
267
+ const observedSnapshotProjects = input.systemEvidence?.workspaceSnapshot?.projects
268
+ .map(({ projectId }) => projectId)
269
+ .sort();
270
+ const laneSnapshotRequired = input.outcome.status === "completed"
271
+ && turn.executionGroupId !== undefined
272
+ && turn.executionLaneId !== undefined
273
+ && requiredSnapshotProjects.length > 0;
274
+ const missingLaneSnapshot = laneSnapshotRequired
275
+ && (observedSnapshotProjects === undefined
276
+ || !isDeepStrictEqual(observedSnapshotProjects, requiredSnapshotProjects));
277
+ const workspaceFailure = laneSnapshotRequired
278
+ ? input.workspaceFailure ?? (missingLaneSnapshot
279
+ ? {
280
+ failureReason: "workspace-unavailable",
281
+ diagnostic: "Core could not freeze the exact clean writable Lane workspace."
282
+ }
283
+ : undefined)
284
+ : undefined;
285
+ const effectiveOutcome = input.outcome.status === "completed" && workspaceFailure !== undefined
286
+ ? {
287
+ status: "failed",
288
+ output: input.outcome.output,
289
+ diagnostic: workspaceFailure.diagnostic,
290
+ failureReason: workspaceFailure.failureReason,
291
+ ...(input.outcome.provider === undefined ? {} : { provider: input.outcome.provider })
292
+ }
293
+ : input.outcome;
294
+ const terminalOutcome = effectiveOutcome.status === "failed"
295
+ ? {
296
+ ...effectiveOutcome,
297
+ diagnostic: boundedTurnFailureDiagnostic(effectiveOutcome.diagnostic)
298
+ }
299
+ : effectiveOutcome;
313
300
  // All Turn, active-pointer, Session, launch, and mailbox fences have passed.
314
301
  // Only now may the exact ReviewRound be terminalized alongside the Turn so a
315
302
  // stale fence never leaves a Round written while the Turn stays active.
316
303
  const reviewRoundTerminalization = terminalizeExactTurnReviewRound(store, {
317
304
  taskId: input.taskId,
318
305
  turn,
319
- outcome: input.outcome,
320
- reviewResult: input.reviewResult,
321
- ...(input.settleFailedExecutionGroup === undefined
322
- ? {}
323
- : { settleFailedExecutionGroup: input.settleFailedExecutionGroup })
306
+ outcome: terminalOutcome
324
307
  }, now);
325
308
  if (reviewRoundTerminalization.disposition !== "applied") {
326
309
  return obsolete(turn, reviewRoundTerminalization.reason ?? "review-round-mismatch");
327
310
  }
328
- const terminal = input.outcome.status === "completed"
329
- ? completeTurn(turn, input.outcome.summary, now, input.outcome.provider)
330
- : failTurn(turn, input.outcome.failureReason ?? "runtime-failed", input.outcome.summary, now, input.outcome.provider);
311
+ const terminal = terminalOutcome.status === "completed"
312
+ ? completeTurn(turn, terminalOutcome.output, now, terminalOutcome.provider, input.systemEvidence)
313
+ : failTurn(turn, terminalOutcome.failureReason, terminalOutcome.diagnostic, now, terminalOutcome.provider, terminalOutcome.output);
331
314
  if (turn.executionGroupId !== undefined
332
315
  && turn.executionLaneId !== undefined
333
316
  && turn.purpose === "execution"
@@ -337,30 +320,48 @@ export function terminalizeExactTaskTurn(store, input, now) {
337
320
  ? undefined
338
321
  : workItemExecutionGroupById(item, turn.executionGroupId);
339
322
  if (item !== null && group !== undefined) {
340
- let grouped = recordExecutionLaneResult(group, turn.executionLaneId, {
341
- summary: input.outcome.summary,
342
- ...(input.reviewResult?.report === undefined ? {} : { report: input.reviewResult.report }),
343
- ...(input.reviewResult?.checks === undefined ? {} : { checks: input.reviewResult.checks }),
344
- ...(input.reviewResult?.findings === undefined ? {} : { findings: input.reviewResult.findings }),
345
- ...(input.reviewResult?.evidence === undefined ? {} : { evidence: input.reviewResult.evidence }),
346
- ...(input.reviewResult?.evidenceCommit === undefined ? {} : { evidenceCommit: input.reviewResult.evidenceCommit }),
347
- ...(input.reviewResult?.gitSnapshot === undefined ? {} : { gitSnapshot: input.reviewResult.gitSnapshot })
348
- }, input.outcome.status, now);
349
- if (input.settleFailedExecutionGroup === true
350
- && input.outcome.status === "failed"
351
- && grouped.resolution === undefined
352
- && grouped.lanes.every((lane) => (lane.status === "completed"
353
- || lane.status === "failed"
354
- || lane.status === "skipped"))) {
355
- grouped = resolveExecutionGroup(grouped, {
356
- decision: "blocked",
357
- summary: input.outcome.summary
323
+ if (terminal.status === "completed") {
324
+ const grouped = updateWorkItemExecutionLane(group, turn.executionLaneId, {
325
+ currentTurnId: turn.id,
326
+ successfulTurnId: turn.id,
327
+ disposition: "succeeded"
328
+ }, now);
329
+ store.saveWorkItem(input.taskId, updateWorkItemExecutionGroup(item, grouped, now));
330
+ }
331
+ }
332
+ }
333
+ if (turn.executionGroupId !== undefined
334
+ && turn.executionLaneId !== undefined
335
+ && turn.purpose === "review"
336
+ && turn.reviewRoundId !== undefined) {
337
+ const round = store.getReviewRound(input.taskId, turn.reviewRoundId);
338
+ const group = round?.executionGroup;
339
+ if (round !== null
340
+ && round !== undefined
341
+ && group !== undefined
342
+ && group.id === turn.executionGroupId) {
343
+ if (terminal.status === "completed") {
344
+ const grouped = updateExecutionLane(group, turn.executionLaneId, {
345
+ currentTurnId: turn.id,
346
+ successfulTurnId: turn.id,
347
+ disposition: "succeeded"
358
348
  }, now);
349
+ store.saveReviewRound(input.taskId, updateReviewExecutionGroup(round, grouped));
359
350
  }
360
- store.saveWorkItem(input.taskId, updateWorkItemExecutionGroup(item, grouped, now));
361
351
  }
362
352
  }
363
353
  store.saveTurn(terminal);
354
+ const dispatchIdentity = {
355
+ taskId: terminal.taskId,
356
+ roleName: terminal.roleName,
357
+ turnId: terminal.id
358
+ };
359
+ const dispatchToken = captureRoleTurnDispatch(store.getWorkMailbox({
360
+ kind: "role",
361
+ taskId: terminal.taskId,
362
+ roleName: terminal.roleName
363
+ }), dispatchIdentity);
364
+ settleRoleTurnDispatch(store, dispatchIdentity, dispatchToken);
364
365
  if (terminal.roleName === "leader") {
365
366
  const wake = store.listTaskWakes(input.taskId)
366
367
  .find((candidate) => candidate.turnId === terminal.id && candidate.status === "dispatched");
@@ -375,6 +376,8 @@ export function terminalizeExactTaskTurn(store, input, now) {
375
376
  store.clearActiveTurn(input.taskId, input.roleName);
376
377
  }
377
378
  settleLaunchReservation(store, sessions, input);
379
+ reconcileWorkItemMainTurns(store, input.taskId, now);
380
+ reconcileReviewMainTurns(store, input.taskId, now);
378
381
  return { disposition: "applied", turn: terminal };
379
382
  }
380
383
  function settleLaunchReservation(store, sessions, input) {
@@ -386,10 +389,10 @@ function settleLaunchReservation(store, sessions, input) {
386
389
  const mailbox = store.getWorkMailbox(target);
387
390
  const reservation = mailbox?.processing;
388
391
  const session = sessions?.sessions[input.agentId];
389
- const launchId = input.nativeSessionId === undefined
390
- ? input.launchId ?? session?.launchId
391
- : session?.launchId;
392
- if (launchId === undefined || !isRuntimeLaunchReservation(reservation, launchId))
392
+ const runtimeGenerationId = input.nativeSessionId === undefined
393
+ ? input.runtimeGenerationId ?? session?.runtimeGenerationId
394
+ : session?.runtimeGenerationId;
395
+ if (runtimeGenerationId === undefined || !isRuntimeLaunchReservation(reservation, runtimeGenerationId))
393
396
  return;
394
397
  const settled = completeProcessing(mailbox, reservation.batchId);
395
398
  if (settled.processing === null && settled.pending === null) {
@@ -26,6 +26,22 @@ export function createTaskMessage(id, taskId, body, kind, author, now, context =
26
26
  export function taskMessageAuthorLabel(author) {
27
27
  return author.type === "role" ? author.roleName : author.type;
28
28
  }
29
+ /** Replace only the mutable content of a Draft user/operator Message. */
30
+ export function updateDraftTaskMessage(message, update) {
31
+ validateTaskMessage(message);
32
+ if (message.kind !== "user" && message.kind !== "operator") {
33
+ throw new Error(`Only user/operator Task Messages can be updated: ${message.id}.`);
34
+ }
35
+ const updated = {
36
+ ...message,
37
+ body: requireBody(update.body),
38
+ ...(update.wakePolicy === undefined
39
+ ? {}
40
+ : { wakePolicy: update.wakePolicy })
41
+ };
42
+ validateTaskMessage(updated);
43
+ return updated;
44
+ }
29
45
  export function validateTaskMessage(message) {
30
46
  if (message.schemaVersion !== 3)
31
47
  throw new Error("Task Message must use schemaVersion 3.");
@@ -2,7 +2,7 @@
2
2
  * Read-only execution audit aggregator (Issue 11 §3).
3
3
  *
4
4
  * The audit answers "what happened in this Home" from durable records alone:
5
- * Turns/failures/durations, wake reasons, Session generations, Review execution
5
+ * Turns/failures/durations, wake reasons, Runtime generations, Review execution
6
6
  * vs semantic failures, Integration failure classes and gate reuse, telemetry
7
7
  * volume, and the longest/stale executions. It never writes Task state, never
8
8
  * wakes a Leader, and never takes the storage write lock — it only calls the
@@ -259,7 +259,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
259
259
  failedCount += 1;
260
260
  failedDurationMs += duration;
261
261
  cumulativeDurationMs += duration;
262
- addLaunchFailureCounts(launchFailures, turn.result?.output);
262
+ addLaunchFailureCounts(launchFailures, turn.result?.diagnostic);
263
263
  failures.push(classifyTurnFailure(turn));
264
264
  }
265
265
  }
@@ -396,11 +396,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
396
396
  let completed = 0;
397
397
  let failedCount = 0;
398
398
  let infraFailed = 0;
399
- let semanticNegative = 0;
400
399
  let deltaTotal = 0;
401
- let deltaAccepted = 0;
402
- let deltaFinding = 0;
403
- let deltaEscalated = 0;
404
400
  const classes = [];
405
401
  for (const taskId of taskIds) {
406
402
  for (const round of store.listReviewRounds(taskId)) {
@@ -413,19 +409,10 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
413
409
  failedCount += 1;
414
410
  if (round.deltaRecheck !== undefined) {
415
411
  deltaTotal += 1;
416
- if (round.deltaRecheck.disposition === "equivalent-and-accepted")
417
- deltaAccepted += 1;
418
- else if (round.deltaRecheck.disposition === "finding")
419
- deltaFinding += 1;
420
- else if (round.deltaRecheck.disposition === "requires-full-review")
421
- deltaEscalated += 1;
422
412
  }
423
- const classification = classifyReviewRound(round, store);
413
+ const classification = classifyReviewRound(round);
424
414
  if (classification.faultClass === "review-infra")
425
415
  infraFailed += 1;
426
- else if (classification.faultClass === "review-semantic-negative") {
427
- semanticNegative += 1;
428
- }
429
416
  if (classification.faultClass !== "other")
430
417
  classes.push(classification);
431
418
  }
@@ -435,14 +422,8 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
435
422
  completed,
436
423
  failed: failedCount,
437
424
  infraFailed,
438
- semanticNegative,
439
425
  faultClasses: countFaultClasses(classes),
440
- deltaRechecks: {
441
- total: deltaTotal,
442
- equivalentAndAccepted: deltaAccepted,
443
- finding: deltaFinding,
444
- requiresFullReview: deltaEscalated
445
- }
426
+ deltaRechecks: deltaTotal
446
427
  });
447
428
  }
448
429
  catch (error) {
@@ -651,11 +632,9 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
651
632
  workItems: withinWindow(store.listWorkItems(taskId), options),
652
633
  changeSets: withinWindow(store.listChangeSets(taskId), options),
653
634
  reviewRounds: withinWindow(store.listReviewRounds(taskId), options),
654
- reviewFindings: withinWindow(store.listReviewFindings(taskId), options),
655
635
  integrations: withinWindow(store.listIntegrationAttempts(taskId), options),
656
636
  durableJobs: withinWindow(store.listDurableJobs(taskId), options),
657
637
  publications: withinWindow(store.listPublicationReferences(taskId), options),
658
- decisions: withinWindow(store.listDecisions(taskId), options),
659
638
  events: withinWindow(store.listEvents(taskId), options),
660
639
  managedWorkspaces: withinWindow(store.listManagedWorkspaces(taskId), options)
661
640
  })];