@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
|
@@ -16,23 +16,11 @@ export class StorageSchemaError extends Error {
|
|
|
16
16
|
/**
|
|
17
17
|
* Inspect the one authoritative SQLite migration head without changing it.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* version authorities for current Homes.
|
|
19
|
+
* Extra files never override the SQLite authority. A missing database in a
|
|
20
|
+
* non-empty Home is unverified, not permission to initialize over its evidence.
|
|
22
21
|
*/
|
|
23
22
|
export function inspectStorageSchema(rootDir) {
|
|
24
23
|
const databasePath = join(rootDir, CURRENT_DATABASE_FILENAME);
|
|
25
|
-
const hasPreBaselineEvidence = existsSync(join(rootDir, "schema.json")) || existsSync(join(rootDir, "state.json"));
|
|
26
|
-
if (hasPreBaselineEvidence) {
|
|
27
|
-
return {
|
|
28
|
-
status: "unsupported",
|
|
29
|
-
direction: "older",
|
|
30
|
-
currentVersion: 0,
|
|
31
|
-
latestVersion: CURRENT_STORAGE_VERSION,
|
|
32
|
-
minimumSupportedVersion: MIN_SUPPORTED_STORAGE_VERSION,
|
|
33
|
-
databasePath
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
24
|
if (!existsSync(databasePath)) {
|
|
37
25
|
if (existsSync(rootDir)) {
|
|
38
26
|
try {
|
package/dist/storage/storeRpc.js
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* - {@link AsyncTaskStore} ........ the async counterpart to `TaskStore`
|
|
10
10
|
* (design §6): every method returns a promise; the worker owns the
|
|
11
|
-
*
|
|
11
|
+
* worker-side connection. The Controller's separate synchronous scheduler
|
|
12
|
+
* connection is not routed through this interface.
|
|
12
13
|
* - {@link AsyncTaskStoreClient} .. the client: serializes requests, applies
|
|
13
14
|
* the storage idempotency/dialect rules on top of the shared bounded RPC
|
|
14
15
|
* (outbox §5.4, `AbortSignal` cancellation §3.1, restart + replay §3.1).
|
|
@@ -16,8 +17,8 @@
|
|
|
16
17
|
* `transactionAsync` ships an ordered batch that the worker runs inside one
|
|
17
18
|
* `BEGIN IMMEDIATE … COMMIT` (§3.2).
|
|
18
19
|
*
|
|
19
|
-
* SQLite is the only product Store. `YUI_STORE_WORKER`
|
|
20
|
-
*
|
|
20
|
+
* SQLite is the only product Store. `YUI_STORE_WORKER` selects the observer
|
|
21
|
+
* execution boundary; it does not select another authority.
|
|
21
22
|
*/
|
|
22
23
|
import { BoundedRpcClient, nextRequestId } from "../core/boundedRpc.js";
|
|
23
24
|
import { StorageCancelledError, StorageConflictError, StorageRecordError } from "./taskStore.js";
|
|
@@ -51,6 +52,12 @@ const READ_ONLY_STORE_METHODS = new Set([
|
|
|
51
52
|
"getTask",
|
|
52
53
|
"readNextActionFacts",
|
|
53
54
|
"readCompletionReadinessFacts",
|
|
55
|
+
"queryContextRecords",
|
|
56
|
+
"latestEventSequence",
|
|
57
|
+
"listEventsByType",
|
|
58
|
+
"listTaskRunWorkspaceBases",
|
|
59
|
+
"contextInputReferences",
|
|
60
|
+
"listActiveRuns",
|
|
54
61
|
"listActiveTaskIds",
|
|
55
62
|
"listPlanningDraftTaskIds",
|
|
56
63
|
"listPendingActivationRequestTaskIds",
|
|
@@ -72,6 +79,7 @@ const READ_ONLY_STORE_METHODS = new Set([
|
|
|
72
79
|
"getRoleSessionSet",
|
|
73
80
|
"getTaskRoleSessionSet",
|
|
74
81
|
"listRoleSessionSets",
|
|
82
|
+
"listProviderRetrySessions",
|
|
75
83
|
"listRuntimeSessionCandidates",
|
|
76
84
|
"getRoleSession",
|
|
77
85
|
"getWorkItem",
|
|
@@ -167,12 +175,8 @@ function storageProtocol(home, options) {
|
|
|
167
175
|
* RPCs) plus {@link transactionAsync}, {@link invokeObserver}, and {@link close}.
|
|
168
176
|
*/
|
|
169
177
|
export class AsyncTaskStoreClient {
|
|
170
|
-
#home;
|
|
171
|
-
#options;
|
|
172
178
|
#rpc;
|
|
173
179
|
constructor(home, options = {}) {
|
|
174
|
-
this.#home = home;
|
|
175
|
-
this.#options = options;
|
|
176
180
|
this.#rpc = new BoundedRpcClient(storageProtocol(home, options), {
|
|
177
181
|
maxInFlight: options.maxInFlight,
|
|
178
182
|
maxQueue: options.maxQueue,
|
|
@@ -217,6 +221,11 @@ export class AsyncTaskStoreClient {
|
|
|
217
221
|
close() {
|
|
218
222
|
return this.#rpc.close();
|
|
219
223
|
}
|
|
224
|
+
/** Best-effort diagnostic batch on this worker, without a Task outbox entry. */
|
|
225
|
+
async flushTelemetry(entries, runCap) {
|
|
226
|
+
const requestId = nextRequestId();
|
|
227
|
+
await this.#rpc.send(requestId, { kind: "telemetry", requestId, entries, runCap });
|
|
228
|
+
}
|
|
220
229
|
/** Currently in-flight requests (metrics/tests). */
|
|
221
230
|
get inFlight() {
|
|
222
231
|
return this.#rpc.inFlight;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Current facts, not another persisted projection or execution status. */
|
|
2
|
+
export const CATALOG_ATTENTION = [
|
|
3
|
+
"openInputs", "pendingOperations", "unknownOperations", "executionSignals"
|
|
4
|
+
];
|
|
5
|
+
/**
|
|
6
|
+
* All filtering/counting stays in SQLite. Only page-sized rows and fixed
|
|
7
|
+
* attention samples cross into JS; no Task/Run/Message/Event body is decoded.
|
|
8
|
+
* The existing catalog and record tables remain the only stored authorities.
|
|
9
|
+
*/
|
|
10
|
+
export function queryTaskCatalog(db, query) {
|
|
11
|
+
const scope = ["1=1"];
|
|
12
|
+
const params = { limit: query.limit };
|
|
13
|
+
if (query.taskId !== undefined) {
|
|
14
|
+
scope.push("c.task_id = @taskId");
|
|
15
|
+
params.taskId = query.taskId;
|
|
16
|
+
}
|
|
17
|
+
if (!query.all)
|
|
18
|
+
scope.push("c.status <> 'archived'");
|
|
19
|
+
const match = ["1=1"];
|
|
20
|
+
if (query.status !== undefined) {
|
|
21
|
+
match.push("status = @status");
|
|
22
|
+
params.status = query.status;
|
|
23
|
+
}
|
|
24
|
+
if (query.project !== undefined) {
|
|
25
|
+
match.push(`EXISTS (SELECT 1 FROM task_records r, json_each(r.payload, '$.projectBindings') p
|
|
26
|
+
WHERE r.task_id = facts.id AND json_extract(p.value, '$.projectId') = @project)`);
|
|
27
|
+
params.project = query.project;
|
|
28
|
+
}
|
|
29
|
+
if (query.search) {
|
|
30
|
+
match.push(`EXISTS (SELECT 1 FROM task_records r WHERE r.task_id = facts.id AND
|
|
31
|
+
(instr(lower(json_extract(r.payload, '$.title')), lower(@search)) > 0
|
|
32
|
+
OR instr(lower(r.task_id), lower(@search)) > 0
|
|
33
|
+
OR EXISTS (SELECT 1 FROM json_each(r.payload, '$.tags') tag
|
|
34
|
+
WHERE instr(lower(tag.value), lower(@search)) > 0)
|
|
35
|
+
OR EXISTS (SELECT 1 FROM json_each(r.payload, '$.projectBindings') b
|
|
36
|
+
JOIN projects p ON p.id = json_extract(b.value, '$.projectId')
|
|
37
|
+
WHERE instr(lower(json_extract(p.payload, '$.name')), lower(@search)) > 0)))`);
|
|
38
|
+
params.search = query.search;
|
|
39
|
+
}
|
|
40
|
+
if (query.attention !== undefined)
|
|
41
|
+
match.push(`${query.attention} > 0`);
|
|
42
|
+
const position = (name, op) => {
|
|
43
|
+
const value = query[name];
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
return;
|
|
46
|
+
params[`${name}Date`] = value.createdAt;
|
|
47
|
+
params[`${name}Id`] = value.id;
|
|
48
|
+
match.push(`(createdAt, id) ${op} (@${name}Date, @${name}Id)`);
|
|
49
|
+
};
|
|
50
|
+
position("through", "<=");
|
|
51
|
+
// A fixed upper key excludes newly-created Tasks on later pages. Updates
|
|
52
|
+
// to mutable filters are current reads, not a snapshot/session protocol.
|
|
53
|
+
const after = [];
|
|
54
|
+
if (query.after !== undefined) {
|
|
55
|
+
params.afterDate = query.after.createdAt;
|
|
56
|
+
params.afterId = query.after.id;
|
|
57
|
+
after.push("(createdAt, id) > (@afterDate, @afterId)");
|
|
58
|
+
}
|
|
59
|
+
const count = (table, condition) => `(SELECT count(*) FROM ${table} x WHERE x.task_id = c.task_id AND (${condition}))`;
|
|
60
|
+
// Execution signals deliberately preserve raw inspectable conditions:
|
|
61
|
+
// live runs (including unknown admission/identity/stall), unresolved work,
|
|
62
|
+
// integrations, and pending Leader recovery. They are NOT a second
|
|
63
|
+
// classifier for working/blocked/success; precise execution stays in detail.
|
|
64
|
+
// Including all live runs is conservative: an off-page runtime attention
|
|
65
|
+
// cannot disappear just because a lightweight read did not fold its history.
|
|
66
|
+
const cte = `WITH facts AS MATERIALIZED (
|
|
67
|
+
SELECT c.task_id AS id, c.created_at AS createdAt, c.updated_at AS updatedAt, c.status,
|
|
68
|
+
${count("work_items", "1=1")} AS workItems,
|
|
69
|
+
${count("turns", "status = 'active'")} AS activeRuns,
|
|
70
|
+
${count("input_requests", "status = 'open'")} AS openInputs,
|
|
71
|
+
${count("durable_jobs", "status IN ('queued','running')")} AS pendingOperations,
|
|
72
|
+
${count("durable_jobs", "status = 'unknown-needs-attention'")} AS unknownOperations,
|
|
73
|
+
CASE WHEN c.status IN ('active','draft') THEN
|
|
74
|
+
${count("turns", "status = 'active'")} +
|
|
75
|
+
${count("work_items", "status = 'open' OR (status = 'accepted' AND json_extract(payload, '$.currentExecutionGroupId') IS NOT NULL)")} +
|
|
76
|
+
${count("integration_attempts", "status IN ('running','validating','blocked','conflicted','failed')")} +
|
|
77
|
+
${count("review_rounds", "status IN ('pending','running','failed')")} +
|
|
78
|
+
${count("task_projections", "kind = 'leader-failure' AND payload IS NOT NULL AND payload <> 'null'")} +
|
|
79
|
+
${count("mailboxes", "role_name = 'leader' AND (json_type(pending) = 'object' OR json_type(processing) = 'object')")}
|
|
80
|
+
ELSE 0 END AS executionSignals
|
|
81
|
+
FROM tasks_catalog c WHERE ${scope.join(" AND ")}
|
|
82
|
+
), matching AS MATERIALIZED (SELECT * FROM facts WHERE ${match.join(" AND ")})
|
|
83
|
+
SELECT
|
|
84
|
+
(SELECT json_group_array(json_object('status',status,'count',n))
|
|
85
|
+
FROM (SELECT status,count(*) n FROM facts GROUP BY status)) AS counts,
|
|
86
|
+
(SELECT count(*) FROM matching) AS total,
|
|
87
|
+
(SELECT json_object('id',id,'createdAt',createdAt) FROM matching
|
|
88
|
+
ORDER BY createdAt DESC,id DESC LIMIT 1) AS upper,
|
|
89
|
+
${CATALOG_ATTENTION.map(kind => `
|
|
90
|
+
(SELECT sum(${kind}) FROM facts) AS ${kind}Count,
|
|
91
|
+
(SELECT count(*) FROM facts WHERE ${kind} > 0) AS ${kind}Tasks,
|
|
92
|
+
(SELECT json_group_array(id) FROM (SELECT id FROM facts WHERE ${kind} > 0
|
|
93
|
+
ORDER BY createdAt,id LIMIT 4)) AS ${kind}Ids`).join(",")},
|
|
94
|
+
(SELECT json_group_array(json_object(
|
|
95
|
+
'id',page.id,'createdAt',page.createdAt,'updatedAt',page.updatedAt,'status',page.status,
|
|
96
|
+
'title',substr(json_extract(r.payload,'$.title'),1,256),
|
|
97
|
+
'summary',substr(json_extract(r.brief,'$.leaderSummary'),1,512),
|
|
98
|
+
'summaryPresent',json(CASE WHEN r.brief IS NULL THEN 'false' ELSE 'true' END),
|
|
99
|
+
'workItems',page.workItems,'activeRuns',page.activeRuns,
|
|
100
|
+
'openInputs',page.openInputs,'pendingOperations',page.pendingOperations,
|
|
101
|
+
'unknownOperations',page.unknownOperations,'executionSignals',page.executionSignals)
|
|
102
|
+
ORDER BY page.createdAt,page.id)
|
|
103
|
+
FROM (SELECT * FROM matching ${after.length ? `WHERE ${after.join(" AND ")}` : ""}
|
|
104
|
+
ORDER BY createdAt,id LIMIT @limit) page
|
|
105
|
+
JOIN task_records r ON r.task_id = page.id) AS rows`;
|
|
106
|
+
const result = db.prepare(cte).get(params);
|
|
107
|
+
const counts = { draft: 0, active: 0, completed: 0, cancelled: 0, archived: 0, total: 0 };
|
|
108
|
+
for (const entry of JSON.parse(result.counts)) {
|
|
109
|
+
counts[entry.status] = entry.count;
|
|
110
|
+
counts.total += entry.count;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
rows: JSON.parse(result.rows),
|
|
114
|
+
total: result.total,
|
|
115
|
+
through: result.upper === null ? null : JSON.parse(result.upper),
|
|
116
|
+
counts,
|
|
117
|
+
attention: Object.fromEntries(CATALOG_ATTENTION.map(kind => [kind, {
|
|
118
|
+
count: Number(result[`${kind}Count`] ?? 0),
|
|
119
|
+
taskCount: Number(result[`${kind}Tasks`] ?? 0),
|
|
120
|
+
taskIds: JSON.parse(result[`${kind}Ids`])
|
|
121
|
+
}]))
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -2,12 +2,12 @@ import { lstatSync, mkdirSync, realpathSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { resolveTimeZone } from "../config/timeZone.js";
|
|
6
|
+
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
5
7
|
import { validateCapabilityGrant } from "../grant/capabilityGrant.js";
|
|
6
8
|
import { validateReleaseWorkflow } from "../release/releaseWorkflow.js";
|
|
7
|
-
import { validatePublicationReference } from "../task/publicationReference.js";
|
|
8
|
-
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetRuns, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
9
|
-
import { resolveTimeZone } from "../output/timePresentation.js";
|
|
10
9
|
import { validateReviewConfig } from "../review/reviewConfig.js";
|
|
10
|
+
import { validatePublicationReference } from "../task/publicationReference.js";
|
|
11
11
|
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
12
12
|
export const CURRENT_CONFIG_SCHEMA_VERSION = 6;
|
|
13
13
|
/** Current SQLite payload-family versions owned by this storage boundary. */
|
|
@@ -151,12 +151,10 @@ export function validateYuiConfig(config) {
|
|
|
151
151
|
"resourcesGcMode",
|
|
152
152
|
"resourcesGcAutoQuarantine",
|
|
153
153
|
"review",
|
|
154
|
-
"leaderNextActionMode",
|
|
155
154
|
"runtimeHealth",
|
|
156
155
|
"controllerTaskConcurrency",
|
|
157
156
|
"agentLaunchInactivityTimeoutSeconds",
|
|
158
157
|
"deliveryTimeoutSeconds",
|
|
159
|
-
"leaderSemanticBudgetRuns",
|
|
160
158
|
"resourcesQuarantineTtlHours",
|
|
161
159
|
"tmuxBin",
|
|
162
160
|
"tmuxHistoryLimit",
|
|
@@ -176,7 +174,6 @@ export function validateYuiConfig(config) {
|
|
|
176
174
|
resolveTimeZone(config.timeZone);
|
|
177
175
|
if (config.review !== undefined)
|
|
178
176
|
validateReviewConfig(config.review);
|
|
179
|
-
resolveLeaderNextActionMode(config.leaderNextActionMode);
|
|
180
177
|
resolveResourcesGcMode(config.resourcesGcMode);
|
|
181
178
|
resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine);
|
|
182
179
|
resolveResourcesQuarantineTtlHours(config.resourcesQuarantineTtlHours);
|
|
@@ -184,7 +181,6 @@ export function validateYuiConfig(config) {
|
|
|
184
181
|
resolveControllerTaskConcurrency(config.controllerTaskConcurrency);
|
|
185
182
|
resolveAgentLaunchInactivityTimeoutSeconds(config.agentLaunchInactivityTimeoutSeconds);
|
|
186
183
|
resolveDeliveryTimeoutSeconds(config.deliveryTimeoutSeconds);
|
|
187
|
-
resolveLeaderSemanticBudgetRuns(config.leaderSemanticBudgetRuns);
|
|
188
184
|
resolveTmuxBin(config.tmuxBin);
|
|
189
185
|
resolveTmuxHistoryLimit(config.tmuxHistoryLimit);
|
|
190
186
|
resolveTelemetryEnabled(config.telemetryEnabled);
|
|
@@ -448,6 +444,8 @@ export function storedPublicationReference(value) {
|
|
|
448
444
|
fields.push("targetBranch");
|
|
449
445
|
if (reference.localCommit !== undefined)
|
|
450
446
|
fields.push("localCommit");
|
|
447
|
+
if (reference.headCommit !== undefined)
|
|
448
|
+
fields.push("headCommit");
|
|
451
449
|
if (reference.remoteCommit !== undefined)
|
|
452
450
|
fields.push("remoteCommit");
|
|
453
451
|
if (reference.evidence !== undefined)
|
|
@@ -4,16 +4,14 @@ import Database from "better-sqlite3";
|
|
|
4
4
|
import { inspectAgentHostCompatibility, describeAgentHostUpgradeBlockers } from "../../runtime/agentHostCompatibility.js";
|
|
5
5
|
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation } from "../../runtime/runtimeObservation.js";
|
|
6
6
|
import { RUNTIME_PROCESS_EXIT_TASK_EVENT, validateRuntimeProcessExitObservation } from "../../runtime/processExitObservation.js";
|
|
7
|
-
import { validateAgentProfile } from "../../profile/agentProfile.js";
|
|
8
|
-
import { validateRoleSessionSet } from "../../executor/agentExecutor.js";
|
|
9
|
-
import { validateReviewRound } from "../../review/reviewRound.js";
|
|
10
|
-
import { validateRun } from "../../agentRun/agentRun.js";
|
|
11
|
-
import { validateDurableJob } from "../../job/durableJob.js";
|
|
12
|
-
import { validateWorkItem } from "../../workItem/workItem.js";
|
|
13
7
|
import { SqliteTaskStore } from "../sqliteStore.js";
|
|
14
8
|
import { storageBackupRoot } from "../homeLayout.js";
|
|
15
9
|
import { preflightUnifyHomeLayout } from "../migrations/unifyHomeLayout.js";
|
|
16
10
|
import { preflightCollapseWorktreeLayout } from "../migrations/collapseWorktreeLayout.js";
|
|
11
|
+
import { preflightNotificationOnlyWakes } from "../migrations/notificationOnlyWakes.js";
|
|
12
|
+
import { preflightVerificationPolicy } from "../migrations/verificationPolicy.js";
|
|
13
|
+
import { preflightCurrentInputContract } from "../migrations/currentInputContract.js";
|
|
14
|
+
import { preflightCurrentRuntimeContract } from "../migrations/currentRuntimeContract.js";
|
|
17
15
|
import { migrateSqliteSchema, storageMigrationPlan } from "../sqliteSchema.js";
|
|
18
16
|
import { CURRENT_DATABASE_FILENAME, inspectStorageSchema } from "../storageSchema.js";
|
|
19
17
|
import { CURRENT_STORAGE_VERSION, MIN_SUPPORTED_STORAGE_VERSION } from "../storageVersions.js";
|
|
@@ -153,7 +151,6 @@ export async function runStorageUpgrade(options) {
|
|
|
153
151
|
database.close();
|
|
154
152
|
}
|
|
155
153
|
validateCurrentStore(options.home);
|
|
156
|
-
rmSync(join(options.home, "schema.json"), { force: true });
|
|
157
154
|
}
|
|
158
155
|
catch (error) {
|
|
159
156
|
const restoration = migrationCommitted
|
|
@@ -206,29 +203,8 @@ export async function runStorageUpgrade(options) {
|
|
|
206
203
|
function validateCurrentStore(home) {
|
|
207
204
|
const store = new SqliteTaskStore(home);
|
|
208
205
|
try {
|
|
209
|
-
store.
|
|
210
|
-
for (const profile of store.listAgentProfiles())
|
|
211
|
-
validateAgentProfile(profile);
|
|
212
|
-
for (const sessions of store.listGlobalRoleSessionSets()) {
|
|
213
|
-
validateRoleSessionSet(sessions);
|
|
214
|
-
}
|
|
215
|
-
for (const owner of store.listSessionOwners()) {
|
|
216
|
-
if (owner.schemaVersion !== 2 || Object.hasOwn(owner, "launchId")) {
|
|
217
|
-
throw new Error("Session owner runtime identity is invalid.");
|
|
218
|
-
}
|
|
219
|
-
}
|
|
206
|
+
store.validateCurrentRecords();
|
|
220
207
|
for (const taskId of store.listTasks().map(({ id }) => id)) {
|
|
221
|
-
for (const job of store.listDurableJobs(taskId))
|
|
222
|
-
validateDurableJob(job);
|
|
223
|
-
for (const item of store.listWorkItems(taskId))
|
|
224
|
-
validateWorkItem(item);
|
|
225
|
-
for (const round of store.listReviewRounds(taskId))
|
|
226
|
-
validateReviewRound(round);
|
|
227
|
-
for (const run of store.listRuns(taskId))
|
|
228
|
-
validateRun(run);
|
|
229
|
-
for (const sessions of store.listRoleSessionSets(taskId)) {
|
|
230
|
-
validateRoleSessionSet(sessions);
|
|
231
|
-
}
|
|
232
208
|
for (const event of store.listEvents(taskId)) {
|
|
233
209
|
if (event.type === RUNTIME_OBSERVATION_TASK_EVENT) {
|
|
234
210
|
createRuntimeObservation(JSON.parse(event.payload.observation ?? ""));
|
|
@@ -345,16 +321,17 @@ function blocked(classification, stage, message, action) {
|
|
|
345
321
|
};
|
|
346
322
|
}
|
|
347
323
|
/**
|
|
348
|
-
* Run each
|
|
324
|
+
* Run each data migration's registered READ-ONLY preflight against the
|
|
349
325
|
* current (pre-upgrade) database and, when any finds a blocker, return a
|
|
350
326
|
* `blocked` result the caller surfaces before touching the Controller or the
|
|
351
|
-
* Home. Returns `null` when the plan carries no
|
|
327
|
+
* Home. Returns `null` when the plan carries no registered readiness check,
|
|
352
328
|
* or when every preflight is clear.
|
|
353
329
|
*
|
|
354
|
-
* Both the
|
|
330
|
+
* Both the 23->24 unify and the 24->25 collapse migrations physically relocate
|
|
355
331
|
* managed worktrees and share the same blocker shape (in-flight Job, conflicting
|
|
356
332
|
* relocation target). When a Home is upgraded across both in one run, their
|
|
357
|
-
* blockers are aggregated so the operator sees
|
|
333
|
+
* blockers are aggregated so the operator sees their readiness problems together.
|
|
334
|
+
* Notification retirement separately rejects unsettled execution references.
|
|
358
335
|
*
|
|
359
336
|
* The database is opened read-only so the checks cannot mutate the authoritative
|
|
360
337
|
* store, and the handle is always closed. An unexpected failure to evaluate a
|
|
@@ -364,8 +341,11 @@ function blocked(classification, stage, message, action) {
|
|
|
364
341
|
function preflightMigrationBlockers(home, plan, classification) {
|
|
365
342
|
const relocatesUnify = plan.some((step) => step.name === "unify-home-layout");
|
|
366
343
|
const relocatesCollapse = plan.some((step) => step.name === "collapse-worktree-layout");
|
|
367
|
-
|
|
368
|
-
|
|
344
|
+
const retiresRunWakes = plan.some((step) => step.name === "notification-only-wakes");
|
|
345
|
+
const changesVerification = plan.some(step => step.name === "advisory-and-verification-policy");
|
|
346
|
+
const changesInput = plan.some(step => step.name === "current-input-contract");
|
|
347
|
+
const retiresRuntime = plan.some(step => step.name === "current-verification-and-owner-contract");
|
|
348
|
+
if (!relocatesUnify && !relocatesCollapse && !retiresRunWakes && !changesVerification && !changesInput && !retiresRuntime)
|
|
369
349
|
return null;
|
|
370
350
|
let blockers;
|
|
371
351
|
try {
|
|
@@ -379,6 +359,14 @@ function preflightMigrationBlockers(home, plan, classification) {
|
|
|
379
359
|
blockers.push(...preflightUnifyHomeLayout(database).blockers);
|
|
380
360
|
if (relocatesCollapse)
|
|
381
361
|
blockers.push(...preflightCollapseWorktreeLayout(database).blockers);
|
|
362
|
+
if (retiresRunWakes)
|
|
363
|
+
preflightNotificationOnlyWakes(database);
|
|
364
|
+
if (changesVerification)
|
|
365
|
+
preflightVerificationPolicy(database);
|
|
366
|
+
if (changesInput)
|
|
367
|
+
preflightCurrentInputContract(database);
|
|
368
|
+
if (retiresRuntime)
|
|
369
|
+
preflightCurrentRuntimeContract(database);
|
|
382
370
|
}
|
|
383
371
|
finally {
|
|
384
372
|
database.close();
|
|
@@ -386,8 +374,8 @@ function preflightMigrationBlockers(home, plan, classification) {
|
|
|
386
374
|
}
|
|
387
375
|
catch (error) {
|
|
388
376
|
return {
|
|
389
|
-
...blocked(classification, "in-flight", `The storage migration readiness check could not be completed: ${messageOf(error)}`, "Resolve the
|
|
390
|
-
+ "
|
|
377
|
+
...blocked(classification, "in-flight", `The storage migration readiness check could not be completed: ${messageOf(error)}`, "Resolve the named execution or resource boundary, preserving original input and results. "
|
|
378
|
+
+ "Confirm the relevant Runs and Jobs are settled, then rerun the upgrade.")
|
|
391
379
|
};
|
|
392
380
|
}
|
|
393
381
|
if (blockers.length === 0)
|
|
@@ -122,6 +122,7 @@ export function taskArchiveDiagnostics(store, task) {
|
|
|
122
122
|
export function renderArchiveDiagnostics(data) {
|
|
123
123
|
return [
|
|
124
124
|
`Archived: ${data.archived}; forced: ${data.forced}; cleanup finished: ${data.cleanupFinished}`,
|
|
125
|
+
`Retained references: ${data.retainedResources.length}; a finished cleanup attempt is not physical resource release evidence.`,
|
|
125
126
|
...data.warnings.map(w => `Warning [${w.resource}]: ${w.detail}`),
|
|
126
127
|
...data.retainedResources.map(r => `Retained [${r.resource}]: ${r.detail}${r.paths ? ` (${r.paths.join(", ")})` : ""}`)
|
|
127
128
|
].join("\n") + "\n";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { readArchiveDelivery } from "../repository/workspaceCleanupInspection.js";
|
|
2
|
+
import { WorkspaceCleanupBlockedError } from "../repository/taskWorkspacePreparer.js";
|
|
3
|
+
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
4
|
+
import { hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
5
|
+
import { cleanupCheckFromError, renderCleanupCheck } from "../workspace/cleanupInspection.js";
|
|
6
|
+
/** Semantic admission facts. Cleanup still has its own resource boundary. */
|
|
7
|
+
export function archiveSettlementChecks(store, task) {
|
|
8
|
+
const checks = [];
|
|
9
|
+
const add = (resource, reason, detail, expected, observed, action) => checks.push({ resource, reason, status: "blocked", detail, expected, observed, sources: [resource], actions: [action] });
|
|
10
|
+
for (const input of store.listInputRequests(task.id).filter(i => i.status === "open")) {
|
|
11
|
+
add(`input:${task.id}/${input.id}`, "unresolved-input", "Open user input is not answered by archive.", "resolved input", input.status, `yui task input show ${task.id}/${input.id}`);
|
|
12
|
+
}
|
|
13
|
+
for (const item of store.listWorkItems(task.id).filter(i => i.status === "open")) {
|
|
14
|
+
add(`work-item:${task.id}/${item.id}`, "owner-unsettled", "WorkItem must be accepted or explicitly retired before archive.", ["accepted", "retired"], item.status, `yui task work show ${task.id}/${item.id}`);
|
|
15
|
+
}
|
|
16
|
+
for (const attempt of store.listIntegrationAttempts(task.id).filter(a => ["running", "blocked", "conflicted", "validating"].includes(a.status))) {
|
|
17
|
+
add(`integration-attempt:${task.id}/${attempt.id}`, "unresolved-integration", "Task has an unresolved Integration Attempt.", "terminal attempt", attempt.status, `yui task integration show ${task.id}/${attempt.id}`);
|
|
18
|
+
}
|
|
19
|
+
for (const job of store.listDurableJobs(task.id).filter(j => ["queued", "running"].includes(j.status) || (j.status === "unknown-needs-attention" && j.acknowledgedAt === undefined))) {
|
|
20
|
+
add(`job:${task.id}/${job.id}`, "active-durable-job", "Task has an active or unacknowledged DurableJob.", "settled job", job.status, `yui job get --task ${task.id} --job ${job.id}`);
|
|
21
|
+
}
|
|
22
|
+
return checks;
|
|
23
|
+
}
|
|
24
|
+
/** Unknown execution is never permission to remove workspaces, even after
|
|
25
|
+
* force admission. These facts are reloaded in the actual force cleanup path.
|
|
26
|
+
*/
|
|
27
|
+
export function archiveExecutionChecks(store, taskId) {
|
|
28
|
+
const checks = [];
|
|
29
|
+
const add = (resource, reason, detail, observed, action) => checks.push({ resource, reason, status: "unknown", detail, expected: "settled execution",
|
|
30
|
+
observed, sources: [resource], actions: [action] });
|
|
31
|
+
for (const run of store.listRuns(taskId).filter(r => r.status === "active")) {
|
|
32
|
+
add(`run:${taskId}/${run.id}`, "active-turn", "AgentRun remains active; archive is not stop evidence.", run.status, `yui task run show ${taskId}/${run.id}`);
|
|
33
|
+
}
|
|
34
|
+
for (const job of store.listDurableJobs(taskId).filter(j => ["queued", "running", "unknown-needs-attention"].includes(j.status))) {
|
|
35
|
+
add(`job:${taskId}/${job.id}`, "unresolved-execution", "DurableJob may still hold resources; acknowledgement is not physical exit evidence.", job.status, `yui job get --task ${taskId} --job ${job.id}`);
|
|
36
|
+
}
|
|
37
|
+
for (const role of store.listRoles(taskId)) {
|
|
38
|
+
const resource = `role:${taskId}/${role.name}`;
|
|
39
|
+
const action = `yui task role session inspect ${taskId} ${role.name}`;
|
|
40
|
+
const provider = store.getTaskRoleSessionSet(taskId, role.name)?.providerBinding;
|
|
41
|
+
const mailbox = store.getWorkMailbox({ kind: "role", taskId, roleName: role.name });
|
|
42
|
+
if (provider?.run != null && !["completed", "failed", "cancelled"].includes(provider.run.status)) {
|
|
43
|
+
add(resource, "unresolved-provider-input", "Original provider input outcome is unresolved; do not replay it.", { attemptId: provider.run.attemptId, status: provider.run.status }, action);
|
|
44
|
+
}
|
|
45
|
+
if (mailbox?.processing != null && (provider?.run == null
|
|
46
|
+
|| !["completed", "failed", "cancelled"].includes(provider.run.status))) {
|
|
47
|
+
add(resource, "unresolved-mailbox", "Original mailbox claim has no proven terminal; archive does not acknowledge it.", { batchId: mailbox.processing.batchId }, action);
|
|
48
|
+
}
|
|
49
|
+
if (hasRuntimeLifecycleWork(store.getWorkMailbox(runtimeLifecycleTarget({ scope: "task", taskId, roleName: role.name })))) {
|
|
50
|
+
add(resource, "unresolved-runtime-lifecycle", "Role has unsettled runtime lifecycle work.", "pending", action);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return checks;
|
|
54
|
+
}
|
|
55
|
+
export async function inspectTaskArchive(coordinator, request) {
|
|
56
|
+
const { store, preparer, runtime } = coordinator;
|
|
57
|
+
const task = store.getTask(request.taskId);
|
|
58
|
+
if (task === null)
|
|
59
|
+
throw new Error(`Task not found: ${request.taskId}.`);
|
|
60
|
+
const settlement = archiveSettlementChecks(store, task);
|
|
61
|
+
const delivery = await readArchiveDelivery(store, preparer.git, task, request.force);
|
|
62
|
+
const deliveryChecks = delivery.projects.filter(p => p.codeDelivery !== "none" && (!p.merged || !p.verified))
|
|
63
|
+
.map(p => ({ resource: `delivery:${task.id}/${p.projectId}`, reason: "delivery-coverage", status: "blocked",
|
|
64
|
+
detail: p.reason, expected: { merged: true, verified: true, localCommit: p.expectedLocalCommit },
|
|
65
|
+
observed: { coverage: p.coverage, merged: p.merged, verified: p.verified, localCommit: p.publication?.localCommit ?? null },
|
|
66
|
+
sources: [`task:${task.id}`, ...(p.publication === null ? [] : [`publication:${task.id}/${p.publication.id}`])],
|
|
67
|
+
actions: [`yui task remote-delivery ${task.id}`] }));
|
|
68
|
+
const execution = archiveExecutionChecks(store, task.id);
|
|
69
|
+
const runtimeResource = `runtime:${task.id}`;
|
|
70
|
+
if (runtime.assertTaskPhysicalResourcesReleased === undefined) {
|
|
71
|
+
execution.push(...cleanupCheckFromError(null, runtimeResource, [`task:${task.id}`], [`yui task role status ${task.id}`]));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
try {
|
|
75
|
+
await runtime.assertTaskPhysicalResourcesReleased(task.id);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
execution.push({ resource: runtimeResource, reason: "physical-resources-unreleased", status: "unknown",
|
|
79
|
+
detail: error instanceof WorkspaceCleanupBlockedError ? error.message
|
|
80
|
+
: "Exact physical resource release is not established by the runtime inspection.",
|
|
81
|
+
expected: "proven physical absence",
|
|
82
|
+
observed: error instanceof WorkspaceCleanupBlockedError ? { reason: error.reason, resource: error.resource } : "unavailable",
|
|
83
|
+
sources: [`task:${task.id}`],
|
|
84
|
+
actions: [`yui task role status ${task.id}`, `yui session reconcile --report`] });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const resources = [];
|
|
88
|
+
for (const workspace of store.listManagedWorkspaces(task.id)) {
|
|
89
|
+
const disposition = workspace.owner.type === "work-item"
|
|
90
|
+
&& store.getWorkItem(task.id, workspace.owner.workItemId)?.status === "retired" ? "abandoned" : request.disposition;
|
|
91
|
+
let checks;
|
|
92
|
+
try {
|
|
93
|
+
checks = await preparer.inspectWorkspaceCleanup(workspace, disposition, request.force);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
checks = cleanupCheckFromError(error, managedWorkspaceKey(workspace.owner), [managedWorkspaceKey(workspace.owner)], [`yui task show ${task.id}`]);
|
|
97
|
+
}
|
|
98
|
+
resources.push({ resource: managedWorkspaceKey(workspace.owner), owner: workspace.owner,
|
|
99
|
+
status: checks.some(c => c.status === "unknown") ? "unknown" : checks.length > 0 ? "blocked" : "checked",
|
|
100
|
+
checks });
|
|
101
|
+
}
|
|
102
|
+
const eligible = ["completed", "cancelled", "archived"].includes(task.status);
|
|
103
|
+
return { taskId: task.id, observedAt: new Date().toISOString(), disposition: request.disposition, force: request.force,
|
|
104
|
+
readOnly: true, authorizesCleanup: false,
|
|
105
|
+
archive: { status: task.status, eligible, alreadyArchived: task.status === "archived",
|
|
106
|
+
settlement, delivery: deliveryChecks, forceBypassesSettlement: request.force,
|
|
107
|
+
requiresIndependentAuthorization: true },
|
|
108
|
+
cleanup: { execution, resources },
|
|
109
|
+
note: "Current observations only. Execution reloads the checks. Force commits archive first and retains unsafe resources; a finished attempt does not prove resource release." };
|
|
110
|
+
}
|
|
111
|
+
export function renderTaskArchivePreflight(data) {
|
|
112
|
+
return [
|
|
113
|
+
`Archive preflight: ${data.taskId} (${data.disposition}${data.force ? ", force" : ""}); read-only, not authorization`,
|
|
114
|
+
`Task: ${data.archive.status}; terminal eligibility: ${data.archive.eligible}`,
|
|
115
|
+
...data.archive.settlement.map(c => `Admission${data.force ? " (force preserves)" : ""}: ${renderCleanupCheck(c)}`),
|
|
116
|
+
...data.archive.delivery.map(c => `Delivery${data.force || data.disposition === "abandoned" ? " (advisory for admission)" : ""}: ${renderCleanupCheck(c)}`),
|
|
117
|
+
...data.cleanup.execution.map(c => `Cleanup: ${renderCleanupCheck(c)}`),
|
|
118
|
+
...data.cleanup.resources.flatMap(r => [
|
|
119
|
+
`Workspace [${r.resource}]: ${r.status}`,
|
|
120
|
+
...r.checks.map(c => ` ${renderCleanupCheck(c)}`)
|
|
121
|
+
]),
|
|
122
|
+
data.note
|
|
123
|
+
].join("\n") + "\n";
|
|
124
|
+
}
|
|
@@ -17,18 +17,13 @@ export function pendingCompletionMessages(store, taskId) {
|
|
|
17
17
|
const ids = new Set(refs.filter(ref => ref.type === "message" && ref.taskId === taskId).map(ref => ref.id));
|
|
18
18
|
return operationalTaskRecords(store.listMessages(taskId), store.listEvents(taskId), "message")
|
|
19
19
|
.filter(message => ids.has(message.id)
|
|
20
|
-
&& (message.kind === "user" || message.kind === "operator")
|
|
20
|
+
&& (message.kind === "user" || message.kind === "operator"));
|
|
21
21
|
}
|
|
22
22
|
const ACTIVE_JOB_STATUSES = new Set([
|
|
23
23
|
"queued",
|
|
24
24
|
"running",
|
|
25
25
|
"unknown-needs-attention"
|
|
26
26
|
]);
|
|
27
|
-
const UNRESOLVED_INTEGRATION_STATUSES = new Set([
|
|
28
|
-
"running",
|
|
29
|
-
"blocked",
|
|
30
|
-
"validating"
|
|
31
|
-
]);
|
|
32
27
|
const TERMINAL_REVIEW_STATUSES = new Set(["completed", "failed"]);
|
|
33
28
|
const TERMINAL_LANE_STATUSES = new Set(["completed", "failed", "skipped"]);
|
|
34
29
|
export function projectCompletionReadiness(facts) {
|
|
@@ -45,7 +40,7 @@ export function projectCompletionReadiness(facts) {
|
|
|
45
40
|
}
|
|
46
41
|
// A pending/running Task-final Review must be resumed or blocked first.
|
|
47
42
|
for (const round of facts.reviewRounds) {
|
|
48
|
-
if (
|
|
43
|
+
if (round.scope !== "task")
|
|
49
44
|
continue;
|
|
50
45
|
if (round.status !== "pending" && round.status !== "running")
|
|
51
46
|
continue;
|
|
@@ -103,11 +98,9 @@ export function projectCompletionReadiness(facts) {
|
|
|
103
98
|
fix: `yui task integration start ${task.id} --work-item ${delivery.workItemId} --project ${delivery.projectId} --strategy <ff|cherry-pick|merge|manual>`
|
|
104
99
|
});
|
|
105
100
|
}
|
|
106
|
-
//
|
|
107
|
-
//
|
|
101
|
+
// Unsettled attempts must be explicitly resolved; terminal history remains
|
|
102
|
+
// evidence and is not a second delivery workflow.
|
|
108
103
|
for (const integration of facts.integrations) {
|
|
109
|
-
if (!UNRESOLVED_INTEGRATION_STATUSES.has(integration.status))
|
|
110
|
-
continue;
|
|
111
104
|
if (!integrationAttemptRequiresSettlement(integration))
|
|
112
105
|
continue;
|
|
113
106
|
blockers.push({
|
|
@@ -117,18 +110,6 @@ export function projectCompletionReadiness(facts) {
|
|
|
117
110
|
fix: `yui task integration continue ${task.id}/${integration.id}`
|
|
118
111
|
});
|
|
119
112
|
}
|
|
120
|
-
// Legacy queue entries may still launch an Integration and therefore must
|
|
121
|
-
// settle even though ChangeSets are no longer delivery authority.
|
|
122
|
-
for (const entry of facts.integrationQueueEntries) {
|
|
123
|
-
if (entry.status === "committed" || entry.status === "superseded")
|
|
124
|
-
continue;
|
|
125
|
-
blockers.push({
|
|
126
|
-
code: "unsettled-integration-queue-entry",
|
|
127
|
-
ref: ref("integration-queue-entry", entry.id),
|
|
128
|
-
reason: `Integration queue entry ${entry.id} is ${entry.status}.`,
|
|
129
|
-
fix: `settle integration queue entry ${entry.id} (continue or supersede)`
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
113
|
// Terminal child workspaces are cleanup advisories: Task completion is the
|
|
133
114
|
// semantic delivery boundary, while archive remains the fail-closed resource
|
|
134
115
|
// reclamation boundary. Missing/non-terminal ownership stays conservative.
|
|
@@ -198,7 +179,7 @@ function workspaceCompletionDisposition(facts, taskId, workspace) {
|
|
|
198
179
|
}
|
|
199
180
|
case "integration-attempt": {
|
|
200
181
|
const integration = facts.integrations.find((entry) => entry.id === owner.integrationAttemptId);
|
|
201
|
-
if (integration !== undefined &&
|
|
182
|
+
if (integration !== undefined && integrationAttemptRequiresSettlement(integration)) {
|
|
202
183
|
return null;
|
|
203
184
|
}
|
|
204
185
|
const value = {
|