@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.
- package/ARCHITECTURE.md +64 -35
- package/README.md +254 -124
- package/dist/agent/managedRuntimeEnvironment.js +5 -8
- package/dist/cli/commandCatalog.js +74 -109
- package/dist/cli/helpRenderer.js +1 -2
- package/dist/cli/interactionCandidates.js +10 -0
- package/dist/cli/interactionPolicy.js +76 -37
- package/dist/cli/roleWizard.js +5 -6
- package/dist/cli/updateOrchestrator.js +10 -8
- package/dist/cli/updatePorts.js +17 -13
- package/dist/cli/upgradeCommand.js +2 -5
- package/dist/cli.js +268 -231
- package/dist/commands/agentCommands.js +13 -0
- package/dist/commands/configCommands.js +7 -19
- package/dist/commands/configOverview.js +3 -2
- package/dist/commands/durableJobCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +4 -9
- package/dist/commands/globalRoleCommands.js +17 -0
- package/dist/commands/profileCommands.js +200 -30
- package/dist/commands/sessionCommands.js +1 -1
- package/dist/commands/taskActor.js +29 -127
- package/dist/commands/taskChangeSetCommands.js +1 -3
- package/dist/commands/taskCommands.js +1771 -2107
- package/dist/commands/taskCompletionGate.js +27 -103
- package/dist/commands/taskContextCommand.js +47 -80
- package/dist/commands/taskExecutionCommands.js +9 -27
- package/dist/commands/taskInputCommands.js +10 -10
- package/dist/commands/taskIntegrationCommands.js +69 -61
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -29
- package/dist/commands/taskOverviewCommand.js +25 -3
- package/dist/commands/taskPublicationCommands.js +151 -85
- package/dist/commands/taskPublicationVerifyCommand.js +181 -0
- package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
- package/dist/commands/taskRoleRuntimeStatus.js +5 -6
- package/dist/commands/taskUpstreamCommands.js +79 -95
- package/dist/config/configCatalog.js +1 -1
- package/dist/context/contextSnapshot.js +17 -2
- package/dist/context/sourceTurnContext.js +30 -0
- package/dist/context/turnContextPack.js +107 -48
- package/dist/context/turnInputContract.js +11 -4
- package/dist/context/wakeNotification.js +84 -22
- package/dist/controller/agentRuntimeObserver.js +9 -12
- package/dist/controller/clientRuntime.js +13 -13
- package/dist/controller/controller.js +11 -11
- package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
- package/dist/controller/jobControl.js +11 -10
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +2 -2
- package/dist/controller/runtime.js +18 -61
- package/dist/controller/runtimeEventInbox.js +57 -34
- package/dist/controller/runtimeEventProcessor.js +36 -96
- package/dist/controller/runtimeHookTurnFence.js +33 -57
- package/dist/controller/runtimeLaunchCoordinator.js +44 -40
- package/dist/controller/runtimeObservationHook.js +18 -12
- package/dist/controller/sessionNotify.js +43 -28
- package/dist/controller/sessionOwnerReconciliation.js +21 -21
- package/dist/controller/structuredProviderObservation.js +13 -16
- package/dist/coordination/workMailboxQueue.js +132 -0
- package/dist/core/controllerServer.js +1 -0
- package/dist/domain/agentResultTransport.js +60 -0
- package/dist/execution/executionHealth.js +111 -118
- package/dist/execution/reviewMainTurn.js +161 -0
- package/dist/execution/workItemExecution.js +454 -0
- package/dist/execution/workItemExecutionProjection.js +309 -0
- package/dist/execution/workItemMainTurn.js +164 -0
- package/dist/executor/agentExecutor.js +19 -19
- package/dist/executor/executorRegistry.js +7 -28
- package/dist/executor/fileRoleLaunchPlanner.js +16 -82
- package/dist/external/githubPublicationVerifier.js +114 -0
- package/dist/external/gitlabPublicationVerifier.js +158 -0
- package/dist/external/pinnedCommandRunner.js +73 -0
- package/dist/integration/changeSetManifest.js +0 -3
- package/dist/integration/deliveryObligation.js +43 -6
- package/dist/integration/gitIntegrationService.js +284 -101
- package/dist/integration/integrationAttempt.js +64 -14
- package/dist/integration/integrationQueueEntry.js +5 -35
- package/dist/integration/integrationQueueService.js +65 -194
- package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
- package/dist/lifecycle/exactTurnTerminalization.js +100 -97
- package/dist/message/message.js +16 -0
- package/dist/observability/executionAudit.js +4 -25
- package/dist/observability/faultClassification.js +58 -70
- package/dist/observability/orchestrationMetrics.js +8 -50
- package/dist/profile/agentProfile.js +60 -15
- package/dist/profile/agentProfileRuntime.js +100 -0
- package/dist/release/releaseWorkflowPorts.js +5 -68
- package/dist/repository/executionLaneGitSnapshot.js +80 -0
- package/dist/repository/gitWorkspace.js +9 -67
- package/dist/repository/taskBaseFreshness.js +3 -1
- package/dist/repository/taskWorkspaceCoordinator.js +32 -4
- package/dist/repository/taskWorkspacePreparer.js +252 -411
- package/dist/resources/liveReferences.js +6 -55
- package/dist/resources/resourceDiscovery.js +1 -30
- package/dist/resources/resourceRegistrar.js +0 -23
- package/dist/review/deltaRecheck.js +8 -65
- package/dist/review/reviewAcceptance.js +22 -19
- package/dist/review/reviewConfig.js +0 -10
- package/dist/review/reviewDecision.js +2 -2
- package/dist/review/reviewRound.js +147 -273
- package/dist/review/reviewerAvailability.js +13 -23
- package/dist/role/role.js +3 -0
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +51 -51
- package/dist/runtime/builtinAgentDrivers.js +9 -2
- package/dist/runtime/codexAppServerRuntime.js +23 -5
- package/dist/runtime/exactControlPlane.js +2 -267
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/launchBroker.js +11 -11
- package/dist/runtime/lifecycleReservation.js +2 -2
- package/dist/runtime/managedCaller.js +111 -0
- package/dist/runtime/ports.js +16 -3
- package/dist/runtime/preallocatedNativeSession.js +2 -2
- package/dist/runtime/processExitObservation.js +3 -2
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/runtimeBinding.js +1 -1
- package/dist/runtime/runtimeObservation.js +33 -16
- package/dist/runtime/runtimeSessionCandidate.js +2 -2
- package/dist/runtime/runtimeStopReceipt.js +11 -11
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/sessionOwnerIdentity.js +9 -9
- package/dist/runtime/sessionOwnerRegistry.js +13 -13
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTerminationGuard.js +9 -9
- package/dist/runtime/sessionTokenMetrics.js +5 -7
- package/dist/runtime/structuredProviderHost.js +5 -5
- package/dist/runtime/taskRuntimeIsolation.js +24 -24
- package/dist/runtime/tmuxAdapters.js +32 -30
- package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
- package/dist/scheduler/leaderWakeupProcessor.js +78 -55
- package/dist/scheduler/roleTurnLiveness.js +6 -6
- package/dist/scheduler/roleTurnStall.js +8 -9
- package/dist/scheduler/taskExecutionProjection.js +19 -41
- package/dist/scheduler/taskObservabilityProjection.js +75 -122
- package/dist/storage/sqliteSchema.js +1 -21
- package/dist/storage/sqliteStore.js +62 -47
- package/dist/storage/storageSchema.js +1 -8
- package/dist/storage/storageVersions.js +7 -4
- package/dist/storage/taskStore.js +8 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
- package/dist/task/completionReadiness.js +17 -98
- package/dist/task/deliveryGuard.js +9 -11
- package/dist/task/draftPlan.js +142 -0
- package/dist/task/nextAction.js +179 -477
- package/dist/task/publicationVerification.js +1 -0
- package/dist/task/remoteDelivery.js +233 -0
- package/dist/task/task.js +108 -4
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/turn/turn.js +109 -16
- package/dist/verification/verificationGateService.js +0 -26
- package/dist/web/assets/client/components.js +99 -121
- package/dist/web/assets/client/i18n.js +136 -58
- package/dist/web/assets/client/view.js +44 -0
- package/dist/web/assets/styles/cards.js +3 -15
- package/dist/web/assets/styles/widgets.js +4 -0
- package/dist/web/webSnapshot.js +29 -14
- package/dist/workItem/dependencyGate.js +34 -0
- package/dist/workItem/workItem.js +99 -266
- package/dist/workspace/workItemChangeSetManager.js +15 -59
- package/docs/task-local-identity.md +1 -1
- package/i18n/README.zh-CN.md +164 -58
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +272 -782
- package/skills/yui-operator/SKILL.md +190 -365
- package/skills/yui-reviewer/SKILL.md +40 -33
- package/skills/yui-runtime/SKILL.md +22 -0
- package/skills/yui-worker/SKILL.md +19 -6
- package/dist/execution/candidateConvergence.js +0 -623
- package/dist/execution/executionGroup.js +0 -854
- package/dist/execution/resourceBroker.js +0 -426
- package/dist/executor/launchPlan.js +0 -16
- package/dist/review/reviewFinding.js +0 -240
- package/dist/review/reviewFindingLedger.js +0 -550
- package/dist/review/reviewOutcomeClassifier.js +0 -255
- package/dist/scheduler/resourceQueueProjection.js +0 -71
- package/dist/task/repairWave.js +0 -150
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { normalizedUniqueIdentities, normalizedUniqueText, requireIdentity,
|
|
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 {
|
|
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:
|
|
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
|
|
142
|
-
throw new Error(`Work Item already has an
|
|
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.
|
|
164
|
-
throw new Error(`Work Item ExecutionGroup
|
|
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
|
-
|
|
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
|
|
220
|
-
// the current pointer;
|
|
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
|
|
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
|
-
|
|
252
|
-
|
|
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
|
|
513
|
-
&& group.lanes.some(({ id,
|
|
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
|
-
|
|
623
|
-
if (group.taskId !== taskId
|
|
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.
|
|
627
|
-
|| group.
|
|
628
|
-
|
|
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.
|
|
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
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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.
|