@zq-silk/yui 0.13.10 → 0.14.1

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.
Files changed (230) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +113 -300
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  5. package/dist/cli/commandCatalog.js +39 -77
  6. package/dist/cli/interactionCandidates.js +4 -4
  7. package/dist/cli/interactionPolicy.js +7 -10
  8. package/dist/cli/invocationRouter.js +1 -1
  9. package/dist/cli/updateCommand.js +6 -27
  10. package/dist/cli/updateOrchestrator.js +63 -484
  11. package/dist/cli/updatePorts.js +15 -333
  12. package/dist/cli/upgradeCommand.js +8 -65
  13. package/dist/cli.js +57 -242
  14. package/dist/commands/configCommands.js +11 -62
  15. package/dist/commands/executionAuditCommands.js +23 -23
  16. package/dist/commands/globalRoleCommands.js +2 -7
  17. package/dist/commands/jobCommands.js +1 -4
  18. package/dist/commands/projectCommands.js +5 -5
  19. package/dist/commands/releaseCommands.js +3 -10
  20. package/dist/commands/resourcesCommands.js +2 -2
  21. package/dist/commands/sessionCommands.js +2 -2
  22. package/dist/commands/taskActor.js +31 -39
  23. package/dist/commands/taskChangeSetCommands.js +5 -12
  24. package/dist/commands/taskCommands.js +464 -1249
  25. package/dist/commands/taskContextCommand.js +35 -36
  26. package/dist/commands/taskExecutionCommands.js +33 -46
  27. package/dist/commands/taskInputCommands.js +20 -42
  28. package/dist/commands/taskNextActionCommand.js +9 -9
  29. package/dist/commands/taskOverviewCommand.js +56 -95
  30. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  31. package/dist/commands/taskUpstreamCommands.js +3 -3
  32. package/dist/commands/taskWorkspaceCommands.js +7 -79
  33. package/dist/commands/telemetryCommands.js +32 -124
  34. package/dist/commands/workflowCommands.js +3 -3
  35. package/dist/completion/fileCompletionManager.js +1 -1
  36. package/dist/config/configCatalog.js +4 -6
  37. package/dist/config/yuiConfig.js +6 -43
  38. package/dist/context/dispatchContext.js +14 -30
  39. package/dist/context/roleSessionContext.js +2 -2
  40. package/dist/context/sessionBootstrapManifest.js +20 -3
  41. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  42. package/dist/context/turnInputContract.js +204 -0
  43. package/dist/context/wakeNotification.js +2 -2
  44. package/dist/controller/agentRuntimeObserver.js +18 -18
  45. package/dist/controller/clientRuntime.js +13 -20
  46. package/dist/controller/controller.js +89 -116
  47. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  48. package/dist/controller/jobControl.js +17 -17
  49. package/dist/controller/resourceInventory.js +6 -6
  50. package/dist/controller/resourceInventoryLinux.js +38 -59
  51. package/dist/controller/resourceInventoryRpc.js +3 -3
  52. package/dist/controller/resourceInventoryWorker.js +1 -1
  53. package/dist/controller/runtime.js +46 -53
  54. package/dist/controller/runtimeEventInbox.js +23 -31
  55. package/dist/controller/runtimeEventProcessor.js +15 -15
  56. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  57. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  58. package/dist/controller/runtimeObservationHook.js +11 -8
  59. package/dist/controller/sessionNotify.js +11 -11
  60. package/dist/controller/structuredProviderObservation.js +89 -16
  61. package/dist/coordination/workMailbox.js +309 -579
  62. package/dist/coordination/workMailboxQueue.js +5 -6
  63. package/dist/core/controllerClient.js +1 -91
  64. package/dist/core/controllerServer.js +3 -6
  65. package/dist/doctor/doctor.js +51 -314
  66. package/dist/execution/candidateConvergence.js +3 -3
  67. package/dist/execution/executionGroup.js +32 -33
  68. package/dist/execution/executionHealth.js +36 -38
  69. package/dist/execution/resourceBroker.js +12 -12
  70. package/dist/executor/agentExecutor.js +30 -265
  71. package/dist/executor/effectiveLaunch.js +9 -19
  72. package/dist/executor/executorRegistry.js +43 -17
  73. package/dist/executor/fileRoleLaunchPlanner.js +91 -57
  74. package/dist/executor/launchPlan.js +3 -3
  75. package/dist/executor/workspacePreflightClassification.js +16 -16
  76. package/dist/grant/capabilityGrant.js +20 -25
  77. package/dist/input/inputRequest.js +12 -12
  78. package/dist/integration/changeSet.js +9 -13
  79. package/dist/integration/changeSetManifest.js +1 -1
  80. package/dist/integration/gitIntegrationService.js +2 -2
  81. package/dist/integration/integrationQueueService.js +5 -21
  82. package/dist/integration/overlapDiagnostics.js +2 -2
  83. package/dist/interaction/operatorPresentation.js +1 -1
  84. package/dist/job/jobRunner.js +1 -1
  85. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  86. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  87. package/dist/message/message.js +6 -6
  88. package/dist/observability/executionAudit.js +74 -86
  89. package/dist/observability/faultClassification.js +7 -24
  90. package/dist/observability/orchestrationMetrics.js +5 -5
  91. package/dist/observability/runtimeIdentity.js +34 -101
  92. package/dist/profile/agentProfile.js +1 -1
  93. package/dist/release/cliHomeReleaseFence.js +3 -3
  94. package/dist/release/releaseWorkflowEngine.js +6 -10
  95. package/dist/release/releaseWorkflowPorts.js +2 -2
  96. package/dist/release/workflowFileLock.js +1 -1
  97. package/dist/repository/checkoutSwap.js +1 -1
  98. package/dist/repository/gitWorkspace.js +0 -13
  99. package/dist/repository/project.js +1 -1
  100. package/dist/repository/projectMaintenanceLock.js +1 -2
  101. package/dist/repository/taskBaseFreshness.js +13 -13
  102. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  103. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  104. package/dist/repository/taskWorkspacePreparer.js +63 -620
  105. package/dist/resources/autoResourceGc.js +3 -3
  106. package/dist/resources/liveReferences.js +1 -1
  107. package/dist/resources/resourceGc.js +1 -1
  108. package/dist/resources/sqliteResourceRegistry.js +1 -1
  109. package/dist/review/reviewDecision.js +7 -7
  110. package/dist/review/reviewFindingLedger.js +1 -1
  111. package/dist/review/reviewOutcomeClassifier.js +35 -50
  112. package/dist/review/reviewRound.js +14 -25
  113. package/dist/review/reviewerAvailability.js +4 -4
  114. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  115. package/dist/role/role.js +6 -3
  116. package/dist/runtime/agentDriver.js +4 -0
  117. package/dist/runtime/agentHost.js +151 -36
  118. package/dist/runtime/builtinAgentDrivers.js +79 -2
  119. package/dist/runtime/codexAppServerRuntime.js +122 -0
  120. package/dist/runtime/continuationManager.js +3 -5
  121. package/dist/runtime/exactControlPlane.js +17 -42
  122. package/dist/runtime/firstProgressAdvisory.js +1 -1
  123. package/dist/runtime/index.js +2 -2
  124. package/dist/runtime/launchDiagnostics.js +2 -2
  125. package/dist/runtime/lifecycleReservation.js +4 -4
  126. package/dist/runtime/processExitObservation.js +1 -1
  127. package/dist/runtime/processExitOutbox.js +0 -32
  128. package/dist/runtime/promptEnvelope.js +9 -9
  129. package/dist/runtime/providerContinuation.js +2 -2
  130. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  131. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  132. package/dist/runtime/recentTurnIds.js +38 -0
  133. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  134. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  135. package/dist/runtime/runtimeObservation.js +58 -21
  136. package/dist/runtime/runtimeProjection.js +19 -27
  137. package/dist/runtime/sessionLaunchRequest.js +2 -2
  138. package/dist/runtime/structuredProviderHost.js +177 -12
  139. package/dist/runtime/tmuxAdapters.js +46 -11
  140. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  141. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  142. package/dist/scheduler/operatorEvent.js +4 -6
  143. package/dist/scheduler/resourceQueueProjection.js +71 -0
  144. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  145. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  146. package/dist/scheduler/taskExecutionProjection.js +99 -99
  147. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  148. package/dist/scheduler/taskWake.js +4 -4
  149. package/dist/scheduler/wakeReason.js +6 -10
  150. package/dist/scheduler/wakeupQueue.js +0 -9
  151. package/dist/setup/setupCommand.js +2 -2
  152. package/dist/storage/currentTaskStore.js +46 -0
  153. package/dist/storage/persistenceWorker.js +1 -1
  154. package/dist/storage/sqliteSchema.js +81 -485
  155. package/dist/storage/sqliteStore.js +206 -365
  156. package/dist/storage/storageSchema.js +1 -15
  157. package/dist/storage/storageVersions.js +8 -9
  158. package/dist/storage/storeRpc.js +10 -34
  159. package/dist/storage/taskStore.js +63 -3415
  160. package/dist/storage/upgrade/recordVersions.js +55 -149
  161. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  162. package/dist/task/completionReadiness.js +5 -5
  163. package/dist/task/deliveryGuard.js +10 -10
  164. package/dist/task/nextAction.js +74 -77
  165. package/dist/task/task.js +2 -4
  166. package/dist/task/taskRecordReference.js +3 -3
  167. package/dist/task/taskRecordRetirement.js +1 -1
  168. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  169. package/dist/telemetry/telemetryConfig.js +14 -14
  170. package/dist/telemetry/telemetryWiring.js +8 -14
  171. package/dist/turn/turn.js +356 -0
  172. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  173. package/dist/verification/gateArtifactStore.js +2 -2
  174. package/dist/web/assets/client/app.js +1 -1
  175. package/dist/web/assets/client/components.js +29 -54
  176. package/dist/web/assets/client/i18n.js +17 -37
  177. package/dist/web/assets/client/view.js +37 -37
  178. package/dist/web/assets/shell.js +1 -1
  179. package/dist/web/assets/styles/cards.js +5 -5
  180. package/dist/web/assets/styles/widgets.js +1 -2
  181. package/dist/web/webSnapshot.js +65 -60
  182. package/dist/workItem/workItem.js +11 -11
  183. package/dist/workspace/workItemChangeSetManager.js +1 -1
  184. package/docs/task-local-identity.md +5 -5
  185. package/i18n/README.zh-CN.md +42 -55
  186. package/package.json +1 -1
  187. package/skills/yui-leader/SKILL.md +85 -83
  188. package/skills/yui-operator/SKILL.md +16 -16
  189. package/skills/yui-reviewer/SKILL.md +10 -10
  190. package/skills/yui-runtime/SKILL.md +22 -24
  191. package/skills/yui-worker/SKILL.md +16 -18
  192. package/dist/context/runContextContract.js +0 -162
  193. package/dist/executor/turnCompletion.js +0 -126
  194. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  195. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  196. package/dist/run/agentRun.js +0 -356
  197. package/dist/run/rejectedYieldAttempt.js +0 -221
  198. package/dist/run/runControlRequest.js +0 -50
  199. package/dist/run/yieldReceipt.js +0 -65
  200. package/dist/runtime/turnCompletion.js +0 -3
  201. package/dist/scheduler/actionability.js +0 -323
  202. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  203. package/dist/scheduler/activeTaskProgress.js +0 -126
  204. package/dist/storage/compatibleTaskStore.js +0 -248
  205. package/dist/storage/migration/baseline.js +0 -78
  206. package/dist/storage/migration/classifier.js +0 -51
  207. package/dist/storage/migration/compatibleCodec.js +0 -53
  208. package/dist/storage/migration/engine.js +0 -147
  209. package/dist/storage/migration/index.js +0 -33
  210. package/dist/storage/migration/planner.js +0 -154
  211. package/dist/storage/migration/productionRegistry.js +0 -3931
  212. package/dist/storage/migration/registry.js +0 -169
  213. package/dist/storage/migration/report.js +0 -54
  214. package/dist/storage/migration/types.js +0 -31
  215. package/dist/storage/upgrade/homeClassification.js +0 -353
  216. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  217. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  218. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  219. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  220. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  221. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  222. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  223. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  224. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  225. package/dist/storage/upgrade/sqliteStateMigration.js +0 -926
  226. package/dist/storage/upgrade/switchProgress.js +0 -80
  227. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  228. package/dist/storage/upgradeCoordination.js +0 -187
  229. package/dist/storage/upgradeFence.js +0 -366
  230. 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
- }