@zq-silk/yui 0.13.9 → 0.14.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 +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
3
|
-
import { requireIdentity, requireTimestamp } from "../domain/validation.js";
|
|
4
|
-
import { readActiveReleasePointer, readHandoverFence, readHandoverReceipt, releaseDirectoryName, releasesDirectory, verifyReleaseIntegrity } from "../release/runtimeRelease.js";
|
|
5
|
-
import { exactControlPlaneDigest, parseExactControlPlaneDescriptor } from "../runtime/exactControlPlane.js";
|
|
6
|
-
import { createTaskFinalReviewContract, sameTaskFinalReviewContract, validateTaskFinalReviewContract } from "./taskFinalReviewContract.js";
|
|
7
|
-
import { governingWorkItemCandidate } from "../workItem/workItem.js";
|
|
8
|
-
import { TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT } from "./taskFinalReviewContractEvent.js";
|
|
9
|
-
export { TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT } from "./taskFinalReviewContractEvent.js";
|
|
10
|
-
/**
|
|
11
|
-
* Resolves the effective Task-final Review contract from its immutable Task
|
|
12
|
-
* records. Mutation and read-model callers share this adapter so a legal
|
|
13
|
-
* release rebind cannot be interpreted differently by the two paths.
|
|
14
|
-
*/
|
|
15
|
-
export function resolveRecordedTaskFinalReviewContract(taskId, workItems, reviewRounds, events) {
|
|
16
|
-
const candidateObservations = workItems.flatMap((item) => {
|
|
17
|
-
const candidate = governingWorkItemCandidate(item);
|
|
18
|
-
return candidate?.taskFinalReviewContract === undefined
|
|
19
|
-
? []
|
|
20
|
-
: [{
|
|
21
|
-
contract: candidate.taskFinalReviewContract,
|
|
22
|
-
createdAt: candidate.createdAt,
|
|
23
|
-
source: `Candidate ${item.id}/${candidate.id}`
|
|
24
|
-
}];
|
|
25
|
-
});
|
|
26
|
-
const historicalCandidateObservations = workItems.flatMap((item) => (item.candidates
|
|
27
|
-
.filter((candidate) => candidate.taskFinalReviewContract !== undefined)
|
|
28
|
-
.map((candidate) => ({
|
|
29
|
-
contract: candidate.taskFinalReviewContract,
|
|
30
|
-
createdAt: candidate.createdAt,
|
|
31
|
-
source: `Historical Candidate ${item.id}/${candidate.id}`
|
|
32
|
-
}))));
|
|
33
|
-
const reviewObservations = reviewRounds.flatMap((round) => ((round.scope ?? "work-item") !== "task"
|
|
34
|
-
|| round.taskFinalReviewContract === undefined
|
|
35
|
-
? []
|
|
36
|
-
: [{
|
|
37
|
-
contract: round.taskFinalReviewContract,
|
|
38
|
-
createdAt: round.createdAt,
|
|
39
|
-
source: `ReviewRound ${round.id}`
|
|
40
|
-
}]));
|
|
41
|
-
return resolveTaskFinalReviewContract(taskId, [...candidateObservations, ...reviewObservations], events, historicalCandidateObservations);
|
|
42
|
-
}
|
|
43
|
-
export function createTaskFinalReviewContractRebind(input) {
|
|
44
|
-
const taskId = requireIdentity(input.taskId, "Task final-review rebind Task id");
|
|
45
|
-
const reviewerRoleName = requireIdentity(input.reviewerRoleName, "Task final-review rebind Reviewer Role");
|
|
46
|
-
const fromContract = validateTaskFinalReviewContract(input.fromContract);
|
|
47
|
-
if (fromContract.taskId !== taskId) {
|
|
48
|
-
throw new Error("Task final-review rebind contract belongs to another Task.");
|
|
49
|
-
}
|
|
50
|
-
if (fromContract.reviewerRoleName !== reviewerRoleName) {
|
|
51
|
-
throw new Error("Task final-review rebind changes the Reviewer identity.");
|
|
52
|
-
}
|
|
53
|
-
const toContract = createTaskFinalReviewContract({
|
|
54
|
-
taskId,
|
|
55
|
-
reviewerRoleName,
|
|
56
|
-
controlPlaneDigest: requireDigest(input.toControlPlaneDigest, "Task final-review rebind target control-plane digest")
|
|
57
|
-
});
|
|
58
|
-
if (fromContract.controlPlaneDigest === toContract.controlPlaneDigest) {
|
|
59
|
-
throw new Error("Task final-review rebind must change the control-plane digest.");
|
|
60
|
-
}
|
|
61
|
-
const fromRelease = validateReleaseIdentity(input.fromRelease, "source");
|
|
62
|
-
const toRelease = validateReleaseIdentity(input.toRelease, "target");
|
|
63
|
-
if (fromRelease.releaseId === toRelease.releaseId) {
|
|
64
|
-
throw new Error("Task final-review rebind must cross an immutable release handover.");
|
|
65
|
-
}
|
|
66
|
-
return Object.freeze({
|
|
67
|
-
schemaVersion: 1,
|
|
68
|
-
taskId,
|
|
69
|
-
reviewerRoleName,
|
|
70
|
-
fromContract,
|
|
71
|
-
toContract,
|
|
72
|
-
fromRelease,
|
|
73
|
-
toRelease,
|
|
74
|
-
handoverId: requireIdentity(input.handoverId, "Task final-review rebind handover id"),
|
|
75
|
-
authorizedBy: requireText(input.authorizedBy, "Task final-review rebind authorizer")
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
export function taskFinalReviewContractRebindPayload(rebind) {
|
|
79
|
-
const validated = createTaskFinalReviewContractRebind({
|
|
80
|
-
...rebind,
|
|
81
|
-
toControlPlaneDigest: rebind.toContract.controlPlaneDigest
|
|
82
|
-
});
|
|
83
|
-
return {
|
|
84
|
-
schemaVersion: "1",
|
|
85
|
-
taskId: validated.taskId,
|
|
86
|
-
reviewerRoleName: validated.reviewerRoleName,
|
|
87
|
-
fromContractDigest: validated.fromContract.digest,
|
|
88
|
-
toContractDigest: validated.toContract.digest,
|
|
89
|
-
fromControlPlaneDigest: validated.fromContract.controlPlaneDigest,
|
|
90
|
-
toControlPlaneDigest: validated.toContract.controlPlaneDigest,
|
|
91
|
-
fromReleaseId: validated.fromRelease.releaseId,
|
|
92
|
-
fromReleaseVersion: validated.fromRelease.version,
|
|
93
|
-
fromReleaseBuildId: validated.fromRelease.buildId,
|
|
94
|
-
fromReleasePackageDigest: validated.fromRelease.packageDigest,
|
|
95
|
-
...(validated.fromRelease.sourceCommit === undefined
|
|
96
|
-
? {}
|
|
97
|
-
: { fromReleaseSourceCommit: validated.fromRelease.sourceCommit }),
|
|
98
|
-
toReleaseId: validated.toRelease.releaseId,
|
|
99
|
-
toReleaseVersion: validated.toRelease.version,
|
|
100
|
-
toReleaseBuildId: validated.toRelease.buildId,
|
|
101
|
-
toReleasePackageDigest: validated.toRelease.packageDigest,
|
|
102
|
-
...(validated.toRelease.sourceCommit === undefined
|
|
103
|
-
? {}
|
|
104
|
-
: { toReleaseSourceCommit: validated.toRelease.sourceCommit }),
|
|
105
|
-
handoverId: validated.handoverId,
|
|
106
|
-
authorizedBy: validated.authorizedBy
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
export function taskFinalReviewContractRebindFromEvent(event) {
|
|
110
|
-
if (event.type !== TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT) {
|
|
111
|
-
throw new Error(`Task event is not a final-review contract rebind: ${event.id}.`);
|
|
112
|
-
}
|
|
113
|
-
const payload = event.payload;
|
|
114
|
-
if (payload.schemaVersion !== "1") {
|
|
115
|
-
throw new Error(`Task final-review rebind event schema is invalid: ${event.id}.`);
|
|
116
|
-
}
|
|
117
|
-
if (payload.taskId !== event.taskId) {
|
|
118
|
-
throw new Error(`Task final-review rebind event belongs to another Task: ${event.id}.`);
|
|
119
|
-
}
|
|
120
|
-
const fromContract = createTaskFinalReviewContract({
|
|
121
|
-
taskId: payload.taskId,
|
|
122
|
-
reviewerRoleName: payload.reviewerRoleName,
|
|
123
|
-
controlPlaneDigest: payload.fromControlPlaneDigest
|
|
124
|
-
});
|
|
125
|
-
if (fromContract.digest !== payload.fromContractDigest) {
|
|
126
|
-
throw new Error(`Task final-review rebind source digest is invalid: ${event.id}.`);
|
|
127
|
-
}
|
|
128
|
-
const rebind = createTaskFinalReviewContractRebind({
|
|
129
|
-
taskId: payload.taskId,
|
|
130
|
-
reviewerRoleName: payload.reviewerRoleName,
|
|
131
|
-
fromContract,
|
|
132
|
-
toControlPlaneDigest: payload.toControlPlaneDigest,
|
|
133
|
-
fromRelease: releaseIdentityFromPayload(payload, "from"),
|
|
134
|
-
toRelease: releaseIdentityFromPayload(payload, "to"),
|
|
135
|
-
handoverId: payload.handoverId,
|
|
136
|
-
authorizedBy: payload.authorizedBy
|
|
137
|
-
});
|
|
138
|
-
if (rebind.toContract.digest !== payload.toContractDigest) {
|
|
139
|
-
throw new Error(`Task final-review rebind target digest is invalid: ${event.id}.`);
|
|
140
|
-
}
|
|
141
|
-
return rebind;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Historical Candidate and Review records remain immutable. Before the first
|
|
145
|
-
* explicit rebind event, older Yui releases may have emitted successive
|
|
146
|
-
* control-plane contracts without a rebind event. Preserve that legacy prefix
|
|
147
|
-
* as a forward-only sequence, then require one strict append-only rebind chain.
|
|
148
|
-
* Any Reviewer change, reversion, fork, or post-rebind drift fails closed.
|
|
149
|
-
*/
|
|
150
|
-
export function resolveTaskFinalReviewContract(taskId, observations, events, historicalObservations = []) {
|
|
151
|
-
const normalizedTaskId = requireIdentity(taskId, "Task final-review contract Task id");
|
|
152
|
-
const orderedObservations = [...observations]
|
|
153
|
-
.map((observation) => ({
|
|
154
|
-
contract: validateTaskFinalReviewContract(observation.contract),
|
|
155
|
-
createdAt: requireTimestamp(observation.createdAt, "Task final-review observation createdAt"),
|
|
156
|
-
source: requireText(observation.source, "Task final-review observation source")
|
|
157
|
-
}))
|
|
158
|
-
.sort(compareCreatedAt);
|
|
159
|
-
const orderedHistoricalObservations = [...historicalObservations]
|
|
160
|
-
.map((observation) => ({
|
|
161
|
-
contract: validateTaskFinalReviewContract(observation.contract),
|
|
162
|
-
createdAt: requireTimestamp(observation.createdAt, "Task final-review historical observation createdAt"),
|
|
163
|
-
source: requireText(observation.source, "Task final-review historical observation source")
|
|
164
|
-
}))
|
|
165
|
-
.sort(compareCreatedAt);
|
|
166
|
-
const orderedEvents = events
|
|
167
|
-
.filter(({ type }) => type === TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT)
|
|
168
|
-
.map((event) => ({
|
|
169
|
-
event,
|
|
170
|
-
createdAt: requireTimestamp(event.createdAt, "Task final-review rebind event createdAt"),
|
|
171
|
-
rebind: taskFinalReviewContractRebindFromEvent(event)
|
|
172
|
-
}))
|
|
173
|
-
.sort((left, right) => compareCreatedAt(left, right)
|
|
174
|
-
|| left.event.id.localeCompare(right.event.id, undefined, { numeric: true }));
|
|
175
|
-
if (orderedObservations.length === 0 && orderedEvents.length === 0)
|
|
176
|
-
return undefined;
|
|
177
|
-
for (const observation of [...orderedObservations, ...orderedHistoricalObservations]) {
|
|
178
|
-
if (observation.contract.taskId !== normalizedTaskId) {
|
|
179
|
-
throw new Error(`${observation.source} carries a final-review contract for another Task.`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const firstRebindAt = orderedEvents[0]?.createdAt;
|
|
183
|
-
const primaryLegacyObservations = firstRebindAt === undefined
|
|
184
|
-
? orderedObservations
|
|
185
|
-
: orderedObservations.filter(({ createdAt }) => createdAt < firstRebindAt);
|
|
186
|
-
const historicalRebindSource = orderedEvents[0] === undefined
|
|
187
|
-
? undefined
|
|
188
|
-
: orderedHistoricalObservations.find((observation) => (observation.createdAt < orderedEvents[0].createdAt
|
|
189
|
-
&& sameTaskFinalReviewContract(observation.contract, orderedEvents[0].rebind.fromContract)));
|
|
190
|
-
const legacyObservations = primaryLegacyObservations.length > 0
|
|
191
|
-
? primaryLegacyObservations
|
|
192
|
-
: historicalRebindSource === undefined ? [] : [historicalRebindSource];
|
|
193
|
-
if (legacyObservations.length === 0) {
|
|
194
|
-
throw new Error(`Task ${normalizedTaskId} has a final-review rebind without an established source contract.`);
|
|
195
|
-
}
|
|
196
|
-
const initial = legacyObservations[0].contract;
|
|
197
|
-
const reviewerRoleName = initial.reviewerRoleName;
|
|
198
|
-
let effective = initial;
|
|
199
|
-
const legacyContractDigests = new Set([initial.digest]);
|
|
200
|
-
for (const observation of legacyObservations) {
|
|
201
|
-
if (observation.contract.reviewerRoleName !== reviewerRoleName) {
|
|
202
|
-
throw new Error(`${observation.source} changes the Task final-review Reviewer identity.`);
|
|
203
|
-
}
|
|
204
|
-
if (sameTaskFinalReviewContract(observation.contract, effective))
|
|
205
|
-
continue;
|
|
206
|
-
if (legacyContractDigests.has(observation.contract.digest)) {
|
|
207
|
-
throw new Error(`${observation.source} reverts a legacy Task final-review contract.`);
|
|
208
|
-
}
|
|
209
|
-
legacyContractDigests.add(observation.contract.digest);
|
|
210
|
-
effective = observation.contract;
|
|
211
|
-
}
|
|
212
|
-
for (const entry of orderedEvents) {
|
|
213
|
-
const { rebind } = entry;
|
|
214
|
-
if (!sameTaskFinalReviewContract(effective, rebind.fromContract)) {
|
|
215
|
-
throw new Error(`Task final-review rebind chain forks at ${entry.event.id}.`);
|
|
216
|
-
}
|
|
217
|
-
if (rebind.reviewerRoleName !== initial.reviewerRoleName) {
|
|
218
|
-
throw new Error(`Task final-review rebind changes Reviewer identity at ${entry.event.id}.`);
|
|
219
|
-
}
|
|
220
|
-
effective = rebind.toContract;
|
|
221
|
-
}
|
|
222
|
-
for (const observation of orderedObservations) {
|
|
223
|
-
if (firstRebindAt === undefined || observation.createdAt < firstRebindAt)
|
|
224
|
-
continue;
|
|
225
|
-
let expected = legacyObservations.at(-1).contract;
|
|
226
|
-
for (const entry of orderedEvents) {
|
|
227
|
-
if (entry.createdAt > observation.createdAt)
|
|
228
|
-
break;
|
|
229
|
-
expected = entry.rebind.toContract;
|
|
230
|
-
}
|
|
231
|
-
if (!sameTaskFinalReviewContract(observation.contract, expected)) {
|
|
232
|
-
throw new Error(`${observation.source} does not match the effective Task final-review contract at creation.`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return Object.freeze({
|
|
236
|
-
initial,
|
|
237
|
-
effective,
|
|
238
|
-
rebinds: Object.freeze(orderedEvents.map(({ rebind }) => rebind))
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Builds the immutable release proof while the caller holds the release
|
|
243
|
-
* handover lock. The current exact control descriptor is supplied only by the
|
|
244
|
-
* CLI preflight; caller-provided target identities are checked against it.
|
|
245
|
-
*/
|
|
246
|
-
export function prepareTaskFinalReviewContractRebindProof(input) {
|
|
247
|
-
const home = resolve(input.home);
|
|
248
|
-
const taskId = requireIdentity(input.taskId, "Task final-review rebind Task id");
|
|
249
|
-
const fromControlPlaneDigest = requireDigest(input.fromControlPlaneDigest, "Task final-review rebind source control-plane digest");
|
|
250
|
-
const toControlPlaneDigest = requireDigest(input.toControlPlaneDigest, "Task final-review rebind target control-plane digest");
|
|
251
|
-
if (fromControlPlaneDigest === toControlPlaneDigest) {
|
|
252
|
-
throw new Error("Task final-review rebind source and target control planes are identical.");
|
|
253
|
-
}
|
|
254
|
-
if (exactControlPlaneDigest(input.currentControlPlane) !== toControlPlaneDigest) {
|
|
255
|
-
throw new Error("Task final-review rebind target is not the current exact control plane.");
|
|
256
|
-
}
|
|
257
|
-
if (resolve(input.currentControlPlane.yuiHome) !== home) {
|
|
258
|
-
throw new Error("Task final-review rebind target control plane belongs to another Home.");
|
|
259
|
-
}
|
|
260
|
-
const fromReleaseId = requireText(input.fromReleaseId, "Source release id");
|
|
261
|
-
const toReleaseId = requireText(input.toReleaseId, "Target release id");
|
|
262
|
-
if (fromReleaseId === toReleaseId) {
|
|
263
|
-
throw new Error("Task final-review rebind source and target releases are identical.");
|
|
264
|
-
}
|
|
265
|
-
if (readHandoverFence(home) !== null) {
|
|
266
|
-
throw new Error("Task final-review rebind is blocked by an unfinished release handover.");
|
|
267
|
-
}
|
|
268
|
-
const active = readActiveReleasePointer(home);
|
|
269
|
-
if (active === null
|
|
270
|
-
|| active.releaseId !== toReleaseId
|
|
271
|
-
|| active.buildId !== input.currentControlPlane.buildId
|
|
272
|
-
|| active.packageDigest !== input.currentControlPlane.activeReleaseDigest) {
|
|
273
|
-
throw new Error("The active release does not match the requested target release and exact control plane.");
|
|
274
|
-
}
|
|
275
|
-
const sourcePath = join(home, "runtime", "control-plane", `${fromControlPlaneDigest}.json`);
|
|
276
|
-
const sourceControl = parseExactControlPlaneDescriptor(readFileSync(sourcePath, "utf8"));
|
|
277
|
-
if (exactControlPlaneDigest(sourceControl) !== fromControlPlaneDigest) {
|
|
278
|
-
throw new Error("Task final-review rebind source control-plane descriptor digest is invalid.");
|
|
279
|
-
}
|
|
280
|
-
if (resolve(sourceControl.yuiHome) !== home) {
|
|
281
|
-
throw new Error("Task final-review rebind source control plane belongs to another Home.");
|
|
282
|
-
}
|
|
283
|
-
const fromRelease = installedReleaseIdentity(home, fromReleaseId, sourceControl, "source");
|
|
284
|
-
const toRelease = installedReleaseIdentity(home, toReleaseId, input.currentControlPlane, "target");
|
|
285
|
-
const handover = readHandoverReceipt(home);
|
|
286
|
-
if (handover === null
|
|
287
|
-
|| handover.outcome !== "completed"
|
|
288
|
-
|| handover.activatedReleaseId !== toReleaseId
|
|
289
|
-
|| typeof handover.handoverId !== "string"
|
|
290
|
-
|| handover.handoverId.length === 0) {
|
|
291
|
-
throw new Error("Task final-review rebind requires the completed release handover that activated the target.");
|
|
292
|
-
}
|
|
293
|
-
return Object.freeze({
|
|
294
|
-
schemaVersion: 1,
|
|
295
|
-
taskId,
|
|
296
|
-
fromControlPlaneDigest,
|
|
297
|
-
toControlPlaneDigest,
|
|
298
|
-
fromRelease,
|
|
299
|
-
toRelease,
|
|
300
|
-
handoverId: handover.handoverId
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
function installedReleaseIdentity(home, releaseId, control, label) {
|
|
304
|
-
if (control.buildId === undefined || control.activeReleaseDigest === undefined) {
|
|
305
|
-
throw new Error(`Task final-review rebind ${label} control plane is not an immutable release.`);
|
|
306
|
-
}
|
|
307
|
-
let manifest;
|
|
308
|
-
try {
|
|
309
|
-
manifest = verifyReleaseIntegrity(join(releasesDirectory(home), releaseId));
|
|
310
|
-
}
|
|
311
|
-
catch (error) {
|
|
312
|
-
throw new Error(`Task final-review rebind ${label} release is unknown or invalid: ${releaseId}.`, { cause: error });
|
|
313
|
-
}
|
|
314
|
-
if (releaseDirectoryName(manifest) !== releaseId
|
|
315
|
-
|| manifest.buildId !== control.buildId
|
|
316
|
-
|| manifest.packageDigest !== control.activeReleaseDigest) {
|
|
317
|
-
throw new Error(`Task final-review rebind ${label} release does not match its control plane.`);
|
|
318
|
-
}
|
|
319
|
-
return validateReleaseIdentity({
|
|
320
|
-
releaseId,
|
|
321
|
-
version: manifest.version,
|
|
322
|
-
buildId: manifest.buildId,
|
|
323
|
-
packageDigest: manifest.packageDigest,
|
|
324
|
-
...(manifest.sourceCommit === undefined ? {} : { sourceCommit: manifest.sourceCommit })
|
|
325
|
-
}, label);
|
|
326
|
-
}
|
|
327
|
-
function releaseIdentityFromPayload(payload, prefix) {
|
|
328
|
-
const title = prefix === "from" ? "From" : "To";
|
|
329
|
-
return validateReleaseIdentity({
|
|
330
|
-
releaseId: payload[`${prefix}ReleaseId`],
|
|
331
|
-
version: payload[`${prefix}ReleaseVersion`],
|
|
332
|
-
buildId: payload[`${prefix}ReleaseBuildId`],
|
|
333
|
-
packageDigest: payload[`${prefix}ReleasePackageDigest`],
|
|
334
|
-
...(payload[`${prefix}ReleaseSourceCommit`] === undefined
|
|
335
|
-
? {}
|
|
336
|
-
: { sourceCommit: payload[`${prefix}ReleaseSourceCommit`] })
|
|
337
|
-
}, title);
|
|
338
|
-
}
|
|
339
|
-
function validateReleaseIdentity(value, label) {
|
|
340
|
-
return Object.freeze({
|
|
341
|
-
releaseId: requireText(value.releaseId, `Task final-review rebind ${label} release id`),
|
|
342
|
-
version: requireText(value.version, `Task final-review rebind ${label} release version`),
|
|
343
|
-
buildId: requireText(value.buildId, `Task final-review rebind ${label} release build id`),
|
|
344
|
-
packageDigest: requireDigest(value.packageDigest, `Task final-review rebind ${label} release package digest`),
|
|
345
|
-
...(value.sourceCommit === undefined
|
|
346
|
-
? {}
|
|
347
|
-
: { sourceCommit: requireText(value.sourceCommit, `Task final-review rebind ${label} source commit`) })
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
function compareCreatedAt(left, right) {
|
|
351
|
-
return left.createdAt.localeCompare(right.createdAt);
|
|
352
|
-
}
|
|
353
|
-
function requireDigest(value, label) {
|
|
354
|
-
if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
|
|
355
|
-
throw new Error(`${label} is invalid.`);
|
|
356
|
-
}
|
|
357
|
-
return value;
|
|
358
|
-
}
|
|
359
|
-
function requireText(value, label) {
|
|
360
|
-
if (typeof value !== "string" || value.includes("\0")) {
|
|
361
|
-
throw new Error(`${label} is invalid.`);
|
|
362
|
-
}
|
|
363
|
-
const normalized = value.trim();
|
|
364
|
-
if (normalized.length === 0)
|
|
365
|
-
throw new Error(`${label} is required.`);
|
|
366
|
-
return normalized;
|
|
367
|
-
}
|