@zq-silk/yui 0.15.12 → 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 +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- 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 +57 -41
- 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 +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- 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/globalRoleCommands.js +53 -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 +1 -10
- 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 +271 -710
- 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 +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- 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 +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -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 +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- 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 +60 -84
- 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 +1 -9
- 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/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/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- 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/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -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 +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- 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 +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- 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 +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- 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 +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -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 -377
- 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",
|
|
@@ -168,12 +175,8 @@ function storageProtocol(home, options) {
|
|
|
168
175
|
* RPCs) plus {@link transactionAsync}, {@link invokeObserver}, and {@link close}.
|
|
169
176
|
*/
|
|
170
177
|
export class AsyncTaskStoreClient {
|
|
171
|
-
#home;
|
|
172
|
-
#options;
|
|
173
178
|
#rpc;
|
|
174
179
|
constructor(home, options = {}) {
|
|
175
|
-
this.#home = home;
|
|
176
|
-
this.#options = options;
|
|
177
180
|
this.#rpc = new BoundedRpcClient(storageProtocol(home, options), {
|
|
178
181
|
maxInFlight: options.maxInFlight,
|
|
179
182
|
maxQueue: options.maxQueue,
|
|
@@ -218,6 +221,11 @@ export class AsyncTaskStoreClient {
|
|
|
218
221
|
close() {
|
|
219
222
|
return this.#rpc.close();
|
|
220
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
|
+
}
|
|
221
229
|
/** Currently in-flight requests (metrics/tests). */
|
|
222
230
|
get inFlight() {
|
|
223
231
|
return this.#rpc.inFlight;
|
|
@@ -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);
|
|
@@ -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)
|
|
@@ -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 = {
|
package/dist/task/draftPlan.js
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
import { RUNTIME_OBSERVATION_TASK_EVENT } from "../runtime/runtimeObservation.js";
|
|
2
1
|
import { SYSTEM_LEADER_ROLE } from "../role/systemRoles.js";
|
|
3
|
-
/**
|
|
4
|
-
* Runtime Task-event types that are emitted with a Role but no Turn in their
|
|
5
|
-
* payload. Each one is still only exempted when its `roleName` resolves to the
|
|
6
|
-
* Leader — the `runtime.` prefix is never exempted, and a type absent from this
|
|
7
|
-
* set keeps disqualifying the Draft.
|
|
8
|
-
*
|
|
9
|
-
* Turn lifecycle events are deliberately not listed: they use one generic type
|
|
10
|
-
* per transition and name the Turn in their payload, so the guard resolves that
|
|
11
|
-
* Turn's own purpose instead of trusting the type. Listing invented
|
|
12
|
-
* `turn.planning-*` types would have exempted nothing while silently
|
|
13
|
-
* disqualifying every Draft that ever held a planning conversation.
|
|
14
|
-
*/
|
|
15
|
-
export const PLANNING_ROLE_EVENT_TYPES = new Set([
|
|
16
|
-
"runtime.session-stop-requested",
|
|
17
|
-
"runtime.session-termination",
|
|
18
|
-
// Written by the liveness/host-exit paths, which admit a Draft planning Turn.
|
|
19
|
-
// Its payload carries the Role and embeds the Turn only inside the serialized
|
|
20
|
-
// observation, so it is resolved as Role-scoped rather than by that blob.
|
|
21
|
-
"runtime.process-exit-observed"
|
|
22
|
-
]);
|
|
23
2
|
export function assertDraftTaskExecutionFree(store, task) {
|
|
24
3
|
if (task.status !== "draft") {
|
|
25
4
|
throw new Error(`Task is not a Draft: ${task.id}/${task.status}.`);
|
|
@@ -168,35 +147,3 @@ function firstDraftExecutionFact(store, task) {
|
|
|
168
147
|
// of delivery authority. The domain records above own delivery facts.
|
|
169
148
|
return undefined;
|
|
170
149
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Whether one runtime/Turn Task event belongs to this Draft's own planning
|
|
173
|
-
* conversation.
|
|
174
|
-
*
|
|
175
|
-
* The binding the event carries decides this, never its type prefix:
|
|
176
|
-
*
|
|
177
|
-
* - it names a Turn → that Turn must exist on this Task and be `planning`;
|
|
178
|
-
* - it names no Turn → its exact type must be Role-scoped by construction and
|
|
179
|
-
* its `roleName` must be the Leader, the only Role a Draft may run.
|
|
180
|
-
*
|
|
181
|
-
* Everything else stays disqualifying, so an unrecognized type, a foreign Role
|
|
182
|
-
* or a Turn reference that cannot be resolved fails closed.
|
|
183
|
-
*/
|
|
184
|
-
function isDraftPlanningEvent(store, task, event) {
|
|
185
|
-
// Several emitters write an absent Turn as an empty string, so only a
|
|
186
|
-
// non-empty id counts as a reference worth resolving.
|
|
187
|
-
const runId = event.payload["runId"];
|
|
188
|
-
if (typeof runId === "string" && runId !== "") {
|
|
189
|
-
return store.getRun(task.id, runId)?.purpose === "planning";
|
|
190
|
-
}
|
|
191
|
-
// A canonical runtime observation is the event normal delivery writes for
|
|
192
|
-
// session.started/conversation.observed/turn.accepted and every terminal. It
|
|
193
|
-
// names its Turn in the same payload field as everything else and was
|
|
194
|
-
// resolved above; only an unfenced host-level observation reaches here, where
|
|
195
|
-
// the Role still has to prove it. Admitting this one type by its own binding
|
|
196
|
-
// is not the same as admitting the `runtime.` prefix.
|
|
197
|
-
if (event.type !== RUNTIME_OBSERVATION_TASK_EVENT
|
|
198
|
-
&& !PLANNING_ROLE_EVENT_TYPES.has(event.type)) {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
return event.payload["roleName"] === SYSTEM_LEADER_ROLE;
|
|
202
|
-
}
|
package/dist/task/nextAction.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { actionableExecutionLaneRecoveries } from "../execution/executionHealth.js";
|
|
2
3
|
import { governingWorkItemDeliveries, workItemDeliverySettled } from "../integration/deliveryObligation.js";
|
|
3
4
|
import { isCompletedTaskReviewEvidenceFromRuns } from "../review/reviewAcceptance.js";
|
|
4
|
-
import { actionableExecutionLaneRecoveries } from "../execution/executionHealth.js";
|
|
5
5
|
import { sameTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
|
|
6
6
|
import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalReviewContractResolution.js";
|
|
7
|
-
import { draftWorkItemDependencyIssue } from "./draftPlan.js";
|
|
8
7
|
import { currentWorkItemCandidate, currentWorkItemExecutionGroup, governingWorkItemCandidate } from "../workItem/workItem.js";
|
|
8
|
+
import { draftWorkItemDependencyIssue } from "./draftPlan.js";
|
|
9
9
|
const OPEN_WORK_ITEM_STATUSES = new Set(["open"]);
|
|
10
10
|
export function projectNextAction(facts) {
|
|
11
11
|
const { task } = facts;
|
|
@@ -150,7 +150,7 @@ export function projectNextAction(facts) {
|
|
|
150
150
|
}
|
|
151
151
|
return buildAction(facts, {
|
|
152
152
|
kind: "integrate-work-item",
|
|
153
|
-
reason: `Integration ${checkingIntegration.id} awaits check-result consumption (${job?.status ?? "read current Job"});
|
|
153
|
+
reason: `Integration ${checkingIntegration.id} awaits check-result consumption (${job?.status ?? "read current Job"}); its exact Job must be consumed to finalize the attempt.`,
|
|
154
154
|
refs,
|
|
155
155
|
preconditions: [{ fact: "The Integration retains its exact check Job", satisfied: true, ref: refs[0] }],
|
|
156
156
|
recommendedCommand: `yui task integration continue ${task.id}/${checkingIntegration.id}`
|
|
@@ -378,7 +378,7 @@ export function projectNextAction(facts) {
|
|
|
378
378
|
}
|
|
379
379
|
if (deliveryWorkItems.length === 0
|
|
380
380
|
&& !taskFinalReviewRequired(facts)
|
|
381
|
-
&& !facts.reviewRounds.some((round) => (
|
|
381
|
+
&& !facts.reviewRounds.some((round) => (round.scope === "task"
|
|
382
382
|
&& (round.status === "pending" || round.status === "running")))) {
|
|
383
383
|
const reviewAlternative = facts.reviewConfig === null
|
|
384
384
|
? []
|
|
@@ -675,7 +675,6 @@ export function durableStateFingerprint(facts) {
|
|
|
675
675
|
...facts.workItems.map((item) => `work:${item.id}:${item.status}:${item.revision}:${item.updatedAt}`),
|
|
676
676
|
...facts.changeSets.map((changeSet) => `change-set:${changeSet.id}:${changeSet.headCommit}`),
|
|
677
677
|
...facts.integrations.map((attempt) => `integration:${attempt.id}:${attempt.status}:${attempt.updatedAt}`),
|
|
678
|
-
...facts.integrationQueueEntries.map((entry) => `integration-queue:${entry.id}:${entry.status}:${entry.updatedAt}`),
|
|
679
678
|
...facts.reviewRounds.map((round) => `review:${round.id}:${round.status}:${round.endedAt ?? ""}`)
|
|
680
679
|
];
|
|
681
680
|
return createHash("sha256").update(parts.join("\n")).digest("hex");
|
|
@@ -819,22 +818,17 @@ function taskFinalReviewRole(facts) {
|
|
|
819
818
|
function ref(kind, id) {
|
|
820
819
|
return { kind, id };
|
|
821
820
|
}
|
|
822
|
-
function latestFailedReviewFor(rounds, workItemId) {
|
|
823
|
-
return [...rounds]
|
|
824
|
-
.reverse()
|
|
825
|
-
.find((round) => round.workItemId === workItemId && round.status === "failed");
|
|
826
|
-
}
|
|
827
821
|
function latestTaskFinalReview(rounds, contract) {
|
|
828
822
|
return [...rounds]
|
|
829
823
|
.reverse()
|
|
830
|
-
.find((round) => (
|
|
824
|
+
.find((round) => (round.scope === "task"
|
|
831
825
|
&& (contract === undefined || sameTaskFinalReviewContract(round.taskFinalReviewContract, contract))));
|
|
832
826
|
}
|
|
833
827
|
function hasValidFinalReview(facts) {
|
|
834
828
|
const contract = taskFinalReviewContract(facts);
|
|
835
829
|
const final = [...facts.reviewRounds]
|
|
836
830
|
.reverse()
|
|
837
|
-
.find((round) => (
|
|
831
|
+
.find((round) => (round.scope === "task"
|
|
838
832
|
&& (contract === undefined || sameTaskFinalReviewContract(round.taskFinalReviewContract, contract))));
|
|
839
833
|
if (final === undefined || !isCompletedTaskReviewEvidenceFromRuns(final, facts.reviewOutcomeEvidence?.runs ?? []))
|
|
840
834
|
return false;
|
|
@@ -853,7 +847,7 @@ function detectProtocolInconsistency(facts) {
|
|
|
853
847
|
: [ref("candidate", `${item.id}/${candidate.id}`)];
|
|
854
848
|
});
|
|
855
849
|
const reviewRefs = facts.reviewRounds
|
|
856
|
-
.filter((round) => (
|
|
850
|
+
.filter((round) => (round.scope === "task"
|
|
857
851
|
&& round.taskFinalReviewContract !== undefined))
|
|
858
852
|
.map((round) => ref("review-round", round.id));
|
|
859
853
|
return {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { usageError } from "../errors/cliError.js";
|
|
3
|
+
import { projectTaskRemoteDelivery } from "../task/remoteDelivery.js";
|
|
4
|
+
import { PUBLICATION_ADOPTED_EVENT } from "./publicationAdoption.js";
|
|
5
|
+
export function projectTaskRemoteDeliveryFromStore(store, task, currentCandidate = null) {
|
|
6
|
+
return createTaskRemoteDeliveryProof(store, task, currentCandidate).delivery;
|
|
7
|
+
}
|
|
8
|
+
export function createTaskRemoteDeliveryProof(store, task, currentCandidate = null) {
|
|
9
|
+
const publications = store.listPublicationReferences(task.id);
|
|
10
|
+
const delivery = projectTaskRemoteDelivery({
|
|
11
|
+
task,
|
|
12
|
+
events: store.listEventsByType(task.id, [
|
|
13
|
+
"task.completed", "task.archived", PUBLICATION_ADOPTED_EVENT
|
|
14
|
+
]),
|
|
15
|
+
publications,
|
|
16
|
+
managedWorkspaces: store.listManagedWorkspaces(task.id),
|
|
17
|
+
runs: task.projectBindings.length === 0 ? [] : store.listTaskRunWorkspaceBases(task.id),
|
|
18
|
+
integrations: store.listIntegrationAttempts(task.id),
|
|
19
|
+
currentCandidate
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
schemaVersion: 1,
|
|
23
|
+
task: taskRemoteDeliveryProofTask(task),
|
|
24
|
+
publications,
|
|
25
|
+
delivery
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function assertTaskRemoteDeliveryProof(store, task, proof) {
|
|
29
|
+
if (proof === undefined || proof.schemaVersion !== 1) {
|
|
30
|
+
throw usageError(`Task remote-delivery preflight proof is required: ${task.id}.`);
|
|
31
|
+
}
|
|
32
|
+
if (!isDeepStrictEqual(proof.task, taskRemoteDeliveryProofTask(task))) {
|
|
33
|
+
throw usageError(`Task changed after remote-delivery preflight: ${task.id}.`);
|
|
34
|
+
}
|
|
35
|
+
if (!isDeepStrictEqual(proof.publications, store.listPublicationReferences(task.id))) {
|
|
36
|
+
throw usageError(`Task Publication evidence changed after remote-delivery preflight: ${task.id}.`);
|
|
37
|
+
}
|
|
38
|
+
// Recheck semantic adoption/Integration facts as well as Publication versions.
|
|
39
|
+
// For provisional cancelled Tasks retain the frozen candidate used at preflight.
|
|
40
|
+
const current = projectTaskRemoteDeliveryFromStore(store, task, {
|
|
41
|
+
projects: proof.delivery.projects.flatMap(p => p.expectedLocalCommit === null
|
|
42
|
+
? [] : [{ projectId: p.projectId, commit: p.expectedLocalCommit }])
|
|
43
|
+
});
|
|
44
|
+
if (!isDeepStrictEqual(current, proof.delivery)) {
|
|
45
|
+
throw usageError(`Task delivery evidence changed after remote-delivery preflight: ${task.id}.`);
|
|
46
|
+
}
|
|
47
|
+
assertTaskRemoteDeliveryIntegrated(proof.delivery);
|
|
48
|
+
return proof.delivery;
|
|
49
|
+
}
|
|
50
|
+
export function assertTaskRemoteDeliveryIntegrated(delivery) {
|
|
51
|
+
if (!delivery.allMerged) {
|
|
52
|
+
const headUnavailable = delivery.projects.filter((project) => (project.codeDelivery !== "none"
|
|
53
|
+
&& project.coverage === "head-unavailable"));
|
|
54
|
+
if (headUnavailable.length > 0 && delivery.source === "task-completed") {
|
|
55
|
+
throw usageError(`Task ${delivery.taskId} has no frozen completion heads for: ${headUnavailable.map(({ projectId }) => projectId).join(", ")}. Historical acceptance cannot be inferred from a merge or archive.`);
|
|
56
|
+
}
|
|
57
|
+
const uncovered = delivery.projects
|
|
58
|
+
.filter((project) => project.codeDelivery !== "none" && !project.merged)
|
|
59
|
+
.map((project) => (`${project.projectId}/${project.coverage}`
|
|
60
|
+
+ (project.publication === null ? "" : `/${project.publication.id}`)))
|
|
61
|
+
.join(", ");
|
|
62
|
+
throw usageError(`Task ${delivery.taskId} is not fully merged into remote delivery: ${uncovered || "expected Project heads are unavailable"}. Inspect each Project reason with task remote-delivery. Record confirmed Publication facts; `
|
|
63
|
+
+ "for a changed post-completion candidate, review publication diff and explicitly adopt its acceptance.");
|
|
64
|
+
}
|
|
65
|
+
if (delivery.allVerified)
|
|
66
|
+
return;
|
|
67
|
+
const unverified = delivery.projects
|
|
68
|
+
.filter((project) => (project.codeDelivery !== "none"
|
|
69
|
+
&& project.merged
|
|
70
|
+
&& !project.verified))
|
|
71
|
+
.map((project) => (`${project.projectId}/${project.coverage}`
|
|
72
|
+
+ (project.publication === null ? "" : `/${project.publication.id}`)))
|
|
73
|
+
.join(", ");
|
|
74
|
+
throw usageError(`Task ${delivery.taskId} has merged Publication evidence that is not verified: `
|
|
75
|
+
+ `${unverified || "verification evidence is unavailable"}. Run task publication verify `
|
|
76
|
+
+ "for each Publication only when the provider read is authorized.");
|
|
77
|
+
}
|
|
78
|
+
export function taskRemoteDeliveryProofTask(task) {
|
|
79
|
+
return {
|
|
80
|
+
id: task.id,
|
|
81
|
+
status: task.status,
|
|
82
|
+
projectBindings: task.projectBindings,
|
|
83
|
+
...(task.workspaceIdentity === undefined
|
|
84
|
+
? {}
|
|
85
|
+
: { workspaceIdentity: task.workspaceIdentity }),
|
|
86
|
+
...(task.completedAt === undefined ? {} : { completedAt: task.completedAt }),
|
|
87
|
+
...(task.retiredAt === undefined ? {} : { retiredAt: task.retiredAt })
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -22,7 +22,6 @@ export const TASK_ACTIVATION_EVENT = Object.freeze({
|
|
|
22
22
|
adopted: "task.activation-adopted",
|
|
23
23
|
failed: "task.activation-failed"
|
|
24
24
|
});
|
|
25
|
-
export const TASK_ACTIVATION_ORIGINS = Object.freeze(["explicit", "submit-develop"]);
|
|
26
25
|
/**
|
|
27
26
|
* The digest that decides whether a repeated requestId is the same request.
|
|
28
27
|
*
|
|
@@ -64,7 +63,6 @@ export function createTaskActivationRequest(taskId, input, now) {
|
|
|
64
63
|
partialResultRefs: []
|
|
65
64
|
},
|
|
66
65
|
startMode: input.startMode,
|
|
67
|
-
...(input.origin === undefined ? {} : { origin: input.origin }),
|
|
68
66
|
...(input.afterPlanningRun === undefined
|
|
69
67
|
? {}
|
|
70
68
|
: { afterPlanningRun: input.afterPlanningRun }),
|
|
@@ -196,26 +194,6 @@ export function admitTaskActivationRequest(task, request, planningRunIsActive) {
|
|
|
196
194
|
}
|
|
197
195
|
return { disposition: "ready", request };
|
|
198
196
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Whether the Controller may continue (auto-adopt) a released request without an
|
|
201
|
-
* explicit activation boundary call (task-32 §2.4).
|
|
202
|
-
*
|
|
203
|
-
* A deferred request already holds activation authority and is released by its
|
|
204
|
-
* planning Turn ending, so it is adoptable regardless of origin. An immediate
|
|
205
|
-
* request is auto-continued only when it carries a recognised provable origin:
|
|
206
|
-
* an explicit activation action, or a develop submission the shared transaction
|
|
207
|
-
* accepted while the Task was unplanned. A request with no origin — every one
|
|
208
|
-
* migrated from before the field existed — is deliberately held back so an
|
|
209
|
-
* upgrade cannot turn a historical pending request into a silent activation; it
|
|
210
|
-
* still adopts through the explicit `yui task activate` boundary, which does not
|
|
211
|
-
* consult origin. This replaces the old actor heuristic that both dropped legal
|
|
212
|
-
* Operator/user immediate requests and could not distinguish provenance.
|
|
213
|
-
*/
|
|
214
|
-
export function activationRequestIsControllerAdoptable(request) {
|
|
215
|
-
if (request.startMode === "after-planning-turn")
|
|
216
|
-
return true;
|
|
217
|
-
return request.origin !== undefined;
|
|
218
|
-
}
|
|
219
197
|
/**
|
|
220
198
|
* The durable terminal outcome for a requestId, read from the activation event
|
|
221
199
|
* ledger.
|
|
@@ -258,9 +236,8 @@ export function validateTaskActivationRequest(request) {
|
|
|
258
236
|
if (!["immediate", "after-planning-turn"].includes(request.startMode)) {
|
|
259
237
|
throw new Error(`Activation start mode is invalid: ${String(request.startMode)}.`);
|
|
260
238
|
}
|
|
261
|
-
if (request
|
|
262
|
-
|
|
263
|
-
throw new Error(`Activation origin is invalid: ${String(request.origin)}.`);
|
|
239
|
+
if (Object.hasOwn(request, "origin")) {
|
|
240
|
+
throw new Error("Activation contains retired origin metadata; use the storage upgrade boundary.");
|
|
264
241
|
}
|
|
265
242
|
if ((request.startMode === "after-planning-turn")
|
|
266
243
|
!== (request.afterPlanningRun !== undefined)) {
|
|
@@ -108,7 +108,6 @@ export function recordTaskActivationRequestInTransaction(tx, input, now) {
|
|
|
108
108
|
actorId: input.actorId,
|
|
109
109
|
authorityRef: input.authorityRef,
|
|
110
110
|
startMode,
|
|
111
|
-
...(input.origin === undefined ? {} : { origin: input.origin }),
|
|
112
111
|
...(planningRun === undefined ? {} : { afterPlanningRun: planningRun }),
|
|
113
112
|
environmentPlan: plan
|
|
114
113
|
}, now);
|
|
@@ -118,7 +117,6 @@ export function recordTaskActivationRequestInTransaction(tx, input, now) {
|
|
|
118
117
|
startMode: request.startMode,
|
|
119
118
|
environmentPlan: describeEnvironmentPlan(request.environmentPlan),
|
|
120
119
|
actor: request.operation.actorId,
|
|
121
|
-
...(request.origin === undefined ? {} : { origin: request.origin }),
|
|
122
120
|
...(request.afterPlanningRun === undefined
|
|
123
121
|
? {}
|
|
124
122
|
: { afterPlanningRun: request.afterPlanningRun })
|