@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
|
+
import { parseRepeatable } from "../cli/parseRepeatable.js";
|
|
4
|
+
import { contextContentDigest } from "../context/contextSnapshot.js";
|
|
5
|
+
import { usageError } from "../errors/cliError.js";
|
|
6
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
7
|
+
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
8
|
+
import { PUBLICATION_ADOPTED_EVENT, publicationAdoption } from "../task/publicationAdoption.js";
|
|
9
|
+
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
10
|
+
import { commitMap, completionEvent } from "../task/remoteDelivery.js";
|
|
11
|
+
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
12
|
+
import { assertTaskDeliveryAuthority, taskActor } from "../task/taskAuthority.js";
|
|
13
|
+
/** Local read -> explicit semantic adoption of those exact bytes. No Git writes,
|
|
14
|
+
* provider reads, completion rewrite or implicit acceptance of Integration. */
|
|
15
|
+
export async function runTaskPublicationAdoptCommand(args, store, options = {}) {
|
|
16
|
+
const [command, ...rest] = args;
|
|
17
|
+
const adopt = command === "adopt";
|
|
18
|
+
if (!adopt && command !== "diff")
|
|
19
|
+
throw usageError("Expected publication diff or adopt.");
|
|
20
|
+
const usage = adopt
|
|
21
|
+
? "yui task publication adopt <task>/<publication> --reviewed-diff <sha256> --acceptance <text> [--integration <id>]"
|
|
22
|
+
: "yui task publication diff <task>/<publication> [--integration <id>]";
|
|
23
|
+
const parsed = parseRepeatable(rest, new Set(), new Set(adopt ? ["--reviewed-diff", "--acceptance", "--integration"] : ["--integration"]), usage);
|
|
24
|
+
if (parsed.positionals.length !== 1)
|
|
25
|
+
throw usageError(usage);
|
|
26
|
+
const ref = resolveTaskRecordReference(parsed.positionals[0], {
|
|
27
|
+
kind: "publicationReference", label: "Publication reference"
|
|
28
|
+
});
|
|
29
|
+
const acceptance = parsed.one.get("--acceptance")?.trim();
|
|
30
|
+
const reviewedDiff = parsed.one.get("--reviewed-diff");
|
|
31
|
+
if (adopt && (!acceptance || !/^[0-9a-f]{64}$/u.test(reviewedDiff ?? ""))) {
|
|
32
|
+
throw usageError(`Adoption requires a reviewed diff digest and explicit acceptance of the full delta. ${usage}`);
|
|
33
|
+
}
|
|
34
|
+
const integrationId = parsed.one.get("--integration");
|
|
35
|
+
const read = (reader) => {
|
|
36
|
+
// The reader addresses only this Task's managed Git objects. Workers and
|
|
37
|
+
// planning Sessions do not acquire publication-adoption authority.
|
|
38
|
+
const actor = adopt
|
|
39
|
+
? assertTaskDeliveryAuthority(reader, options.environment, ref.taskId)
|
|
40
|
+
: taskActor(options.environment, ref.taskId);
|
|
41
|
+
const task = reader.getTask(ref.taskId);
|
|
42
|
+
if (task === null || task.status !== "completed") {
|
|
43
|
+
throw usageError("Publication candidate adoption/diff requires a completed, unarchived Task.");
|
|
44
|
+
}
|
|
45
|
+
const publication = reader.getPublicationReference(task.id, ref.localId);
|
|
46
|
+
if (publication === null || publication.localCommit === undefined) {
|
|
47
|
+
throw usageError("Publication must record an exact local candidate.");
|
|
48
|
+
}
|
|
49
|
+
const current = reader.findPublicationReferenceByExternalKey(publicationExternalKey(publication));
|
|
50
|
+
if (current?.taskId !== task.id || current.id !== publication.id) {
|
|
51
|
+
throw usageError("Publication is not the current record for its external identity.");
|
|
52
|
+
}
|
|
53
|
+
const completion = task.completedAt === undefined ? undefined
|
|
54
|
+
: completionEvent(reader.listEvents(task.id), task.completedAt);
|
|
55
|
+
const acceptedCommit = commitMap(completion?.payload.projectHeads).get(publication.projectId);
|
|
56
|
+
if (completion === undefined || acceptedCommit === undefined) {
|
|
57
|
+
throw usageError("Exact completion evidence is unavailable; it cannot be inferred or repaired by adoption.");
|
|
58
|
+
}
|
|
59
|
+
const workspace = reader.getTaskWorkspace(task.id);
|
|
60
|
+
const entry = workspace?.entries.find(e => e.projectId === publication.projectId);
|
|
61
|
+
if (workspace?.owner.type !== "task" || workspace.owner.taskId !== task.id
|
|
62
|
+
|| entry === undefined
|
|
63
|
+
|| !task.projectBindings.some(b => b.projectId === publication.projectId)) {
|
|
64
|
+
throw usageError("The Publication Project has no Task-owned workspace for local Git evidence.");
|
|
65
|
+
}
|
|
66
|
+
const integration = integrationId === undefined ? null
|
|
67
|
+
: reader.getIntegrationAttempt(task.id, integrationId);
|
|
68
|
+
if (integrationId !== undefined && (integration === null
|
|
69
|
+
|| integration.status !== "committed"
|
|
70
|
+
|| integration.projectId !== publication.projectId
|
|
71
|
+
|| integration.afterCommit !== publication.localCommit)) {
|
|
72
|
+
throw usageError("Integration must be this Task/Project's committed exact publication candidate.");
|
|
73
|
+
}
|
|
74
|
+
return { task, publication, completion, acceptedCommit, workspace, entry, integration, actor };
|
|
75
|
+
};
|
|
76
|
+
const facts = store.transaction(read);
|
|
77
|
+
const git = options.git ?? new NodeGitWorkspace();
|
|
78
|
+
const localCommit = facts.publication.localCommit;
|
|
79
|
+
const [acceptedTree, candidateTree, diff] = await Promise.all([
|
|
80
|
+
git.resolveTree(facts.entry.path, facts.acceptedCommit),
|
|
81
|
+
git.resolveTree(facts.entry.path, localCommit),
|
|
82
|
+
git.diffTextBetween({ repositoryPath: facts.entry.path,
|
|
83
|
+
fromCommit: facts.acceptedCommit, toCommit: localCommit, exact: true })
|
|
84
|
+
]);
|
|
85
|
+
// Bind both endpoints even when the text diff is empty (equal trees).
|
|
86
|
+
const diffDigest = createHash("sha256").update(JSON.stringify({
|
|
87
|
+
acceptedCommit: facts.acceptedCommit, localCommit, acceptedTree, candidateTree, diff
|
|
88
|
+
})).digest("hex");
|
|
89
|
+
const evidence = {
|
|
90
|
+
projectId: facts.publication.projectId,
|
|
91
|
+
publicationId: facts.publication.id,
|
|
92
|
+
completionEventId: facts.completion.id,
|
|
93
|
+
acceptedCommit: facts.acceptedCommit,
|
|
94
|
+
localCommit, acceptedTree, candidateTree, diffDigest,
|
|
95
|
+
...(facts.integration === null ? {} : {
|
|
96
|
+
integrationId: facts.integration.id, integrationDigest: contextContentDigest(facts.integration)
|
|
97
|
+
})
|
|
98
|
+
};
|
|
99
|
+
// No async Git/provider work under the transaction; fixed objects cannot move.
|
|
100
|
+
const event = store.transaction(tx => {
|
|
101
|
+
if (!isDeepStrictEqual(read(tx), facts)) {
|
|
102
|
+
throw usageError("Task, Publication or Integration changed while reading candidate evidence.");
|
|
103
|
+
}
|
|
104
|
+
if (!adopt)
|
|
105
|
+
return null;
|
|
106
|
+
if (reviewedDiff !== diffDigest) {
|
|
107
|
+
throw usageError(`Reviewed diff does not match this candidate. Read publication diff ${ref.taskId}/${ref.localId}.`);
|
|
108
|
+
}
|
|
109
|
+
const existing = publicationAdoption(facts.publication, facts.completion, facts.acceptedCommit, tx.listEvents(facts.task.id), tx.listPublicationReferences(facts.task.id), tx.listIntegrationAttempts(facts.task.id)).event;
|
|
110
|
+
if (existing !== null && existing.payload.diffDigest === diffDigest
|
|
111
|
+
&& existing.payload.acceptance === acceptance
|
|
112
|
+
&& existing.payload.integrationId === integrationId)
|
|
113
|
+
return existing;
|
|
114
|
+
const saved = createTaskEvent(tx.nextEventId(facts.task.id), facts.task.id, PUBLICATION_ADOPTED_EVENT, { ...evidence, acceptance: acceptance, by: facts.actor }, options.now?.() ?? new Date());
|
|
115
|
+
tx.saveEvent(facts.task.id, saved);
|
|
116
|
+
return saved;
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
output: event === null
|
|
120
|
+
? `Publication candidate: ${ref.taskId}/${ref.localId}\nAccepted: ${facts.acceptedCommit}\n`
|
|
121
|
+
+ `Candidate: ${localCommit}\nReviewed diff digest: ${diffDigest}\n`
|
|
122
|
+
+ "Review all removals, additions and conflict resolutions against the original acceptance before adoption.\n"
|
|
123
|
+
+ `${diff}\n`
|
|
124
|
+
: `Adopted Publication candidate ${ref.taskId}/${ref.localId} (${event.id}); remote verification remains separate.\n`,
|
|
125
|
+
data: event === null ? { ...evidence, diff } : event
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
-
import { taskActor } from "./taskActor.js";
|
|
3
2
|
import { dataError, taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
3
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
5
4
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
@@ -7,6 +6,7 @@ import { formatTimestamp } from "../output/timePresentation.js";
|
|
|
7
6
|
import { resolveProject } from "../repository/project.js";
|
|
8
7
|
import { createPublicationReference } from "../task/publicationReference.js";
|
|
9
8
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
9
|
+
import { taskActor } from "../task/taskAuthority.js";
|
|
10
10
|
const PROVIDERS = new Set(["github", "gitlab"]);
|
|
11
11
|
const EXTERNAL_KINDS = new Set(["pull-request", "merge-request"]);
|
|
12
12
|
const STATES = new Set(["open", "merged", "closed"]);
|
|
@@ -17,6 +17,7 @@ const INHERITED_METADATA_FIELDS = [
|
|
|
17
17
|
"targetBranch"
|
|
18
18
|
];
|
|
19
19
|
const INHERITED_EVIDENCE_FIELDS = [
|
|
20
|
+
"headCommit",
|
|
20
21
|
"remoteCommit",
|
|
21
22
|
"evidence",
|
|
22
23
|
"mergedAt"
|
|
@@ -34,7 +35,7 @@ export function runPublicationCommand(args, store, options) {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
function upsertPublication(args, store, options) {
|
|
37
|
-
const usage = "Task publication upsert usage: yui task publication upsert <task> --project <project> --provider <github|gitlab> --repository <owner/name> --kind <pull-request|merge-request> --id <external-id> [--url <url>] [--title <text>] [--source-branch <branch>] [--target-branch <branch>] [--local-commit <sha>] [--remote-commit <sha>] [--state <open|merged|closed>] [--reported|--verified] [--evidence <text>] [--merged-at <iso-timestamp>].";
|
|
38
|
+
const usage = "Task publication upsert usage: yui task publication upsert <task> --project <project> --provider <github|gitlab> --repository <owner/name> --kind <pull-request|merge-request> --id <external-id> [--url <url>] [--title <text>] [--source-branch <branch>] [--target-branch <branch>] [--local-commit <sha>] [--head-commit <sha>] [--remote-commit <sha>] [--state <open|merged|closed>] [--reported|--verified] [--evidence <text>] [--merged-at <iso-timestamp>].";
|
|
38
39
|
const parsed = parseUpsertArgs(args, usage);
|
|
39
40
|
exactPositionals(parsed.positionals, 1, usage);
|
|
40
41
|
const task = requireTask(store, parsed.positionals[0]);
|
|
@@ -72,6 +73,9 @@ function upsertPublication(args, store, options) {
|
|
|
72
73
|
...(parsed.options.has("--remote-commit")
|
|
73
74
|
? { remoteCommit: requiredOption(parsed.options, "--remote-commit", usage) }
|
|
74
75
|
: {}),
|
|
76
|
+
...(parsed.options.has("--head-commit")
|
|
77
|
+
? { headCommit: requiredOption(parsed.options, "--head-commit", usage) }
|
|
78
|
+
: {}),
|
|
75
79
|
...(state === undefined ? {} : { state }),
|
|
76
80
|
...(verification === undefined ? {} : { verification }),
|
|
77
81
|
...(parsed.options.has("--evidence")
|
|
@@ -181,6 +185,7 @@ function renderPublication(reference, timeZone) {
|
|
|
181
185
|
: [`Branches: ${reference.sourceBranch ?? "unknown"} -> ${reference.targetBranch ?? "unknown"}`]),
|
|
182
186
|
`State: ${reference.state}`,
|
|
183
187
|
`Verification: ${reference.verification}`,
|
|
188
|
+
...(reference.headCommit === undefined ? [] : [`Remote head: ${reference.headCommit}`]),
|
|
184
189
|
`Lineage: ${reference.localCommit ?? "unknown"} -> ${reference.externalId} -> ${reference.remoteCommit ?? "unknown"}`,
|
|
185
190
|
...(reference.mergedAt === undefined
|
|
186
191
|
? []
|
|
@@ -234,6 +239,10 @@ function inheritPublicationInput(existing, input) {
|
|
|
234
239
|
const mergedAtChanged = input.mergedAt !== undefined
|
|
235
240
|
&& input.mergedAt !== existing.mergedAt;
|
|
236
241
|
const evidenceContextChanged = localCommitChanged
|
|
242
|
+
|| (input.headCommit !== undefined
|
|
243
|
+
&& normalizeCommitForComparison(input.headCommit) !== existing.headCommit)
|
|
244
|
+
|| input.projectId !== existing.projectId
|
|
245
|
+
|| input.externalKind !== existing.externalKind
|
|
237
246
|
|| stateChanged
|
|
238
247
|
|| remoteCommitChanged
|
|
239
248
|
|| evidenceChanged
|
|
@@ -321,7 +330,7 @@ function parseUpsertArgs(args, usage) {
|
|
|
321
330
|
const valueOptions = new Set([
|
|
322
331
|
"--project", "--provider", "--repository", "--kind", "--id", "--url",
|
|
323
332
|
"--title", "--source-branch", "--target-branch", "--local-commit",
|
|
324
|
-
"--remote-commit", "--state", "--evidence", "--merged-at"
|
|
333
|
+
"--remote-commit", "--head-commit", "--state", "--evidence", "--merged-at"
|
|
325
334
|
]);
|
|
326
335
|
const flags = new Set(["--reported", "--verified"]);
|
|
327
336
|
const positionals = [];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
-
import { taskActor } from "
|
|
2
|
+
import { taskActor } from "../task/taskAuthority.js";
|
|
3
3
|
import { upsertTaskPublication } from "./taskPublicationCommands.js";
|
|
4
|
-
import { projectTaskRemoteDeliveryFromStore } from "./taskRemoteDeliveryCommand.js";
|
|
5
4
|
import { dataError, runtimeError, taskNotFound, usageError, CliError } from "../errors/cliError.js";
|
|
6
5
|
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
7
6
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
@@ -14,8 +13,7 @@ export async function runTaskPublicationVerifyCommand(args, store, options) {
|
|
|
14
13
|
throw usageError(`Archived Task Publication cannot be verified: ${task.id}.`);
|
|
15
14
|
}
|
|
16
15
|
taskActor(options.environment, task.id);
|
|
17
|
-
const
|
|
18
|
-
const request = preparePublicationVerification(store, reference.taskId, reference.localId, initialCandidate, options.environment);
|
|
16
|
+
const request = preparePublicationVerification(store, reference.taskId, reference.localId, options.environment);
|
|
19
17
|
const verifier = options.verifiers[request.publication.provider];
|
|
20
18
|
if (verifier === undefined) {
|
|
21
19
|
throw usageError(`Publication verification is not supported for provider `
|
|
@@ -42,19 +40,22 @@ export async function runTaskPublicationVerifyCommand(args, store, options) {
|
|
|
42
40
|
+ `${error instanceof Error ? error.message : String(error)}`);
|
|
43
41
|
}
|
|
44
42
|
assertVerificationObservation(request, observation);
|
|
45
|
-
const
|
|
46
|
-
const result = commitPublicationVerification(store, request, observation, finalCandidate, options.now?.() ?? new Date());
|
|
43
|
+
const result = commitPublicationVerification(store, request, observation, options.now?.() ?? new Date());
|
|
47
44
|
return {
|
|
48
45
|
kind: "output",
|
|
49
|
-
output: result.
|
|
50
|
-
? `Publication ${result.reference.id}
|
|
51
|
-
+
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
output: result.reference.verification !== "verified"
|
|
47
|
+
? `Recorded Publication ${result.reference.id}: remote state=${observation.state}; `
|
|
48
|
+
+ `head=${observation.headCommit}; local=${request.expectedLocalCommit}. `
|
|
49
|
+
+ "Not verified; prior verification is no longer current.\n"
|
|
50
|
+
: result.idempotent
|
|
51
|
+
? `Publication ${result.reference.id} is already verified for `
|
|
52
|
+
+ `${result.reference.repository}#${result.reference.externalId}.\n`
|
|
53
|
+
: `Verified publication ${result.reference.id} for `
|
|
54
|
+
+ `${result.reference.repository}#${result.reference.externalId}.\n`,
|
|
54
55
|
data: { publication: result.reference, observation }
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
|
-
function preparePublicationVerification(store, taskId, publicationId,
|
|
58
|
+
function preparePublicationVerification(store, taskId, publicationId, environment) {
|
|
58
59
|
return store.transaction((reader) => {
|
|
59
60
|
const task = reader.getTask(taskId);
|
|
60
61
|
if (task === null)
|
|
@@ -72,27 +73,19 @@ function preparePublicationVerification(store, taskId, publicationId, currentCan
|
|
|
72
73
|
throw usageError(`Publication ${publication.id} is not the current unsuperseded record `
|
|
73
74
|
+ `for ${publicationExternalKey(publication)}.`);
|
|
74
75
|
}
|
|
75
|
-
const
|
|
76
|
-
const project = delivery.projects.find(({ projectId }) => (projectId === publication.projectId));
|
|
76
|
+
const project = task.projectBindings.find(({ projectId }) => (projectId === publication.projectId));
|
|
77
77
|
if (project === undefined) {
|
|
78
78
|
throw usageError(`Publication ${publication.id} Project is not bound to Task ${task.id}: `
|
|
79
79
|
+ `${publication.projectId}.`);
|
|
80
80
|
}
|
|
81
|
-
if (project.expectedLocalCommit === null) {
|
|
82
|
-
throw usageError(`Task delivery head is unavailable for Publication ${publication.id}.`);
|
|
83
|
-
}
|
|
84
81
|
if (publication.localCommit === undefined) {
|
|
85
82
|
throw usageError(`Publication ${publication.id} must record a local commit before verification.`);
|
|
86
83
|
}
|
|
87
|
-
if (publication.localCommit !== project.expectedLocalCommit) {
|
|
88
|
-
throw usageError(`Publication ${publication.id} local commit ${publication.localCommit} `
|
|
89
|
-
+ `does not match Task delivery head ${project.expectedLocalCommit}.`);
|
|
90
|
-
}
|
|
91
84
|
return {
|
|
92
85
|
task: taskVerificationInvariant(task),
|
|
93
86
|
actor,
|
|
94
87
|
publication,
|
|
95
|
-
expectedLocalCommit:
|
|
88
|
+
expectedLocalCommit: publication.localCommit
|
|
96
89
|
};
|
|
97
90
|
});
|
|
98
91
|
}
|
|
@@ -104,22 +97,17 @@ function assertVerificationObservation(request, observation) {
|
|
|
104
97
|
|| observation.externalId !== publication.externalId) {
|
|
105
98
|
throw usageError(`Provider returned mismatched Publication identity for ${publication.id}.`);
|
|
106
99
|
}
|
|
107
|
-
if (observation.
|
|
108
|
-
throw usageError(
|
|
109
|
-
+ `${publication.externalId} is ${observation.state}, not merged.`);
|
|
100
|
+
if (!/^[0-9a-f]{40}$/u.test(observation.headCommit)) {
|
|
101
|
+
throw usageError("Provider observation requires an exact remote head.");
|
|
110
102
|
}
|
|
111
|
-
if (observation.
|
|
112
|
-
throw usageError(`Remote ${publication.externalKind} head ${observation.headCommit} `
|
|
113
|
-
+ `does not match Task delivery head ${request.expectedLocalCommit}.`);
|
|
114
|
-
}
|
|
115
|
-
if (observation.remoteCommit === undefined) {
|
|
103
|
+
if (observation.state === "merged" && observation.remoteCommit === undefined) {
|
|
116
104
|
throw usageError(`Merged remote ${publication.externalKind} did not expose a remote commit.`);
|
|
117
105
|
}
|
|
118
106
|
if (observation.evidence.trim().length === 0) {
|
|
119
107
|
throw usageError("Provider verification evidence is required.");
|
|
120
108
|
}
|
|
121
109
|
}
|
|
122
|
-
function commitPublicationVerification(store, request, observation,
|
|
110
|
+
function commitPublicationVerification(store, request, observation, now) {
|
|
123
111
|
return store.transaction((tx) => {
|
|
124
112
|
const task = tx.getTask(request.task.id);
|
|
125
113
|
if (task === null)
|
|
@@ -134,12 +122,6 @@ function commitPublicationVerification(store, request, observation, currentCandi
|
|
|
134
122
|
throw usageError(`Publication evidence changed during verification: `
|
|
135
123
|
+ `${task.id}/${request.publication.id}.`);
|
|
136
124
|
}
|
|
137
|
-
const delivery = projectTaskRemoteDeliveryFromStore(tx, task, currentCandidate);
|
|
138
|
-
const expectedLocalCommit = delivery.projects.find(({ projectId }) => (projectId === request.publication.projectId))?.expectedLocalCommit;
|
|
139
|
-
if (expectedLocalCommit !== request.expectedLocalCommit) {
|
|
140
|
-
throw usageError(`Task delivery head changed during Publication verification: `
|
|
141
|
-
+ `${request.publication.projectId}@${expectedLocalCommit ?? "unavailable"}.`);
|
|
142
|
-
}
|
|
143
125
|
return upsertTaskPublication(tx, task, {
|
|
144
126
|
projectId: request.publication.projectId,
|
|
145
127
|
provider: request.publication.provider,
|
|
@@ -150,9 +132,11 @@ function commitPublicationVerification(store, request, observation, currentCandi
|
|
|
150
132
|
? {}
|
|
151
133
|
: { externalUrl: observation.externalUrl }),
|
|
152
134
|
localCommit: request.expectedLocalCommit,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
135
|
+
headCommit: observation.headCommit,
|
|
136
|
+
...(observation.remoteCommit === undefined ? {} : { remoteCommit: observation.remoteCommit }),
|
|
137
|
+
state: observation.state,
|
|
138
|
+
verification: observation.state === "merged"
|
|
139
|
+
&& observation.headCommit === request.expectedLocalCommit ? "verified" : "reported",
|
|
156
140
|
evidence: observation.evidence,
|
|
157
141
|
...(observation.mergedAt === undefined
|
|
158
142
|
? {}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDeepStrictEqual } from "node:util";
|
|
2
1
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
3
|
-
import {
|
|
2
|
+
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
4
3
|
export function runTaskRemoteDeliveryCommand(args, store, currentCandidate = null) {
|
|
5
4
|
const usage = "Task remote-delivery usage: yui task remote-delivery <task> [--json].";
|
|
6
5
|
const asJson = args.includes("--json");
|
|
@@ -9,7 +8,7 @@ export function runTaskRemoteDeliveryCommand(args, store, currentCandidate = nul
|
|
|
9
8
|
throw usageError(usage);
|
|
10
9
|
}
|
|
11
10
|
const taskId = positionals[0].trim();
|
|
12
|
-
const data = store.
|
|
11
|
+
const data = store.readTransaction((reader) => {
|
|
13
12
|
const task = reader.getTask(taskId);
|
|
14
13
|
if (task === null)
|
|
15
14
|
throw taskNotFound(taskId);
|
|
@@ -23,76 +22,6 @@ export function runTaskRemoteDeliveryCommand(args, store, currentCandidate = nul
|
|
|
23
22
|
data
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
|
-
export function projectTaskRemoteDeliveryFromStore(store, task, currentCandidate = null) {
|
|
27
|
-
return projectTaskRemoteDelivery({
|
|
28
|
-
task,
|
|
29
|
-
events: store.listEvents(task.id),
|
|
30
|
-
publications: store.listPublicationReferences(task.id),
|
|
31
|
-
managedWorkspaces: store.listManagedWorkspaces(task.id),
|
|
32
|
-
runs: store.listRuns(task.id),
|
|
33
|
-
currentCandidate
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
export function createTaskRemoteDeliveryProof(store, task, currentCandidate = null) {
|
|
37
|
-
const publications = store.listPublicationReferences(task.id);
|
|
38
|
-
const delivery = projectTaskRemoteDelivery({
|
|
39
|
-
task,
|
|
40
|
-
events: store.listEvents(task.id),
|
|
41
|
-
publications,
|
|
42
|
-
managedWorkspaces: store.listManagedWorkspaces(task.id),
|
|
43
|
-
runs: store.listRuns(task.id),
|
|
44
|
-
currentCandidate
|
|
45
|
-
});
|
|
46
|
-
return {
|
|
47
|
-
schemaVersion: 1,
|
|
48
|
-
task: taskRemoteDeliveryProofTask(task),
|
|
49
|
-
publications,
|
|
50
|
-
delivery
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export function assertTaskRemoteDeliveryProof(store, task, proof) {
|
|
54
|
-
if (proof === undefined || proof.schemaVersion !== 1) {
|
|
55
|
-
throw usageError(`Task remote-delivery preflight proof is required: ${task.id}.`);
|
|
56
|
-
}
|
|
57
|
-
if (!isDeepStrictEqual(proof.task, taskRemoteDeliveryProofTask(task))) {
|
|
58
|
-
throw usageError(`Task changed after remote-delivery preflight: ${task.id}.`);
|
|
59
|
-
}
|
|
60
|
-
if (!isDeepStrictEqual(proof.publications, store.listPublicationReferences(task.id))) {
|
|
61
|
-
throw usageError(`Task Publication evidence changed after remote-delivery preflight: ${task.id}.`);
|
|
62
|
-
}
|
|
63
|
-
assertTaskRemoteDeliveryIntegrated(proof.delivery);
|
|
64
|
-
return proof.delivery;
|
|
65
|
-
}
|
|
66
|
-
export function assertTaskRemoteDeliveryIntegrated(delivery) {
|
|
67
|
-
if (!delivery.allMerged) {
|
|
68
|
-
const headUnavailable = delivery.projects.filter((project) => (project.codeDelivery !== "none"
|
|
69
|
-
&& project.coverage === "head-unavailable"));
|
|
70
|
-
if (headUnavailable.length > 0 && delivery.source === "task-completed") {
|
|
71
|
-
throw usageError(`Task ${delivery.taskId} has no frozen completion heads for: ${headUnavailable.map(({ projectId }) => projectId).join(", ")}. Reopen and complete it again to record exact heads before archiving; `
|
|
72
|
-
+ "An explicitly authorized --force archive retains this evidence gap.");
|
|
73
|
-
}
|
|
74
|
-
const uncovered = delivery.projects
|
|
75
|
-
.filter((project) => project.codeDelivery !== "none" && !project.merged)
|
|
76
|
-
.map((project) => (`${project.projectId}/${project.coverage}`
|
|
77
|
-
+ (project.publication === null ? "" : `/${project.publication.id}`)))
|
|
78
|
-
.join(", ");
|
|
79
|
-
throw usageError(`Task ${delivery.taskId} is not fully merged into remote delivery: ${uncovered || "expected Project heads are unavailable"}. Record confirmed current-head evidence with task publication upsert, `
|
|
80
|
-
+ "or use task archive --abandon for an intentional non-merge.");
|
|
81
|
-
}
|
|
82
|
-
if (delivery.allVerified)
|
|
83
|
-
return;
|
|
84
|
-
const unverified = delivery.projects
|
|
85
|
-
.filter((project) => (project.codeDelivery !== "none"
|
|
86
|
-
&& project.merged
|
|
87
|
-
&& !project.verified))
|
|
88
|
-
.map((project) => (`${project.projectId}/${project.coverage}`
|
|
89
|
-
+ (project.publication === null ? "" : `/${project.publication.id}`)))
|
|
90
|
-
.join(", ");
|
|
91
|
-
throw usageError(`Task ${delivery.taskId} has merged Publication evidence that is not verified: `
|
|
92
|
-
+ `${unverified || "verification evidence is unavailable"}. Run task publication verify `
|
|
93
|
-
+ "for each Publication, or repeat task archive --integrated --force to explicitly "
|
|
94
|
-
+ "archive despite evidence gaps, retaining the original delivery facts.");
|
|
95
|
-
}
|
|
96
25
|
export function renderTaskRemoteDelivery(delivery, indent = "") {
|
|
97
26
|
const source = `${delivery.source}${delivery.provisional ? " (provisional)" : ""}`;
|
|
98
27
|
const lines = [
|
|
@@ -108,27 +37,17 @@ export function renderTaskRemoteDelivery(delivery, indent = "") {
|
|
|
108
37
|
`${indent}Projects:${delivery.projects.length === 0 ? " none" : ""}`,
|
|
109
38
|
...delivery.projects.map((project) => (`${indent}- ${project.directory} (${project.projectId}): `
|
|
110
39
|
+ `expected=${shortCommit(project.expectedLocalCommit)}; `
|
|
40
|
+
+ `candidate=${shortCommit(project.deliveryLocalCommit)}; `
|
|
41
|
+
+ `adoption=${project.adoption?.id ?? "none"}; `
|
|
111
42
|
+ `base=${shortCommit(project.baseCommit)}; `
|
|
112
43
|
+ `publication=${project.publication?.id ?? "none"}; `
|
|
113
44
|
+ `state=${project.state ?? "none"}; `
|
|
114
45
|
+ `verification=${project.verification ?? "none"}; `
|
|
115
46
|
+ `remote=${shortCommit(project.remoteCommit)}; `
|
|
116
|
-
+ `coverage=${project.coverage}`))
|
|
47
|
+
+ `coverage=${project.coverage}\n${indent} ${project.reason}`))
|
|
117
48
|
];
|
|
118
49
|
return `${lines.join("\n")}\n`;
|
|
119
50
|
}
|
|
120
51
|
function shortCommit(commit) {
|
|
121
52
|
return commit === null ? "unknown" : commit.slice(0, 12);
|
|
122
53
|
}
|
|
123
|
-
function taskRemoteDeliveryProofTask(task) {
|
|
124
|
-
return {
|
|
125
|
-
id: task.id,
|
|
126
|
-
status: task.status,
|
|
127
|
-
projectBindings: task.projectBindings,
|
|
128
|
-
...(task.workspaceIdentity === undefined
|
|
129
|
-
? {}
|
|
130
|
-
: { workspaceIdentity: task.workspaceIdentity }),
|
|
131
|
-
...(task.completedAt === undefined ? {} : { completedAt: task.completedAt }),
|
|
132
|
-
...(task.retiredAt === undefined ? {} : { retiredAt: task.retiredAt })
|
|
133
|
-
};
|
|
134
|
-
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { parseRepeatable } from "../cli/parseRepeatable.js";
|
|
1
2
|
import { usageError } from "../errors/cliError.js";
|
|
2
3
|
import { GitIntegrationService } from "../integration/gitIntegrationService.js";
|
|
3
4
|
import { createIntegrationAttempt } from "../integration/integrationAttempt.js";
|
|
4
5
|
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
5
6
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
6
7
|
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
7
|
-
import {
|
|
8
|
-
import { taskLocalActor } from "./taskActor.js";
|
|
8
|
+
import { taskLocalActor } from "../task/taskAuthority.js";
|
|
9
9
|
export async function runTaskUpstreamCommand(args, store, home, options = {}) {
|
|
10
10
|
const [command, ...rest] = args;
|
|
11
11
|
if (command === "integrate")
|
|
@@ -15,7 +15,7 @@ export async function runTaskUpstreamCommand(args, store, home, options = {}) {
|
|
|
15
15
|
: `Unknown command: task upstream ${command}`);
|
|
16
16
|
}
|
|
17
17
|
async function integrateUpstream(args, store, home, options) {
|
|
18
|
-
const usage = "Task upstream integrate usage: yui task upstream integrate <task> (--latest|--project <project>) [--check <command> ...].";
|
|
18
|
+
const usage = "Task upstream integrate usage: yui task upstream integrate <task> (--latest|--project <project>) [--check <command> ...] [--rerun-checks].";
|
|
19
19
|
let latest = false;
|
|
20
20
|
const normalized = [];
|
|
21
21
|
for (const arg of args) {
|
|
@@ -29,7 +29,7 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
29
29
|
? ["--project", arg.slice("--project=".length)]
|
|
30
30
|
: [arg]));
|
|
31
31
|
}
|
|
32
|
-
const parsed = parseRepeatable(normalized, new Set(["--check"]), new Set(["--project"]), usage);
|
|
32
|
+
const parsed = parseRepeatable(normalized, new Set(["--check"]), new Set(["--project"]), usage, new Set(["--rerun-checks"]));
|
|
33
33
|
if (parsed.positionals.length !== 1)
|
|
34
34
|
throw usageError(usage);
|
|
35
35
|
const taskId = parsed.positionals[0];
|
|
@@ -59,7 +59,7 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
59
59
|
if (workspace === null || workspace.owner.type !== "task") {
|
|
60
60
|
throw usageError(`Task has no authoritative main clone: ${task.id}.`);
|
|
61
61
|
}
|
|
62
|
-
const service = new GitIntegrationService(store.rootDirectory(), store, git, now);
|
|
62
|
+
const service = new GitIntegrationService(store.rootDirectory(), store, git, now, options.environment, undefined, options.jobPort);
|
|
63
63
|
const results = [];
|
|
64
64
|
for (const binding of task.projectBindings) {
|
|
65
65
|
if (projectRef !== undefined && binding.projectId !== projectRef)
|
|
@@ -99,20 +99,31 @@ async function integrateUpstream(args, store, home, options) {
|
|
|
99
99
|
taskBaseCommit: binding.baseCommit,
|
|
100
100
|
strategy: "rebase"
|
|
101
101
|
},
|
|
102
|
-
checkCommands: parsed.many.get("--check") ?? []
|
|
102
|
+
checkCommands: parsed.many.get("--check") ?? [],
|
|
103
|
+
rerunChecks: parsed.one.has("--rerun-checks")
|
|
103
104
|
}, now());
|
|
104
105
|
tx.saveIntegrationAttempt(task.id, created);
|
|
105
106
|
return created;
|
|
106
107
|
});
|
|
107
108
|
const result = await service.integrate(task.id, attempt.id);
|
|
108
109
|
results.push(result);
|
|
109
|
-
|
|
110
|
+
// Each Project has an independent candidate and Job. --latest admits all
|
|
111
|
+
// requested Projects, then the Agent continues the returned exact attempts.
|
|
112
|
+
if (result.status !== "committed" && result.status !== "checks-running")
|
|
110
113
|
break;
|
|
111
114
|
}
|
|
112
115
|
if (projectRef !== undefined && results.length === 0) {
|
|
113
116
|
throw usageError(`Task Project not found: ${task.id}/${projectRef}.`);
|
|
114
117
|
}
|
|
115
|
-
const lines = results.
|
|
118
|
+
const lines = results.flatMap(result => {
|
|
119
|
+
const { attempt, status } = result;
|
|
120
|
+
return [
|
|
121
|
+
` ${attempt.id} ${attempt.projectId}: ${attempt.beforeCommit.slice(0, 12)} -> ${(attempt.afterCommit ?? attempt.candidateCommit ?? attempt.beforeCommit).slice(0, 12)} (${status})`,
|
|
122
|
+
...(status === "checks-running" ? [
|
|
123
|
+
` Job ${result.job.id}; continue with yui task integration continue ${task.id}/${attempt.id}`
|
|
124
|
+
] : [])
|
|
125
|
+
];
|
|
126
|
+
});
|
|
116
127
|
return {
|
|
117
128
|
output: `Upstream Integration results for Task ${task.id}:\n${lines.join("\n")}\n`,
|
|
118
129
|
data: { taskId: task.id, integrations: results }
|
|
@@ -103,7 +103,12 @@ export async function runWorkflowCommandAsync(args, store, options) {
|
|
|
103
103
|
// failure so the caller knows this path was compromised.
|
|
104
104
|
if (typeof store.rootDirectory === "function") {
|
|
105
105
|
const auditStore = openConfiguredTaskStore(store.rootDirectory());
|
|
106
|
-
|
|
106
|
+
try {
|
|
107
|
+
recordTaskEvent(auditStore, taskId, "release-workflow.run", eventPayload, eventNow);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
auditStore.close();
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
113
|
throw error;
|
|
109
114
|
}
|