@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
@@ -1,10 +1,9 @@
1
- import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
2
- import { contextContentDigest, validateContextSnapshotRef } from "../context/contextSnapshot.js";
1
+ import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
3
2
  import { validateReviewConfig } from "../review/reviewConfig.js";
4
3
  import { taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
5
4
  import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
6
5
  import { validateTaskRecordReference } from "../task/taskRecordReference.js";
7
- import { assertExecutionGroupTransition, validateExecutionGroup, WORK_ITEM_EXPLORATION_STAGES } from "../execution/executionGroup.js";
6
+ import { assertWorkItemExecutionGroupTransition, validateWorkItemExecutionGroup as validateExecutionGroupRecord, workItemExecutionGroupSettled } from "../execution/workItemExecution.js";
8
7
  const TERMINAL_STATUSES = [
9
8
  "completed",
10
9
  "failed",
@@ -13,7 +12,7 @@ const TERMINAL_STATUSES = [
13
12
  export function createWorkItem(id, taskId, input, now) {
14
13
  const timestamp = now.toISOString();
15
14
  return validateWorkItem({
16
- schemaVersion: 13,
15
+ schemaVersion: 15,
17
16
  id: requireIdentity(id, "Work Item id"),
18
17
  taskId: requireIdentity(taskId, "Task id"),
19
18
  title: requireText(input.title, "Work item title"),
@@ -38,6 +37,54 @@ export function createWorkItem(id, taskId, input, now) {
38
37
  updatedAt: timestamp
39
38
  });
40
39
  }
40
+ /** Replace the mutable definition of an execution-free Draft WorkItem. */
41
+ export function editDraftWorkItemDefinition(workItem, update, now) {
42
+ validateWorkItem(workItem);
43
+ if (workItem.status !== "pending"
44
+ || workItem.executionGroups.length > 0
45
+ || workItem.candidates.length > 0
46
+ || workItem.disposition !== undefined
47
+ || workItem.workspaceDisposition !== undefined) {
48
+ throw new Error(`Work Item has execution or retirement facts: ${workItem.id}.`);
49
+ }
50
+ const next = {
51
+ ...workItem,
52
+ ...(update.title === undefined
53
+ ? {}
54
+ : { title: requireText(update.title, "Work item title") }),
55
+ ...(update.objective === undefined
56
+ ? {}
57
+ : { objective: requireText(update.objective, "Work item objective") }),
58
+ ...(update.acceptance === undefined
59
+ ? {}
60
+ : { acceptance: normalizedUniqueText(update.acceptance, "Work item acceptance criterion") }),
61
+ ...(update.dependsOn === undefined
62
+ ? {}
63
+ : { dependsOn: normalizedUniqueIdentities(update.dependsOn, "Work item dependency") }),
64
+ ...(update.writeProjectIds === undefined
65
+ ? {}
66
+ : {
67
+ writeProjectIds: normalizedUniqueIdentities(update.writeProjectIds, "Work item writable Project")
68
+ }),
69
+ revision: workItem.revision + 1,
70
+ updatedAt: now.toISOString()
71
+ };
72
+ if (update.baseRefs === null)
73
+ delete next.baseRefs;
74
+ else if (update.baseRefs !== undefined) {
75
+ const normalized = normalizeProjectBaseRefs(update.baseRefs);
76
+ if (normalized.length === 0)
77
+ delete next.baseRefs;
78
+ else
79
+ next.baseRefs = normalized;
80
+ }
81
+ if (update.assignee === null)
82
+ delete next.assignee;
83
+ else if (update.assignee !== undefined) {
84
+ next.assignee = requireIdentity(update.assignee, "Work item assignee");
85
+ }
86
+ return validateWorkItem(next);
87
+ }
41
88
  export function submitWorkItemCandidate(workItem, input, now) {
42
89
  validateWorkItem(workItem);
43
90
  if (workItem.status !== "running") {
@@ -138,8 +185,8 @@ export function attachWorkItemExecutionGroup(workItem, executionGroup, now) {
138
185
  throw new Error(`Work Item historical ExecutionGroup is immutable: ${workItem.id}/${checked.id}.`);
139
186
  }
140
187
  const current = currentWorkItemExecutionGroup(workItem);
141
- if (current !== undefined && current.resolution === undefined) {
142
- throw new Error(`Work Item already has an unresolved ExecutionGroup: ${workItem.id}/${current.id}.`);
188
+ if (current !== undefined && !workItemExecutionGroupSettled(current)) {
189
+ throw new Error(`Work Item already has an open ExecutionGroup: ${workItem.id}/${current.id}.`);
143
190
  }
144
191
  return validateWorkItem({
145
192
  ...workItem,
@@ -160,12 +207,12 @@ export function updateWorkItemExecutionGroup(workItem, executionGroup, now) {
160
207
  if (workItem.currentExecutionGroupId !== checked.id) {
161
208
  throw new Error(`Work Item historical ExecutionGroup is immutable: ${workItem.id}/${checked.id}.`);
162
209
  }
163
- if (JSON.stringify(existing.target) !== JSON.stringify(checked.target)) {
164
- throw new Error(`Work Item ExecutionGroup target is immutable: ${workItem.id}/${checked.id}.`);
210
+ if (JSON.stringify(existing.assignment) !== JSON.stringify(checked.assignment)) {
211
+ throw new Error(`Work Item ExecutionGroup Assignment is immutable: ${workItem.id}/${checked.id}.`);
165
212
  }
166
213
  if (JSON.stringify(existing) === JSON.stringify(checked))
167
214
  return workItem;
168
- assertExecutionGroupTransition(existing, checked);
215
+ assertWorkItemExecutionGroupTransition(existing, checked);
169
216
  return validateWorkItem({
170
217
  ...workItem,
171
218
  executionGroups: workItem.executionGroups.map((group) => group.id === checked.id ? checked : group),
@@ -216,10 +263,10 @@ export function retryFailedWorkItem(workItem, now) {
216
263
  throw new Error(`Work item workspace is already settled: ${workItem.id}.`);
217
264
  }
218
265
  const { outcome: _outcome, endedAt: _endedAt, ...base } = workItem;
219
- // Keep every historical Group. A resolved current Group is cleared only as
220
- // the current pointer; dispatch appends a fresh immutable Group.
266
+ // Keep every historical Group. A settled current Group is cleared only as
267
+ // the current pointer; redispatch appends a fresh immutable Group.
221
268
  const current = currentWorkItemExecutionGroup(workItem);
222
- const retryBase = current?.resolution === undefined
269
+ const retryBase = current === undefined || !workItemExecutionGroupSettled(current)
223
270
  ? base
224
271
  : (({ currentExecutionGroupId: _currentExecutionGroupId, ...history }) => history)(base);
225
272
  return validateWorkItem({
@@ -248,8 +295,31 @@ export function recordWorkItemWorkspaceDisposition(workItem, disposition, now) {
248
295
  });
249
296
  }
250
297
  export function validateWorkItem(workItem) {
251
- if (workItem.schemaVersion !== 13)
252
- throw new Error("WorkItem must use schemaVersion 13.");
298
+ rejectUnknownFields(workItem, [
299
+ "schemaVersion",
300
+ "id",
301
+ "taskId",
302
+ "title",
303
+ "objective",
304
+ "acceptance",
305
+ "dependsOn",
306
+ "writeProjectIds",
307
+ "executionGroups",
308
+ "currentExecutionGroupId",
309
+ "baseRefs",
310
+ "revision",
311
+ "assignee",
312
+ "status",
313
+ "candidates",
314
+ "outcome",
315
+ "disposition",
316
+ "workspaceDisposition",
317
+ "createdAt",
318
+ "updatedAt",
319
+ "endedAt"
320
+ ], "WorkItem");
321
+ if (workItem.schemaVersion !== 15)
322
+ throw new Error("WorkItem must use schemaVersion 15.");
253
323
  validateTaskRecordReference({ taskId: workItem.taskId, localId: workItem.id }, "workItem");
254
324
  requireIdentity(workItem.taskId, "Task id");
255
325
  requireText(workItem.title, "Work item title");
@@ -284,7 +354,6 @@ export function validateWorkItem(workItem) {
284
354
  }
285
355
  groupIds.add(group.id);
286
356
  }
287
- validateWorkItemExplorationHistory(workItem.executionGroups);
288
357
  if (workItem.currentExecutionGroupId !== undefined) {
289
358
  requireIdentity(workItem.currentExecutionGroupId, "ExecutionGroup id");
290
359
  if (!groupIds.has(workItem.currentExecutionGroupId)) {
@@ -311,16 +380,6 @@ export function validateWorkItem(workItem) {
311
380
  if (candidate.workItemRevision > workItem.revision) {
312
381
  throw new Error("Work Item candidate revision cannot exceed the Work Item revision.");
313
382
  }
314
- if (candidate.executionGroupId !== undefined) {
315
- const group = workItem.executionGroups.find(({ id }) => id === candidate.executionGroupId);
316
- if (group?.stage !== undefined) {
317
- if (group.stage.stage !== "resolve"
318
- || group.resolution?.decision !== "accept"
319
- || !group.resolution.selectedLaneIds.includes(candidate.executionLaneId)) {
320
- throw new Error("An exploration Candidate must come from the accepted Resolve stage.");
321
- }
322
- }
323
- }
324
383
  });
325
384
  const currentCandidate = currentWorkItemCandidate(workItem);
326
385
  if (workItem.status === "awaiting_acceptance" && currentCandidate === undefined) {
@@ -363,130 +422,6 @@ export function validateWorkItem(workItem) {
363
422
  }
364
423
  return workItem;
365
424
  }
366
- /**
367
- * Project the next legal exploration stage from immutable WorkItem Group
368
- * history. Retry repeats the current stage; retry at Resolve begins the next
369
- * round. Reject has no continuation, while blocked follows the same bounded
370
- * retry path after the Leader settles any required InputRequest.
371
- */
372
- export function planWorkItemExplorationStage(workItem, input) {
373
- validateWorkItem(workItem);
374
- const snapshot = validateContextSnapshotRef(input.contextSnapshotRef);
375
- const capacity = executionStrategyCapacity(input.strategy);
376
- const current = currentWorkItemExecutionGroup(workItem);
377
- if (workItem.executionGroups.length === 0) {
378
- if (workItem.status !== "pending" && workItem.status !== "running") {
379
- throw new Error(`Work Item cannot begin exploration from ${workItem.status}: ${workItem.id}.`);
380
- }
381
- if (input.mode === undefined) {
382
- throw new Error("The first exploration stage requires a mode.");
383
- }
384
- if (input.convergence === undefined) {
385
- throw new Error("The first exploration stage requires a candidate convergence policy.");
386
- }
387
- const resourceBudget = requireNewStageResourceBudget(input.resourceBudget);
388
- const resources = requireNewStageResources(input.resources);
389
- return {
390
- schemaVersion: 1,
391
- mode: input.mode,
392
- stage: "plan",
393
- round: 1,
394
- stageAttempt: 1,
395
- maxRounds: requirePositiveInteger(input.maxRounds ?? 1, "Exploration max rounds"),
396
- budget: {
397
- maxLanes: capacity,
398
- maxAttempts: requirePositiveInteger(input.maxAttempts ?? 1, "Exploration stage max attempts"),
399
- ...resourceBudget
400
- },
401
- resources,
402
- contextSnapshotRef: snapshot,
403
- parentResults: [],
404
- ...(input.convergence === undefined ? {} : { convergence: input.convergence })
405
- };
406
- }
407
- if (current === undefined || current.stage === undefined) {
408
- throw new Error("A Work Item cannot mix single and exploration ExecutionGroups.");
409
- }
410
- if (current.resolution === undefined) {
411
- throw new Error(`Execution stage is still active: ${current.id}.`);
412
- }
413
- if (input.mode !== undefined && input.mode !== current.stage.mode) {
414
- throw new Error("Work Item exploration mode is immutable.");
415
- }
416
- if (input.maxRounds !== undefined && input.maxRounds !== current.stage.maxRounds) {
417
- throw new Error("Work Item exploration maxRounds is immutable.");
418
- }
419
- const parentResults = selectedParentResults(current);
420
- const base = {
421
- schemaVersion: 1,
422
- mode: current.stage.mode,
423
- maxRounds: current.stage.maxRounds,
424
- contextSnapshotRef: snapshot,
425
- parentResults,
426
- ...(current.stage.convergence === undefined
427
- ? {}
428
- : { convergence: current.stage.convergence })
429
- };
430
- if (current.resolution.decision === "reject") {
431
- throw new Error(`Rejected exploration has no continuation: ${current.id}.`);
432
- }
433
- if (current.resolution.decision === "accept") {
434
- if (current.stage.stage === "resolve") {
435
- throw new Error(`Accepted Resolve stage already completed exploration: ${current.id}.`);
436
- }
437
- const resourceBudget = requireNewStageResourceBudget(input.resourceBudget);
438
- const resources = requireNewStageResources(input.resources);
439
- return {
440
- ...base,
441
- stage: nextExplorationStage(current.stage.stage),
442
- round: current.stage.round,
443
- stageAttempt: 1,
444
- budget: {
445
- maxLanes: capacity,
446
- maxAttempts: requirePositiveInteger(input.maxAttempts ?? 1, "Exploration stage max attempts"),
447
- ...resourceBudget
448
- },
449
- resources
450
- };
451
- }
452
- if (current.stage.stage === "resolve" && current.resolution.decision === "retry") {
453
- if (current.stage.round >= current.stage.maxRounds) {
454
- throw new Error(`Work Item exploration round budget is exhausted: ${workItem.id}.`);
455
- }
456
- const resourceBudget = requireNewStageResourceBudget(input.resourceBudget);
457
- const resources = requireNewStageResources(input.resources);
458
- return {
459
- ...base,
460
- stage: "plan",
461
- round: current.stage.round + 1,
462
- stageAttempt: 1,
463
- budget: {
464
- maxLanes: capacity,
465
- maxAttempts: requirePositiveInteger(input.maxAttempts ?? 1, "Exploration stage max attempts"),
466
- ...resourceBudget
467
- },
468
- resources
469
- };
470
- }
471
- if (current.stage.stageAttempt >= current.stage.budget.maxAttempts) {
472
- throw new Error(`Work Item exploration stage attempt budget is exhausted: ${workItem.id}.`);
473
- }
474
- if (capacity !== current.stage.budget.maxLanes) {
475
- throw new Error("A retried exploration stage must keep its frozen Lane budget.");
476
- }
477
- if (input.maxAttempts !== undefined
478
- && input.maxAttempts !== current.stage.budget.maxAttempts) {
479
- throw new Error("A retried exploration stage must keep its frozen attempt budget.");
480
- }
481
- return {
482
- ...base,
483
- stage: current.stage.stage,
484
- round: current.stage.round,
485
- stageAttempt: current.stage.stageAttempt + 1,
486
- budget: current.stage.budget,
487
- ...(current.stage.resources === undefined ? {} : { resources: current.stage.resources })
488
- };
489
- }
490
425
  /** Resolve the WorkItem's current execution iteration. */
491
426
  export function currentWorkItemExecutionGroup(workItem) {
492
427
  return workItem.currentExecutionGroupId === undefined
@@ -509,124 +444,17 @@ export function workItemOwnsUnresolvedExecutionLane(workItem, executionGroupId,
509
444
  return false;
510
445
  const group = workItem.executionGroups.find(({ id }) => id === executionGroupId);
511
446
  return group !== undefined
512
- && group.resolution === undefined
513
- && group.lanes.some(({ id, status }) => (id === executionLaneId && status === "failed"));
514
- }
515
- function validateWorkItemExplorationHistory(groups) {
516
- const staged = groups.filter(({ stage }) => stage !== undefined);
517
- if (staged.length === 0)
518
- return;
519
- if (staged.length !== groups.length) {
520
- throw new Error("A Work Item cannot mix single and exploration ExecutionGroups.");
521
- }
522
- const first = groups[0].stage;
523
- if (first.stage !== "plan"
524
- || first.round !== 1
525
- || first.stageAttempt !== 1
526
- || first.parentResults.length !== 0) {
527
- throw new Error("Work Item exploration must begin at Plan round 1 attempt 1.");
528
- }
529
- for (let index = 1; index < groups.length; index += 1) {
530
- const previous = groups[index - 1];
531
- const current = groups[index];
532
- const before = previous.stage;
533
- const after = current.stage;
534
- if (previous.resolution === undefined) {
535
- throw new Error(`Exploration stage must resolve before its successor: ${previous.id}.`);
536
- }
537
- if (previous.resolution.decision === "reject") {
538
- throw new Error(`Rejected exploration cannot have a successor: ${previous.id}.`);
539
- }
540
- if (after.mode !== before.mode
541
- || after.maxRounds !== before.maxRounds
542
- || JSON.stringify(after.convergence) !== JSON.stringify(before.convergence)) {
543
- throw new Error("Work Item exploration mode, maxRounds and convergence policy are immutable.");
544
- }
545
- const parents = selectedParentResults(previous);
546
- if (JSON.stringify(after.parentResults) !== JSON.stringify(parents)) {
547
- throw new Error(`Exploration parentResults do not match ${previous.id}.`);
548
- }
549
- if (previous.resolution.decision === "accept") {
550
- if (before.stage === "resolve"
551
- || after.stage !== nextExplorationStage(before.stage)
552
- || after.round !== before.round
553
- || after.stageAttempt !== 1) {
554
- throw new Error(`Exploration stage transition is invalid: ${previous.id}/${current.id}.`);
555
- }
556
- continue;
557
- }
558
- if (before.stage === "resolve" && previous.resolution.decision === "retry") {
559
- if (before.round >= before.maxRounds
560
- || after.stage !== "plan"
561
- || after.round !== before.round + 1
562
- || after.stageAttempt !== 1) {
563
- throw new Error(`Exploration round transition is invalid: ${previous.id}/${current.id}.`);
564
- }
565
- continue;
566
- }
567
- if (after.stage !== before.stage
568
- || after.round !== before.round
569
- || after.stageAttempt !== before.stageAttempt + 1
570
- || after.stageAttempt > before.budget.maxAttempts
571
- || JSON.stringify(after.budget) !== JSON.stringify(before.budget)
572
- || JSON.stringify(after.resources) !== JSON.stringify(before.resources)) {
573
- throw new Error(`Exploration retry transition is invalid: ${previous.id}/${current.id}.`);
574
- }
575
- }
576
- }
577
- function requireNewStageResourceBudget(value) {
578
- if (value?.maxTokens === undefined
579
- || value.maxToolCalls === undefined
580
- || value.maxWallClockSeconds === undefined) {
581
- throw new Error("A new exploration stage requires token, tool-call and wall-clock budgets.");
582
- }
583
- return {
584
- maxTokens: requirePositiveInteger(value.maxTokens, "Exploration stage max tokens"),
585
- maxToolCalls: requirePositiveInteger(value.maxToolCalls, "Exploration stage max tool calls"),
586
- maxWallClockSeconds: requirePositiveInteger(value.maxWallClockSeconds, "Exploration stage max wall-clock seconds")
587
- };
588
- }
589
- function requireNewStageResources(value) {
590
- if (value === undefined) {
591
- throw new Error("A new exploration stage requires resource completion policy.");
592
- }
593
- return value;
594
- }
595
- function selectedParentResults(group) {
596
- if (group.resolution === undefined) {
597
- throw new Error(`ExecutionGroup is unresolved: ${group.id}.`);
598
- }
599
- return group.resolution.selectedLaneIds.map((laneId) => {
600
- const lane = group.lanes.find(({ id }) => id === laneId);
601
- if (lane?.result === undefined) {
602
- throw new Error(`Selected ExecutionLane result is missing: ${group.id}/${laneId}.`);
603
- }
604
- return {
605
- executionGroupId: group.id,
606
- executionLaneId: lane.id,
607
- resultDigest: contextContentDigest(lane.result)
608
- };
609
- });
610
- }
611
- function nextExplorationStage(stage) {
612
- const index = WORK_ITEM_EXPLORATION_STAGES.indexOf(stage);
613
- const next = WORK_ITEM_EXPLORATION_STAGES[index + 1];
614
- if (next === undefined)
615
- throw new Error("Resolve is the final exploration stage.");
616
- return next;
617
- }
618
- function executionStrategyCapacity(strategy) {
619
- return requirePositiveInteger(strategy.mode === "fixed" ? strategy.count : strategy.max, "Execution strategy capacity");
447
+ && !workItemExecutionGroupSettled(group)
448
+ && group.lanes.some(({ id, disposition, currentTurnId }) => (id === executionLaneId && disposition === "open" && currentTurnId !== undefined));
620
449
  }
621
450
  function validateWorkItemExecutionGroup(group, taskId, workItemId) {
622
- validateExecutionGroup(group);
623
- if (group.taskId !== taskId || group.purpose !== "execution") {
451
+ validateExecutionGroupRecord(group);
452
+ if (group.taskId !== taskId) {
624
453
  throw new Error(`Work Item ExecutionGroup provenance is invalid: ${workItemId}.`);
625
454
  }
626
- if (group.target.kind !== "work-item"
627
- || group.target.workItemId !== workItemId
628
- || group.target.taskId !== taskId) {
629
- throw new Error(`Work Item ExecutionGroup target is invalid: ${workItemId}.`);
455
+ if (group.assignment.workItemId !== workItemId
456
+ || group.assignment.taskId !== taskId) {
457
+ throw new Error(`Work Item ExecutionGroup Assignment is invalid: ${workItemId}.`);
630
458
  }
631
459
  return group;
632
460
  }
@@ -700,11 +528,10 @@ export function validateWorkItemCandidate(candidate) {
700
528
  validateCandidateGitSnapshot(candidate.gitSnapshot, candidate.workspace);
701
529
  }
702
530
  if (candidate.taskMainSnapshot !== undefined) {
703
- if (candidate.source.type !== "direct"
704
- || candidate.workspace !== undefined
531
+ if (candidate.workspace !== undefined
705
532
  || candidate.gitSnapshot !== undefined
706
533
  || candidate.taskFinalReviewContract === undefined) {
707
- throw new Error("Task-main Candidate snapshot requires an exact direct metadata-only Candidate.");
534
+ throw new Error("Task-main Candidate snapshot requires an exact metadata-only Candidate.");
708
535
  }
709
536
  validateDirectTaskMainSnapshot(candidate.taskMainSnapshot);
710
537
  }
@@ -933,3 +760,9 @@ function sameDisposition(left, right) {
933
760
  && left.summary === right.summary
934
761
  && left.replacementWorkItemId === right.replacementWorkItemId;
935
762
  }
763
+ function rejectUnknownFields(value, fields, label) {
764
+ const allowed = new Set(fields);
765
+ const unknown = Object.keys(value).find((field) => !allowed.has(field));
766
+ if (unknown !== undefined)
767
+ throw new Error(`${label} has unknown field: ${unknown}.`);
768
+ }
@@ -60,35 +60,26 @@ export class WorkItemChangeSetManager {
60
60
  throw new Error(`WorkItem Project workspace is not clean: ${item.id}/${entry.projectId}.`);
61
61
  }
62
62
  const headCommit = (await git.inspect(entry.path, "HEAD")).baseCommit;
63
- const latest = latestWorkItemChangeSet(this.store, item.taskId, item.id, entry.projectId);
64
- if (headCommit === entry.baseCommit) {
65
- if (latest !== undefined) {
66
- throw new Error(`WorkItem Project no longer matches its latest ChangeSet: ${item.id}/${entry.projectId}.`);
67
- }
68
- projects.push({
69
- projectId: entry.projectId,
70
- baseCommit: entry.baseCommit,
71
- headCommit
72
- });
73
- continue;
74
- }
75
- if (latest === undefined
76
- || latest.baseCommit !== entry.baseCommit
77
- || latest.headCommit !== headCommit
78
- || latest.branch !== entry.branch) {
79
- throw new Error(`WorkItem Project has uncaptured commits: ${item.id}/${entry.projectId}.`);
63
+ const candidate = governingWorkItemCandidate(item);
64
+ const resultCommit = candidate?.gitSnapshot?.projects.find(({ projectId }) => projectId === entry.projectId)?.commit;
65
+ if (candidate?.workspace === undefined
66
+ || !isDeepStrictEqual(candidate.workspace, workspace)
67
+ || resultCommit !== headCommit) {
68
+ throw new Error(`WorkItem Project no longer matches its frozen result: ${item.id}/${entry.projectId}.`);
80
69
  }
81
70
  const integrated = this.store.listIntegrationAttempts(item.taskId).some((integration) => (integration.status === "committed"
82
71
  && integration.projectId === entry.projectId
83
- && integration.changeSetIds.includes(latest.id)));
72
+ && integration.source.kind === "work-item"
73
+ && integration.source.workItemId === item.id
74
+ && integration.source.startCommit === entry.baseCommit
75
+ && integration.source.resultCommit === headCommit));
84
76
  if (!integrated) {
85
- throw new Error(`WorkItem ChangeSet is not integrated: ${latest.id}.`);
77
+ throw new Error(`WorkItem result is not integrated: ${item.id}/${entry.projectId}.`);
86
78
  }
87
79
  projects.push({
88
80
  projectId: entry.projectId,
89
81
  baseCommit: entry.baseCommit,
90
- headCommit,
91
- changeSetId: latest.id
82
+ headCommit
92
83
  });
93
84
  }
94
85
  return {
@@ -167,8 +158,7 @@ export class WorkItemChangeSetManager {
167
158
  projects.push({
168
159
  projectId: entry.projectId,
169
160
  baseCommit: entry.baseCommit,
170
- headCommit,
171
- changeSetId: latest.id
161
+ headCommit
172
162
  });
173
163
  continue;
174
164
  }
@@ -226,8 +216,7 @@ export class WorkItemChangeSetManager {
226
216
  deletedPaths: result.deletedPaths
227
217
  }),
228
218
  deletedPaths: result.deletedPaths,
229
- ...(targetRef === undefined ? {} : { targetRef }),
230
- evidenceRefs: this.#captureEvidenceRefs(context.taskId, context.workItemId, result.headCommit)
219
+ ...(targetRef === undefined ? {} : { targetRef })
231
220
  });
232
221
  return this.store.transaction((tx) => {
233
222
  assertCaptureStillCurrent(tx, context);
@@ -258,39 +247,6 @@ export class WorkItemChangeSetManager {
258
247
  return changeSet;
259
248
  });
260
249
  }
261
- /**
262
- * Verified evidence for the exact reviewed candidate. A completed
263
- * ReviewRound freezes the candidate it reviewed at `reviewBaseCommit`; when
264
- * the captured ChangeSet head is that commit, the round's checks are
265
- * durable evidence for it. "completed" alone is not a verdict: the
266
- * reviewer may have completed with failed checks (requires-repair), so only a
267
- * round with an explicit positive verdict — at least one passed check and
268
- * no failed one — may feed `evidenceRefs`. The link is by
269
- * `reviewBaseCommit`, not by `evidenceCommit`: the latter records the exact
270
- * tree the checks ran on (the frozen base for a clean review, a diagnostic
271
- * commit for a committed-diagnostic review, absent for a dirty review), and
272
- * the queue separately re-checks that tree binding before waiving a gate.
273
- */
274
- #captureEvidenceRefs(taskId, workItemId, headCommit) {
275
- return this.store.listReviewRounds(taskId)
276
- .filter((round) => round.status === "completed"
277
- && round.workItemId === workItemId
278
- && round.reviewBaseCommit === headCommit
279
- && hasPositiveReviewVerdict(round.checks))
280
- .map(({ id }) => `review-round:${id}`);
281
- }
282
- }
283
- /**
284
- * A ReviewRound carries reusable verification evidence only when its checks
285
- * state an explicit positive verdict: at least one check passed and none
286
- * failed. A round without checks, or with only skipped checks, has no
287
- * positive conclusion; a round with a failed check rejects the candidate.
288
- * Neither may skip a later gate.
289
- */
290
- function hasPositiveReviewVerdict(checks) {
291
- return checks !== undefined
292
- && checks.some((check) => check.outcome === "passed")
293
- && checks.every((check) => check.outcome !== "failed");
294
250
  }
295
251
  function requireCapturableContext(store, taskId, workItemId, taskFinalReviewContract) {
296
252
  const item = store.getWorkItem(taskId, workItemId);
@@ -327,7 +283,7 @@ function requireCapturableContext(store, taskId, workItemId, taskFinalReviewCont
327
283
  throw new Error(`Work item has no managed workspace: ${item.id}.`);
328
284
  }
329
285
  const candidate = item.candidates.at(-1);
330
- if (candidate?.source.type !== "direct"
286
+ if (candidate === undefined
331
287
  || !sameTaskFinalReviewContract(candidate.taskFinalReviewContract, taskFinalReviewContract)) {
332
288
  throw new Error(`Work item has no matching exact Task-final direct Candidate: ${item.id}.`);
333
289
  }
@@ -51,7 +51,7 @@ There is no compatibility lookup, cross-Task guess, or bare-ID fallback.
51
51
 
52
52
  ## Current-schema boundary
53
53
 
54
- Runtime opens only aggregate v13 / StoredTask v12. It does not convert,
54
+ Runtime opens only aggregate v31 / Task v7. It does not convert,
55
55
  dual-read, or infer records from an older schema. If an existing `YUI_HOME`
56
56
  does not match the current schema, keep it untouched for external archival
57
57
  and initialize a fresh home for this runtime.