@zq-silk/yui 0.15.11 → 0.16.0
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 +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +92 -48
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +45 -25
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +355 -732
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +79 -90
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +6 -5
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- package/skills/yui-runtime/references/recovery.md +71 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -376
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { isDeepStrictEqual } from "node:util";
|
|
2
1
|
import { lstat } from "node:fs/promises";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { isArchivePersistenceFailure, recordArchiveCleanup } from "../task/archiveDiagnostics.js";
|
|
4
|
-
import {
|
|
4
|
+
import { archiveExecutionChecks, archiveSettlementChecks } from "../task/archivePreflight.js";
|
|
5
|
+
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
6
|
+
import { CleanupInspectionError } from "../workspace/cleanupInspection.js";
|
|
5
7
|
import { WorkItemChangeSetManager } from "../workspace/workItemChangeSetManager.js";
|
|
6
8
|
import { hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
7
9
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
@@ -85,8 +87,12 @@ export class TaskWorkspaceCoordinator {
|
|
|
85
87
|
.map(({ projectId }) => projectId);
|
|
86
88
|
const releaseMaintenance = projectIds.length === 0
|
|
87
89
|
? () => { }
|
|
88
|
-
: acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
90
|
+
: await acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
89
91
|
try {
|
|
92
|
+
if (!isDeepStrictEqual(this.store.getWorkItem(taskId, workItemId), item)
|
|
93
|
+
|| !isDeepStrictEqual(this.store.getWorkItemWorkspace(taskId, workItemId), workspace)) {
|
|
94
|
+
throw new Error(`Work item changed while waiting for cleanup: ${taskId}/${workItemId}.`);
|
|
95
|
+
}
|
|
90
96
|
const state = await this.preparer.inspectWorkItemWorkspace(item.taskId, item.id);
|
|
91
97
|
if (state === "dirty")
|
|
92
98
|
return "dirty";
|
|
@@ -96,7 +102,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
96
102
|
const laneCleanup = await this.preparer.cleanupExecutionLaneWorkspacesForWorkItem(item.taskId, item.id);
|
|
97
103
|
if (laneCleanup === "dirty")
|
|
98
104
|
return "dirty";
|
|
99
|
-
return this.preparer.cleanupWorkItemWorkspace(item.taskId, item.id, disposition);
|
|
105
|
+
return await this.preparer.cleanupWorkItemWorkspace(item.taskId, item.id, disposition);
|
|
100
106
|
}
|
|
101
107
|
finally {
|
|
102
108
|
releaseMaintenance();
|
|
@@ -137,8 +143,12 @@ export class TaskWorkspaceCoordinator {
|
|
|
137
143
|
.map(({ projectId }) => projectId);
|
|
138
144
|
const releaseMaintenance = projectIds.length === 0
|
|
139
145
|
? () => { }
|
|
140
|
-
: acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
146
|
+
: await acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
141
147
|
try {
|
|
148
|
+
if (!isDeepStrictEqual(this.store.getReviewRound(taskId, reviewRoundId), round)
|
|
149
|
+
|| !isDeepStrictEqual(this.store.getReviewRoundWorkspace(taskId, reviewRoundId), workspace)) {
|
|
150
|
+
throw new Error(`ReviewRound changed while waiting for cleanup: ${taskId}/${reviewRoundId}.`);
|
|
151
|
+
}
|
|
142
152
|
const state = await this.preparer.inspectReviewRoundWorkspace(taskId, reviewRoundId);
|
|
143
153
|
if (state === "dirty")
|
|
144
154
|
return "dirty";
|
|
@@ -146,7 +156,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
146
156
|
const laneCleanup = await this.preparer.cleanupExecutionLaneWorkspacesForReviewRound(taskId, reviewRoundId);
|
|
147
157
|
if (laneCleanup === "dirty")
|
|
148
158
|
return "dirty";
|
|
149
|
-
return this.preparer.cleanupReviewRoundWorkspace(taskId, reviewRoundId);
|
|
159
|
+
return await this.preparer.cleanupReviewRoundWorkspace(taskId, reviewRoundId);
|
|
150
160
|
}
|
|
151
161
|
finally {
|
|
152
162
|
releaseMaintenance();
|
|
@@ -161,6 +171,9 @@ export class TaskWorkspaceCoordinator {
|
|
|
161
171
|
if (task.status !== "completed" && task.status !== "cancelled") {
|
|
162
172
|
throw new Error(`Task must be completed or retired before archive cleanup: ${task.id}.`);
|
|
163
173
|
}
|
|
174
|
+
const settlement = [...archiveSettlementChecks(this.store, task), ...archiveExecutionChecks(this.store, taskId)];
|
|
175
|
+
if (settlement.length > 0)
|
|
176
|
+
throw new CleanupInspectionError(settlement);
|
|
164
177
|
const managedWorkspaces = [...this.store.listManagedWorkspaces(task.id)]
|
|
165
178
|
.sort((left, right) => managedWorkspaceKey(left.owner)
|
|
166
179
|
.localeCompare(managedWorkspaceKey(right.owner)));
|
|
@@ -172,7 +185,14 @@ export class TaskWorkspaceCoordinator {
|
|
|
172
185
|
for (const entry of workspace.entries)
|
|
173
186
|
projectIds.add(entry.projectId);
|
|
174
187
|
}
|
|
175
|
-
releaseMaintenance = acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
188
|
+
releaseMaintenance = await acquireProjectMaintenanceLocks(this.preparer.home, projectIds);
|
|
189
|
+
this.#assertTaskArchiveLifecycle(task);
|
|
190
|
+
const currentWorkspaces = [...this.store.listManagedWorkspaces(task.id)]
|
|
191
|
+
.sort((left, right) => managedWorkspaceKey(left.owner)
|
|
192
|
+
.localeCompare(managedWorkspaceKey(right.owner)));
|
|
193
|
+
if (!isDeepStrictEqual(currentWorkspaces, managedWorkspaces)) {
|
|
194
|
+
throw new Error(`Task workspaces changed while waiting for archive cleanup: ${taskId}.`);
|
|
195
|
+
}
|
|
176
196
|
const laneWorkspaces = managedWorkspaces.filter(({ owner }) => owner.type === "execution-lane");
|
|
177
197
|
const integrationWorkspaces = managedWorkspaces.filter(({ owner }) => owner.type === "integration-attempt");
|
|
178
198
|
const workspaces = managedWorkspaces
|
|
@@ -200,93 +220,22 @@ export class TaskWorkspaceCoordinator {
|
|
|
200
220
|
workItems: allWorkItems,
|
|
201
221
|
reviewRounds: allReviewRounds
|
|
202
222
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
reason: "dirty-worktree",
|
|
214
|
-
resource: `review-round:${task.id}/${round.id}`,
|
|
215
|
-
retryable: true
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
for (const item of workItems) {
|
|
220
|
-
if (await this.preparer.inspectWorkItemWorkspace(task.id, item.id) === "dirty") {
|
|
221
|
-
return {
|
|
222
|
-
taskId,
|
|
223
|
-
status: "retained-dirty",
|
|
224
|
-
...(task.cwd === undefined ? {} : { path: task.cwd }),
|
|
225
|
-
error: `WorkItem worktree is dirty: ${item.id}.`,
|
|
226
|
-
reason: "dirty-worktree",
|
|
227
|
-
resource: `work-item:${task.id}/${item.id}`,
|
|
228
|
-
retryable: true
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
for (const workspace of laneWorkspaces) {
|
|
233
|
-
if (workspace.owner.type !== "execution-lane")
|
|
234
|
-
continue;
|
|
235
|
-
if (await this.preparer.inspectExecutionLaneWorkspace(task.id, workspace.owner.executionGroupId, workspace.owner.executionLaneId) === "dirty") {
|
|
236
|
-
return {
|
|
237
|
-
taskId,
|
|
238
|
-
status: "retained-dirty",
|
|
239
|
-
...(task.cwd === undefined ? {} : { path: task.cwd }),
|
|
240
|
-
error: `Execution Lane worktree is dirty: ${managedWorkspaceKey(workspace.owner)}.`,
|
|
241
|
-
reason: "dirty-worktree",
|
|
242
|
-
resource: managedWorkspaceKey(workspace.owner),
|
|
243
|
-
retryable: true
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
for (const workspace of integrationWorkspaces) {
|
|
248
|
-
if (workspace.owner.type !== "integration-attempt")
|
|
249
|
-
continue;
|
|
250
|
-
const attempt = this.store.getIntegrationAttempt(task.id, workspace.owner.integrationAttemptId);
|
|
251
|
-
if (attempt === null
|
|
252
|
-
|| attempt.status === "running"
|
|
253
|
-
|| attempt.status === "blocked"
|
|
254
|
-
|| attempt.status === "conflicted"
|
|
255
|
-
|| attempt.status === "validating") {
|
|
256
|
-
throw new Error(`IntegrationAttempt must be terminal before archive cleanup: ${workspace.owner.integrationAttemptId}.`);
|
|
257
|
-
}
|
|
258
|
-
if (await this.preparer.inspectIntegrationWorkspace(task.id, attempt.id) === "dirty") {
|
|
259
|
-
return {
|
|
260
|
-
taskId,
|
|
261
|
-
status: "retained-dirty",
|
|
262
|
-
...(task.cwd === undefined ? {} : { path: task.cwd }),
|
|
263
|
-
error: `Integration worktree is dirty: ${attempt.id}.`,
|
|
264
|
-
reason: "dirty-worktree",
|
|
265
|
-
resource: `integration-attempt:${task.id}/${attempt.id}`,
|
|
266
|
-
retryable: true
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
const state = await this.preparer.inspectTaskMainWorkspace(taskId);
|
|
271
|
-
if (state === "dirty") {
|
|
272
|
-
return {
|
|
273
|
-
taskId,
|
|
274
|
-
status: "retained-dirty",
|
|
275
|
-
...(task.cwd === undefined ? {} : { path: task.cwd }),
|
|
276
|
-
error: `Task main worktree is dirty: ${task.id}.`,
|
|
277
|
-
reason: "dirty-worktree",
|
|
278
|
-
resource: `task-worktree:${task.id}`,
|
|
279
|
-
retryable: true
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
const activeRole = this.store.listRoles(taskId)
|
|
283
|
-
.find((role) => this.store.getActiveRun(taskId, role.name) !== null);
|
|
284
|
-
if (activeRole !== undefined) {
|
|
285
|
-
throw new WorkspaceCleanupBlockedError("active-turn", `role:${task.id}/${activeRole.name}`, true, `Task Role still has an active AgentRun: ${task.id}/${activeRole.name}.`);
|
|
223
|
+
const checks = [];
|
|
224
|
+
for (const workspace of managedWorkspaces) {
|
|
225
|
+
const workspaceDisposition = workspace.owner.type === "work-item"
|
|
226
|
+
&& this.store.getWorkItem(task.id, workspace.owner.workItemId)?.status === "retired" ? "abandoned" : disposition;
|
|
227
|
+
const inspected = await this.preparer.inspectWorkspaceCleanup(workspace, workspaceDisposition);
|
|
228
|
+
// Child worktrees are removed first below. Their Git registrations
|
|
229
|
+
// necessarily exist now; the clone's removal boundary rechecks them
|
|
230
|
+
// after every child cleanup and never removes an unresolved owner.
|
|
231
|
+
checks.push(...inspected.filter(c => !(workspace.owner.type === "task"
|
|
232
|
+
&& c.reason === "git-worktree-registrations")));
|
|
286
233
|
}
|
|
234
|
+
if (checks.length > 0)
|
|
235
|
+
throw new CleanupInspectionError(checks);
|
|
287
236
|
const roleNames = this.store.listRoles(taskId).map(({ name }) => name);
|
|
288
237
|
await this.#stopLiveRoles(taskId, roleNames);
|
|
289
|
-
await this.runtime.
|
|
238
|
+
await this.runtime.releaseTaskTerminals(task.id);
|
|
290
239
|
this.#assertTaskArchiveSnapshot(snapshot);
|
|
291
240
|
for (const workspace of laneWorkspaces) {
|
|
292
241
|
this.#assertTaskArchiveLifecycle(task);
|
|
@@ -297,7 +246,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
297
246
|
for (const workspace of integrationWorkspaces) {
|
|
298
247
|
this.#assertTaskArchiveLifecycle(task);
|
|
299
248
|
if (workspace.owner.type === "integration-attempt") {
|
|
300
|
-
await this.preparer.cleanupIntegrationWorkspace(task.id, workspace.owner.integrationAttemptId);
|
|
249
|
+
await this.preparer.cleanupIntegrationWorkspace(task.id, workspace.owner.integrationAttemptId, { preserveChanges: true });
|
|
301
250
|
}
|
|
302
251
|
}
|
|
303
252
|
for (const round of reviewRounds) {
|
|
@@ -309,26 +258,30 @@ export class TaskWorkspaceCoordinator {
|
|
|
309
258
|
await this.preparer.cleanupWorkItemWorkspace(task.id, item.id, item.status === "accepted" ? disposition : "abandoned");
|
|
310
259
|
}
|
|
311
260
|
this.#assertTaskArchiveLifecycle(task);
|
|
312
|
-
return this.preparer.cleanupTaskForArchive(taskId);
|
|
261
|
+
return await this.preparer.cleanupTaskForArchive(taskId, disposition);
|
|
313
262
|
}
|
|
314
263
|
catch (error) {
|
|
315
264
|
const task = this.store.getTask(taskId);
|
|
316
265
|
return {
|
|
317
266
|
taskId,
|
|
318
|
-
status: "
|
|
267
|
+
status: error instanceof CleanupInspectionError && error.checks.every(c => c.reason === "dirty-worktree")
|
|
268
|
+
? "retained-dirty" : "failed",
|
|
319
269
|
...(task?.cwd === undefined ? {} : { path: task.cwd }),
|
|
320
270
|
error: error instanceof Error ? error.message : String(error),
|
|
321
|
-
...(error instanceof
|
|
322
|
-
? {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
271
|
+
...(error instanceof CleanupInspectionError
|
|
272
|
+
? { checks: error.checks, reason: error.checks[0]?.reason ?? "cleanup-failed",
|
|
273
|
+
resource: error.checks[0]?.resource ?? `task:${taskId}`, retryable: true }
|
|
274
|
+
: error instanceof WorkspaceCleanupBlockedError
|
|
275
|
+
? {
|
|
276
|
+
reason: error.reason,
|
|
277
|
+
resource: error.resource,
|
|
278
|
+
retryable: error.retryable
|
|
279
|
+
}
|
|
280
|
+
: {
|
|
281
|
+
reason: "cleanup-failed",
|
|
282
|
+
resource: `task:${taskId}`,
|
|
283
|
+
retryable: true
|
|
284
|
+
})
|
|
332
285
|
};
|
|
333
286
|
}
|
|
334
287
|
finally {
|
|
@@ -368,15 +321,9 @@ export class TaskWorkspaceCoordinator {
|
|
|
368
321
|
for (const role of this.store.listRoles(taskId)) {
|
|
369
322
|
const released = await attempt({ resource: `role:${taskId}/${role.name}`,
|
|
370
323
|
detail: "Exact Role runtime stop." }, async () => {
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|| !["completed", "failed", "cancelled"].includes(provider.run.status))) {
|
|
375
|
-
throw new Error(`Original mailbox claim ${mailbox.processing.batchId} retained; execution outcome is not known.`);
|
|
376
|
-
}
|
|
377
|
-
if (provider?.run != null && !["completed", "failed", "cancelled"].includes(provider.run.status)) {
|
|
378
|
-
throw new Error(`Provider input ${provider.run.attemptId}/${provider.run.status} retained; establish exact quiescence before resolving it.`);
|
|
379
|
-
}
|
|
324
|
+
const checks = archiveExecutionChecks(this.store, taskId).filter(c => c.resource === `role:${taskId}/${role.name}`);
|
|
325
|
+
if (checks.length > 0)
|
|
326
|
+
throw new CleanupInspectionError(checks);
|
|
380
327
|
await this.#stopLiveRoles(taskId, [role.name]);
|
|
381
328
|
return "released";
|
|
382
329
|
});
|
|
@@ -384,17 +331,12 @@ export class TaskWorkspaceCoordinator {
|
|
|
384
331
|
}
|
|
385
332
|
const physicalReleased = await attempt({ resource: `runtime:${taskId}`,
|
|
386
333
|
detail: "Verify exact physical resource release before workspace deletion." }, async () => {
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
throw new Error(`Execution resources retained: ${activeRun?.id ?? job?.id}; archive is not proof of quiescence.`);
|
|
391
|
-
}
|
|
334
|
+
const checks = archiveExecutionChecks(this.store, taskId);
|
|
335
|
+
if (checks.length > 0)
|
|
336
|
+
throw new CleanupInspectionError(checks);
|
|
392
337
|
if (!runtimeReleased)
|
|
393
338
|
throw new Error("One or more Role runtimes could not be safely released.");
|
|
394
|
-
|
|
395
|
-
throw new Error("Physical resource release inspection is unavailable; workspace references retained.");
|
|
396
|
-
}
|
|
397
|
-
await this.runtime.assertTaskPhysicalResourcesReleased(taskId);
|
|
339
|
+
await this.runtime.releaseTaskTerminals(taskId);
|
|
398
340
|
return "released";
|
|
399
341
|
});
|
|
400
342
|
if (physicalReleased) {
|
|
@@ -406,7 +348,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
406
348
|
detail: "Remove only clean, exactly owned workspace resources.",
|
|
407
349
|
paths: [workspace.root, ...workspace.entries.filter(e => e.access === "write").map(e => e.path)]
|
|
408
350
|
}, async () => {
|
|
409
|
-
const release = acquireProjectMaintenanceLocks(this.preparer.home, workspace.entries.map(e => e.projectId));
|
|
351
|
+
const release = await acquireProjectMaintenanceLocks(this.preparer.home, workspace.entries.map(e => e.projectId));
|
|
410
352
|
try {
|
|
411
353
|
if (!isDeepStrictEqual(this.store.getManagedWorkspace(workspace.owner), workspace)) {
|
|
412
354
|
throw new Error("Workspace ownership changed; retained.");
|
|
@@ -418,16 +360,16 @@ export class TaskWorkspaceCoordinator {
|
|
|
418
360
|
if (item?.status === "accepted" && disposition === "integrated") {
|
|
419
361
|
await new WorkItemChangeSetManager(this.store).assertIntegrated(taskId, item.id);
|
|
420
362
|
}
|
|
421
|
-
return this.preparer.cleanupWorkItemWorkspace(taskId, owner.workItemId, item?.status === "retired" ? "abandoned" : disposition);
|
|
363
|
+
return await this.preparer.cleanupWorkItemWorkspace(taskId, owner.workItemId, item?.status === "retired" ? "abandoned" : disposition);
|
|
422
364
|
}
|
|
423
365
|
case "review-round":
|
|
424
|
-
return this.preparer.cleanupReviewRoundWorkspace(taskId, owner.reviewRoundId);
|
|
366
|
+
return await this.preparer.cleanupReviewRoundWorkspace(taskId, owner.reviewRoundId);
|
|
425
367
|
case "execution-lane":
|
|
426
|
-
return this.preparer.cleanupExecutionLaneWorkspace(taskId, owner.executionGroupId, owner.executionLaneId);
|
|
368
|
+
return await this.preparer.cleanupExecutionLaneWorkspace(taskId, owner.executionGroupId, owner.executionLaneId);
|
|
427
369
|
case "integration-attempt":
|
|
428
370
|
if (await this.preparer.inspectIntegrationWorkspace(taskId, owner.integrationAttemptId) === "dirty")
|
|
429
371
|
return "dirty";
|
|
430
|
-
return this.preparer.cleanupIntegrationWorkspace(taskId, owner.integrationAttemptId);
|
|
372
|
+
return await this.preparer.cleanupIntegrationWorkspace(taskId, owner.integrationAttemptId, { preserveChanges: true });
|
|
431
373
|
case "task": {
|
|
432
374
|
const current = this.store.getTask(taskId);
|
|
433
375
|
const delivery = projectTaskRemoteDeliveryFromStore(this.store, current);
|
|
@@ -435,7 +377,8 @@ export class TaskWorkspaceCoordinator {
|
|
|
435
377
|
throw new Error("Task main retained: local commits are not covered by verified remote delivery. Force does not discard them.");
|
|
436
378
|
}
|
|
437
379
|
for (const entry of workspace.entries.filter(e => e.access === "write")) {
|
|
438
|
-
const
|
|
380
|
+
const covered = delivery.projects.find(p => p.projectId === entry.projectId);
|
|
381
|
+
const expected = covered?.expectedLocalCommit;
|
|
439
382
|
const path = await lstat(entry.path).catch(error => {
|
|
440
383
|
if (error.code === "ENOENT")
|
|
441
384
|
return null;
|
|
@@ -444,11 +387,12 @@ export class TaskWorkspaceCoordinator {
|
|
|
444
387
|
if (expected !== null && expected !== undefined
|
|
445
388
|
&& path !== null) {
|
|
446
389
|
const actual = await this.preparer.git.inspect(entry.path, "HEAD");
|
|
447
|
-
if (actual.baseCommit !== expected)
|
|
390
|
+
if (actual.baseCommit !== expected && actual.baseCommit !== covered?.deliveryLocalCommit) {
|
|
448
391
|
throw new Error(`Task main HEAD changed: ${entry.path}; retained.`);
|
|
392
|
+
}
|
|
449
393
|
}
|
|
450
394
|
}
|
|
451
|
-
const result = await this.preparer.cleanupTaskForArchive(taskId);
|
|
395
|
+
const result = await this.preparer.cleanupTaskForArchive(taskId, disposition);
|
|
452
396
|
if (result.status !== "removed")
|
|
453
397
|
throw new Error(result.error ?? "Task main or dependent workspace retained.");
|
|
454
398
|
return "removed";
|
|
@@ -500,9 +444,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
500
444
|
* human/Leader has taken responsibility for the outcome.
|
|
501
445
|
*/
|
|
502
446
|
#assertNoActiveWorkItemDurableJobs(item) {
|
|
503
|
-
const jobs = this.store.listDurableJobs
|
|
504
|
-
if (jobs === undefined)
|
|
505
|
-
return;
|
|
447
|
+
const jobs = this.store.listDurableJobs(item.taskId);
|
|
506
448
|
const blocking = jobs.filter((job) => (job.owner.kind === "work-item"
|
|
507
449
|
&& job.owner.workItemId === item.id
|
|
508
450
|
&& (job.status === "queued"
|
|
@@ -523,18 +465,16 @@ export class TaskWorkspaceCoordinator {
|
|
|
523
465
|
#reviewRoundRoleNames(round) {
|
|
524
466
|
return [
|
|
525
467
|
round.reviewerRoleName,
|
|
526
|
-
...(round.executionGroup?.lanes.map(({ roleName }) => roleName) ?? []),
|
|
527
468
|
...(round.executionGroup?.lanes.map(({ roleName }) => roleName) ?? [])
|
|
528
469
|
];
|
|
529
470
|
}
|
|
530
471
|
async #stopLiveRoles(taskId, roleNames) {
|
|
531
472
|
const targets = [...new Set(roleNames)];
|
|
532
|
-
const getActiveRun = this.store.getActiveRun?.bind(this.store);
|
|
533
473
|
for (const roleName of targets) {
|
|
534
|
-
if (getActiveRun
|
|
474
|
+
if (this.store.getActiveRun(taskId, roleName) !== null) {
|
|
535
475
|
throw new Error(`Role has an active AgentRun: ${taskId}/${roleName}.`);
|
|
536
476
|
}
|
|
537
|
-
if (
|
|
477
|
+
if (hasRuntimeLifecycleWork(this.store.getWorkMailbox(runtimeLifecycleTarget({ scope: "task", taskId, roleName })))) {
|
|
538
478
|
throw new Error(`Role has unsettled runtime lifecycle state: ${taskId}/${roleName}.`);
|
|
539
479
|
}
|
|
540
480
|
}
|
|
@@ -551,11 +491,9 @@ export class TaskWorkspaceCoordinator {
|
|
|
551
491
|
});
|
|
552
492
|
if (live.length > 0)
|
|
553
493
|
await this.runtime.stopTaskRoleSessions(taskId, live);
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
|
|
557
|
-
// cannot reserve a AgentRun/Session between absence proof and terminalization.
|
|
558
|
-
if (inspect === undefined || this.store.transaction === undefined)
|
|
494
|
+
// Recheck exact runtime quiescence under the Task transaction when this
|
|
495
|
+
// runtime exposes physical panes. Store reads are always authoritative.
|
|
496
|
+
if (inspect === undefined)
|
|
559
497
|
return;
|
|
560
498
|
this.store.transaction((tx) => {
|
|
561
499
|
const panes = inspect(taskId);
|
|
@@ -566,7 +504,7 @@ export class TaskWorkspaceCoordinator {
|
|
|
566
504
|
if (tx.getActiveRun(taskId, roleName) !== null) {
|
|
567
505
|
throw new Error(`Role has an active AgentRun: ${taskId}/${roleName}.`);
|
|
568
506
|
}
|
|
569
|
-
if (
|
|
507
|
+
if (hasRuntimeLifecycleWork(tx.getWorkMailbox(runtimeLifecycleTarget({ scope: "task", taskId, roleName })))) {
|
|
570
508
|
throw new Error(`Role has unsettled runtime lifecycle state: ${taskId}/${roleName}.`);
|
|
571
509
|
}
|
|
572
510
|
}
|