@zq-silk/yui 0.8.2 → 0.8.6

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 (68) hide show
  1. package/ARCHITECTURE.md +40 -22
  2. package/README.md +61 -9
  3. package/dist/cli/commandCatalog.js +31 -14
  4. package/dist/cli/operatorWizard.js +10 -20
  5. package/dist/cli.js +187 -22
  6. package/dist/commands/executionAuditCommands.js +30 -0
  7. package/dist/commands/operatorCommands.js +42 -1
  8. package/dist/commands/taskCommands.js +648 -74
  9. package/dist/commands/taskCompletionGate.js +166 -2
  10. package/dist/commands/taskContextCommand.js +6 -1
  11. package/dist/commands/taskInputCommands.js +48 -10
  12. package/dist/commands/taskNextActionCommand.js +36 -3
  13. package/dist/context/runContextPack.js +19 -4
  14. package/dist/context/sessionBootstrapManifest.js +83 -2
  15. package/dist/controller/clientRuntime.js +7 -7
  16. package/dist/controller/controller.js +16 -8
  17. package/dist/controller/fileSchedulerStoreAdapter.js +64 -5
  18. package/dist/controller/handoverCandidate.js +10 -3
  19. package/dist/controller/sessionNotify.js +4 -22
  20. package/dist/executor/agentAdapter.js +2 -2
  21. package/dist/executor/agentExecutor.js +25 -5
  22. package/dist/executor/fileRoleLaunchPlanner.js +16 -11
  23. package/dist/integration/gitIntegrationService.js +50 -2
  24. package/dist/integration/integrationCheckEvidenceReuse.js +53 -0
  25. package/dist/observability/executionAudit.js +47 -1
  26. package/dist/observability/faultClassification.js +6 -4
  27. package/dist/observability/orchestrationMetrics.js +196 -0
  28. package/dist/operator/operatorSessionHistory.js +36 -0
  29. package/dist/release/releaseHandover.js +7 -5
  30. package/dist/release/runtimeRelease.js +15 -0
  31. package/dist/repository/gitWorkspace.js +7 -4
  32. package/dist/repository/taskBaseFreshness.js +4 -2
  33. package/dist/repository/taskWorkspaceCoordinator.js +13 -10
  34. package/dist/review/deltaRecheck.js +3 -2
  35. package/dist/review/reviewFindingLedger.js +5 -4
  36. package/dist/review/reviewOutcomeClassifier.js +252 -54
  37. package/dist/review/taskFinalReviewContractEvent.js +1 -0
  38. package/dist/review/taskFinalReviewContractRebind.js +350 -0
  39. package/dist/run/agentRun.js +2 -2
  40. package/dist/run/runIdentity.js +10 -70
  41. package/dist/runtime/agentHost.js +3 -4
  42. package/dist/runtime/codexAppServerRuntime.js +6 -0
  43. package/dist/runtime/firstProgressStopLoss.js +52 -0
  44. package/dist/runtime/launchBroker.js +10 -2
  45. package/dist/runtime/runtimeDeadlines.js +14 -0
  46. package/dist/runtime/sessionTitle.js +24 -12
  47. package/dist/runtime/structuredProviderHost.js +7 -1
  48. package/dist/runtime/tmuxAdapters.js +10 -3
  49. package/dist/scheduler/activeRoleRunDelivery.js +20 -18
  50. package/dist/scheduler/leaderWakeupProcessor.js +33 -2
  51. package/dist/scheduler/wakeReason.js +2 -0
  52. package/dist/storage/sqliteStore.js +10 -1
  53. package/dist/storage/taskStore.js +12 -1
  54. package/dist/task/completionReadiness.js +91 -19
  55. package/dist/task/deliveryGuard.js +3 -1
  56. package/dist/task/nextAction.js +146 -51
  57. package/dist/task/publicationReference.js +1 -0
  58. package/dist/task/repairWave.js +14 -1
  59. package/dist/task/task.js +10 -0
  60. package/dist/web/webSnapshot.js +7 -1
  61. package/dist/workItem/workItem.js +12 -0
  62. package/dist/workspace/workItemChangeSetManager.js +2 -1
  63. package/i18n/README.zh-CN.md +28 -8
  64. package/package.json +1 -1
  65. package/skills/yui-leader/SKILL.md +79 -32
  66. package/skills/yui-operator/SKILL.md +51 -10
  67. package/skills/yui-reviewer/SKILL.md +23 -0
  68. package/skills/yui-runtime/SKILL.md +7 -2
@@ -1,61 +1,259 @@
1
+ import { matchYieldReceipt } from "../run/yieldReceipt.js";
1
2
  const INFRA_SIGNATURES = [
2
- {
3
- kind: "session-not-stopped",
4
- pattern: /session must be stopped before workspace migration/iu
5
- },
6
- {
7
- kind: "run-start",
8
- pattern: /role run could not start|could not start (?:the )?(?:reviewer|role) run/iu
9
- },
10
- {
11
- kind: "storage-lock",
12
- pattern: /\.state\.lock|state lock|lock timeout|storage lock/iu
13
- },
14
- {
15
- kind: "tmux-exit",
16
- pattern: /tmux[^\n]{0,80}(?:exited|exit|died|vanished)|pane (?:exited|died)/iu
17
- },
18
- {
19
- kind: "yield-timeout",
20
- pattern: /yield timeout|controller yield timeout|yield timed out/iu
21
- },
22
- {
23
- kind: "run-identity",
24
- pattern: /wrong run id|unknown run id|run id (?:is )?(?:invalid|unknown|mismatch)/iu
25
- },
26
- {
27
- kind: "policy",
28
- pattern: /cyber_?policy|policy denial|permission denied by policy/iu
29
- },
30
- {
31
- kind: "baseline-contamination",
32
- pattern: /cross[-\s]?baseline|baseline pollution|contaminated baseline|wrong base sha/iu
33
- }
3
+ { kind: "session-not-stopped", pattern: /session must be stopped before workspace migration/iu },
4
+ { kind: "run-start", pattern: /role run could not start|could not start (?:the )?(?:reviewer|role) run/iu },
5
+ { kind: "storage-lock", pattern: /\.state\.lock|state lock|lock timeout|storage lock/iu },
6
+ { kind: "tmux-exit", pattern: /tmux[^\n]{0,80}(?:exited|exit|died|vanished)|pane (?:exited|died)/iu },
7
+ { kind: "yield-timeout", pattern: /yield timeout|controller yield timeout|yield timed out/iu },
8
+ { kind: "run-identity", pattern: /wrong run id|unknown run id|run id (?:is )?(?:invalid|unknown|mismatch)/iu },
9
+ { kind: "policy", pattern: /cyber_?policy|policy denial|permission denied by policy/iu },
10
+ { kind: "baseline-contamination", pattern: /cross[-\s]?baseline|baseline pollution|contaminated baseline|wrong base sha/iu },
11
+ { kind: "context-load", pattern: /(?:run )?(?:context|context pack) load (?:failed|unavailable|unauthorized|stale|mismatched|malformed)/iu },
12
+ { kind: "workspace-binding", pattern: /workspace is not the durable owner|workspace-binding failure/iu }
34
13
  ];
35
- /**
36
- * Classifies a terminal Round. `completed` is always semantic (the reviewer
37
- * delivered a report); `failed` is always infra. Non-terminal Rounds have no
38
- * outcome yet.
39
- */
40
- export function classifyReviewRoundOutcome(round) {
41
- if (round.status === "completed") {
42
- return { kind: "semantic", reason: "Reviewer delivered a report." };
43
- }
44
- if (round.status !== "failed")
14
+ /** Classify one terminal Round without rewriting it. */
15
+ export function classifyReviewRoundOutcome(round, evidence) {
16
+ if (round.status !== "completed" && round.status !== "failed")
45
17
  return null;
46
- const text = `${round.summary ?? ""}\n${round.report ?? ""}`;
47
- for (const { kind, pattern } of INFRA_SIGNATURES) {
48
- if (pattern.test(text)) {
49
- return { kind: "infra", infraKind: kind, reason: `Review execution attempt failed (${kind}).` };
18
+ const infraKind = classifyInfraKind(`${round.summary ?? ""}\n${round.report ?? ""}`);
19
+ if (round.status === "failed") {
20
+ const semanticEvidence = failedRoundSemanticEvidence(round, evidence);
21
+ return semanticEvidence === null
22
+ ? {
23
+ kind: "non-semantic",
24
+ infraKind,
25
+ reason: "Failed Review execution carries no semantic report evidence."
26
+ }
27
+ : {
28
+ kind: "ambiguous",
29
+ infraKind,
30
+ reason: semanticEvidence
31
+ };
32
+ }
33
+ if (!explicitCompletedReviewInfrastructureFailure(round.summary ?? "", round)) {
34
+ return { kind: "semantic", reason: "Reviewer delivered a terminal report." };
35
+ }
36
+ if (evidence === undefined) {
37
+ return {
38
+ kind: "ambiguous",
39
+ infraKind,
40
+ reason: "Completed Round claims an infrastructure failure but corroborating Run/Event evidence was not supplied."
41
+ };
42
+ }
43
+ const corroborationFailure = completedInfrastructureCorroborationFailure(round, evidence);
44
+ return corroborationFailure === null
45
+ ? {
46
+ kind: "non-semantic",
47
+ infraKind,
48
+ reason: "Completed Round and yielded Run agree on an explicit pre-review infrastructure failure."
49
+ }
50
+ : {
51
+ kind: "ambiguous",
52
+ infraKind,
53
+ reason: corroborationFailure
54
+ };
55
+ }
56
+ /** True only when a Round may consume semantic Review budget or feed findings. */
57
+ export function isSemanticReviewRound(round, evidence) {
58
+ return classifyReviewRoundOutcome(round, evidence)?.kind === "semantic";
59
+ }
60
+ function classifyInfraKind(text) {
61
+ return INFRA_SIGNATURES.find(({ pattern }) => pattern.test(text))?.kind ?? "other-infra";
62
+ }
63
+ function failedRoundSemanticEvidence(round, evidence) {
64
+ if ((round.checks ?? []).length > 0)
65
+ return "Failed Round records review checks.";
66
+ if (round.evidenceCommit !== undefined)
67
+ return "Failed Round records a review evidence commit.";
68
+ if (round.deltaRecheck?.disposition !== undefined || round.deltaRecheck?.reasoning !== undefined) {
69
+ return "Failed Round records a semantic delta-recheck disposition.";
70
+ }
71
+ if (round.report !== round.summary)
72
+ return "Failed Round stores a report distinct from its terminal summary.";
73
+ if (runtimeFailureSummaryHasReviewerOutput(round.report ?? "")) {
74
+ return "Failed Round stores non-empty Reviewer output in its runtime failure summary.";
75
+ }
76
+ if (looksLikeStructuredReviewReport(round.report ?? "")) {
77
+ return "Failed Round stores a structured Reviewer report.";
78
+ }
79
+ const semanticLane = (round.executionGroup?.lanes ?? []).find((lane) => ((lane.result?.checks ?? []).length > 0
80
+ || (lane.result?.findings ?? []).length > 0
81
+ || (lane.result?.evidence ?? []).length > 0
82
+ || lane.result?.evidenceCommit !== undefined
83
+ || lane.result?.gitSnapshot !== undefined
84
+ || lane.status === "yielded"
85
+ || lane.status === "completed"));
86
+ if (semanticLane !== undefined)
87
+ return `Reviewer Lane ${semanticLane.id} delivered semantic evidence.`;
88
+ if (evidence !== undefined) {
89
+ const reviewRun = evidence.listAgentRuns(round.taskId).find((run) => (run.purpose === "review"
90
+ && run.reviewRoundId === round.id
91
+ && (run.status === "yielded" || runtimeFailureSummaryHasReviewerOutput(run.summary ?? ""))));
92
+ if (reviewRun !== undefined)
93
+ return `Reviewer Run ${reviewRun.id} records Reviewer output.`;
94
+ const finding = evidence.listReviewFindings(round.taskId).find((entry) => (entry.firstReviewRoundId === round.id || entry.lastReviewRoundId === round.id));
95
+ if (finding !== undefined)
96
+ return `Review finding ${finding.id} references the Round.`;
97
+ const completion = evidence.listEvents(round.taskId).find((event) => (event.type === "review.completed" && event.payload.reviewRoundId === round.id));
98
+ if (completion !== undefined)
99
+ return `Review completion Event ${completion.id} exists.`;
100
+ }
101
+ return null;
102
+ }
103
+ function completedInfrastructureCorroborationFailure(round, store) {
104
+ if ((round.checks ?? []).length > 0)
105
+ return "Completed Round records review checks.";
106
+ if (round.evidenceCommit !== round.reviewBaseCommit) {
107
+ return "Completed Round lacks an exact frozen-head evidence commit.";
108
+ }
109
+ if (round.report !== round.summary) {
110
+ return "Completed Round stores a report distinct from its terminal summary.";
111
+ }
112
+ if (runtimeFailureSummaryHasReviewerOutput(round.report ?? "")) {
113
+ return "Completed Round stores non-empty Reviewer output in its runtime failure summary.";
114
+ }
115
+ if (round.deltaRecheck?.disposition !== undefined || round.deltaRecheck?.reasoning !== undefined) {
116
+ return "Completed Round records a semantic delta-recheck disposition.";
117
+ }
118
+ if (looksLikeStructuredReviewReport(round.report ?? "")) {
119
+ return "Completed Round stores a structured Reviewer report.";
120
+ }
121
+ for (const lane of round.executionGroup?.lanes ?? []) {
122
+ if (lane.status === "pending" || lane.status === "running") {
123
+ return `Reviewer Lane ${lane.id} is still active.`;
124
+ }
125
+ if ((lane.result?.checks ?? []).length > 0
126
+ || (lane.result?.findings ?? []).length > 0
127
+ || (lane.result?.evidence ?? []).length > 0
128
+ || (lane.result?.evidenceCommit !== undefined
129
+ && lane.result.evidenceCommit !== round.reviewBaseCommit)
130
+ || lane.result?.gitSnapshot !== undefined) {
131
+ return `Reviewer Lane ${lane.id} delivered semantic evidence.`;
132
+ }
133
+ if (lane.status === "yielded" || lane.status === "completed") {
134
+ if (lane.result === undefined
135
+ || lane.result.summary !== round.summary
136
+ || lane.result.report !== round.report
137
+ || lane.result.evidenceCommit !== round.reviewBaseCommit) {
138
+ return `Reviewer Lane ${lane.id} output is absent or differs from the Round.`;
139
+ }
140
+ }
141
+ else {
142
+ return `Completed Round has non-completed Reviewer Lane ${lane.id}/${lane.status}.`;
143
+ }
144
+ }
145
+ const runs = store.listAgentRuns(round.taskId).filter((run) => (run.purpose === "review" && run.reviewRoundId === round.id));
146
+ const active = runs.find(({ status }) => status === "active");
147
+ if (active !== undefined)
148
+ return `Reviewer Run ${active.id} is still active.`;
149
+ const output = runs.find((run) => (run.status === "failed" && runtimeFailureSummaryHasReviewerOutput(run.summary ?? "")));
150
+ if (output !== undefined)
151
+ return `Reviewer Run ${output.id} records Reviewer output.`;
152
+ const yielded = runs.filter(({ status }) => status === "yielded");
153
+ if (round.reviewerRunId === undefined
154
+ || yielded.length !== 1
155
+ || yielded[0].id !== round.reviewerRunId) {
156
+ return "Completed Round lacks one exact yielded Reviewer Run.";
157
+ }
158
+ const run = yielded[0];
159
+ if (run.roleName !== round.reviewerRoleName
160
+ || run.summary !== round.summary
161
+ || run.yieldReceipt === undefined) {
162
+ return `Reviewer Run ${run.id} does not match the non-semantic Round receipt.`;
163
+ }
164
+ const receiptMatch = matchYieldReceipt(run.yieldReceipt, {
165
+ status: "yielded",
166
+ summary: round.summary ?? "",
167
+ reviewResult: {
168
+ report: round.report ?? "",
169
+ checks: round.checks ?? [],
170
+ evidenceCommit: round.reviewBaseCommit
50
171
  }
172
+ });
173
+ if (receiptMatch?.kind !== "replayed") {
174
+ return `Reviewer Run ${run.id} yield receipt does not cover the Round outcome.`;
175
+ }
176
+ const finding = store.listReviewFindings(round.taskId).find((entry) => (entry.firstReviewRoundId === round.id || entry.lastReviewRoundId === round.id));
177
+ if (finding !== undefined)
178
+ return `Review finding ${finding.id} references the Round.`;
179
+ const events = store.listEvents(round.taskId).filter((event) => (event.type === "review.completed" && event.payload.reviewRoundId === round.id));
180
+ if (events.length !== 1)
181
+ return "Completed Round lacks one exact completion Event.";
182
+ const event = events[0];
183
+ if (event.payload.workItemId !== round.workItemId
184
+ || event.payload.candidateId !== round.candidateId
185
+ || event.payload.reviewBaseCommit !== round.reviewBaseCommit
186
+ || event.payload.evidenceCommit !== round.reviewBaseCommit
187
+ || event.payload.checks !== "none") {
188
+ return `Review completion Event ${event.id} carries mismatched or semantic evidence.`;
189
+ }
190
+ return null;
191
+ }
192
+ function explicitCompletedReviewInfrastructureFailure(summary, round) {
193
+ if (exactCompletedReviewInfrastructureFailureReport(summary))
194
+ return true;
195
+ const report = summary.trim();
196
+ if (report === `Role Run workspace is not the durable owner: ${round.taskId}/${round.reviewerRoleName}.`
197
+ || (round.reviewerRunId !== undefined
198
+ && report === `Review Run workspace is not the durable owner: ${round.reviewerRunId}.`)) {
199
+ return true;
51
200
  }
52
- return {
53
- kind: "infra",
54
- infraKind: "other-infra",
55
- reason: "Review execution attempt failed before a semantic report."
56
- };
201
+ return /^(?:Run )?(?:Context|Context Pack) load (?:failed|unavailable|unauthorized|stale|mismatched|malformed)(?:: (?:failure|mismatch|unavailable|unauthorized|stale|mismatched|malformed))?\.?$/iu
202
+ .test(report);
57
203
  }
58
- /** True when this Round may feed the finding ledger. */
59
- export function isSemanticReviewRound(round) {
60
- return classifyReviewRoundOutcome(round)?.kind === "semantic";
204
+ function exactCompletedReviewInfrastructureFailureReport(summary) {
205
+ const lines = summary.trim().split(/\r?\n/u);
206
+ const envelope = [
207
+ /^# Review result: (?:context-load|workspace-binding) failure$/u,
208
+ /^$/u,
209
+ /^The assigned Run context could not be safely matched to this native session, so no candidate review was performed\.$/u,
210
+ /^$/u,
211
+ /^- Run: `[^`\r\n]+`$/u,
212
+ /^- ReviewRound: `[^`\r\n]+`$/u,
213
+ /^- Review base commit: `[0-9a-f]{40}`$/u,
214
+ /^- Frozen target: `[^`\r\n]+`$/u,
215
+ /^- Authorized workspace from the exact Context Pack: `[^`\r\n]+`$/u,
216
+ /^- Session-attached workspace: `[^`\r\n]+`$/u,
217
+ /^- Verification: both paths resolve distinctly and have different filesystem inodes \(`[^`\r\n]+` vs `[^`\r\n]+`\)\.$/u,
218
+ /^$/u,
219
+ /^## Findings$/u,
220
+ /^$/u,
221
+ /^- Verified-fixed findings: none; review did not start\.$/u,
222
+ /^- New findings: none; candidate sources were intentionally not inspected\.$/u,
223
+ /^- Accepted risks: none accepted\.$/u,
224
+ /^- Residual verification gaps: the complete frozen diff, changed control-flow paths, callers, data-integrity behavior, and required deterministic checks remain unreviewed because the Review workspace binding is mismatched\.$/u,
225
+ /^$/u,
226
+ /^## Checks actually run$/u,
227
+ /^$/u,
228
+ /^- Exact Context API load: passed for Task\/Run\/Role\/purpose\/subject\/snapshot\/adapter\.$/u,
229
+ /^- Workspace binding verification: failed\.$/u,
230
+ /^- Candidate build\/tests\/package checks: not run\.$/u,
231
+ /^- Real-provider E2E: not run and not authorized\.$/u,
232
+ /^$/u,
233
+ /^## Required next action$/u,
234
+ /^$/u,
235
+ /^Attach the native Reviewer session to the exact workspace recorded by the Context Pack, or issue a fresh internally consistent Review Run\/Context Pack\. Then perform the complete bounded Task-final review at the frozen head\.$/u
236
+ ];
237
+ return lines.length === envelope.length
238
+ && envelope.every((pattern, index) => pattern.test(lines[index]));
239
+ }
240
+ function runtimeFailureSummaryHasReviewerOutput(summary) {
241
+ const output = /(?:^|\n)last_assistant_message:[ \t]*([\s\S]*)$/u.exec(summary)?.[1];
242
+ return output !== undefined && output.trim().length > 0;
243
+ }
244
+ function looksLikeStructuredReviewReport(report) {
245
+ let parsed;
246
+ try {
247
+ parsed = JSON.parse(report);
248
+ }
249
+ catch {
250
+ return false;
251
+ }
252
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
253
+ return false;
254
+ const record = parsed;
255
+ return [
256
+ "summary", "report", "checks", "findings", "evidence", "evidenceCommit",
257
+ "deltaDisposition", "deltaReasoning"
258
+ ].some((key) => Object.hasOwn(record, key));
61
259
  }
@@ -0,0 +1 @@
1
+ export const TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT = "review.task-final-contract-rebound";
@@ -0,0 +1,350 @@
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 reviewObservations = reviewRounds.flatMap((round) => ((round.scope ?? "work-item") !== "task"
27
+ || round.taskFinalReviewContract === undefined
28
+ ? []
29
+ : [{
30
+ contract: round.taskFinalReviewContract,
31
+ createdAt: round.createdAt,
32
+ source: `ReviewRound ${round.id}`
33
+ }]));
34
+ return resolveTaskFinalReviewContract(taskId, [...candidateObservations, ...reviewObservations], events);
35
+ }
36
+ export function createTaskFinalReviewContractRebind(input) {
37
+ const taskId = requireIdentity(input.taskId, "Task final-review rebind Task id");
38
+ const reviewerRoleName = requireIdentity(input.reviewerRoleName, "Task final-review rebind Reviewer Role");
39
+ const fromContract = validateTaskFinalReviewContract(input.fromContract);
40
+ if (fromContract.taskId !== taskId) {
41
+ throw new Error("Task final-review rebind contract belongs to another Task.");
42
+ }
43
+ if (fromContract.reviewerRoleName !== reviewerRoleName) {
44
+ throw new Error("Task final-review rebind changes the Reviewer identity.");
45
+ }
46
+ const toContract = createTaskFinalReviewContract({
47
+ taskId,
48
+ reviewerRoleName,
49
+ controlPlaneDigest: requireDigest(input.toControlPlaneDigest, "Task final-review rebind target control-plane digest")
50
+ });
51
+ if (fromContract.controlPlaneDigest === toContract.controlPlaneDigest) {
52
+ throw new Error("Task final-review rebind must change the control-plane digest.");
53
+ }
54
+ const fromRelease = validateReleaseIdentity(input.fromRelease, "source");
55
+ const toRelease = validateReleaseIdentity(input.toRelease, "target");
56
+ if (fromRelease.releaseId === toRelease.releaseId) {
57
+ throw new Error("Task final-review rebind must cross an immutable release handover.");
58
+ }
59
+ return Object.freeze({
60
+ schemaVersion: 1,
61
+ taskId,
62
+ reviewerRoleName,
63
+ fromContract,
64
+ toContract,
65
+ fromRelease,
66
+ toRelease,
67
+ handoverId: requireIdentity(input.handoverId, "Task final-review rebind handover id"),
68
+ authorizedBy: requireText(input.authorizedBy, "Task final-review rebind authorizer")
69
+ });
70
+ }
71
+ export function taskFinalReviewContractRebindPayload(rebind) {
72
+ const validated = createTaskFinalReviewContractRebind({
73
+ ...rebind,
74
+ toControlPlaneDigest: rebind.toContract.controlPlaneDigest
75
+ });
76
+ return {
77
+ schemaVersion: "1",
78
+ taskId: validated.taskId,
79
+ reviewerRoleName: validated.reviewerRoleName,
80
+ fromContractDigest: validated.fromContract.digest,
81
+ toContractDigest: validated.toContract.digest,
82
+ fromControlPlaneDigest: validated.fromContract.controlPlaneDigest,
83
+ toControlPlaneDigest: validated.toContract.controlPlaneDigest,
84
+ fromReleaseId: validated.fromRelease.releaseId,
85
+ fromReleaseVersion: validated.fromRelease.version,
86
+ fromReleaseBuildId: validated.fromRelease.buildId,
87
+ fromReleasePackageDigest: validated.fromRelease.packageDigest,
88
+ ...(validated.fromRelease.sourceCommit === undefined
89
+ ? {}
90
+ : { fromReleaseSourceCommit: validated.fromRelease.sourceCommit }),
91
+ toReleaseId: validated.toRelease.releaseId,
92
+ toReleaseVersion: validated.toRelease.version,
93
+ toReleaseBuildId: validated.toRelease.buildId,
94
+ toReleasePackageDigest: validated.toRelease.packageDigest,
95
+ ...(validated.toRelease.sourceCommit === undefined
96
+ ? {}
97
+ : { toReleaseSourceCommit: validated.toRelease.sourceCommit }),
98
+ handoverId: validated.handoverId,
99
+ authorizedBy: validated.authorizedBy
100
+ };
101
+ }
102
+ export function taskFinalReviewContractRebindFromEvent(event) {
103
+ if (event.type !== TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT) {
104
+ throw new Error(`Task event is not a final-review contract rebind: ${event.id}.`);
105
+ }
106
+ const payload = event.payload;
107
+ if (payload.schemaVersion !== "1") {
108
+ throw new Error(`Task final-review rebind event schema is invalid: ${event.id}.`);
109
+ }
110
+ if (payload.taskId !== event.taskId) {
111
+ throw new Error(`Task final-review rebind event belongs to another Task: ${event.id}.`);
112
+ }
113
+ const fromContract = createTaskFinalReviewContract({
114
+ taskId: payload.taskId,
115
+ reviewerRoleName: payload.reviewerRoleName,
116
+ controlPlaneDigest: payload.fromControlPlaneDigest
117
+ });
118
+ if (fromContract.digest !== payload.fromContractDigest) {
119
+ throw new Error(`Task final-review rebind source digest is invalid: ${event.id}.`);
120
+ }
121
+ const rebind = createTaskFinalReviewContractRebind({
122
+ taskId: payload.taskId,
123
+ reviewerRoleName: payload.reviewerRoleName,
124
+ fromContract,
125
+ toControlPlaneDigest: payload.toControlPlaneDigest,
126
+ fromRelease: releaseIdentityFromPayload(payload, "from"),
127
+ toRelease: releaseIdentityFromPayload(payload, "to"),
128
+ handoverId: payload.handoverId,
129
+ authorizedBy: payload.authorizedBy
130
+ });
131
+ if (rebind.toContract.digest !== payload.toContractDigest) {
132
+ throw new Error(`Task final-review rebind target digest is invalid: ${event.id}.`);
133
+ }
134
+ return rebind;
135
+ }
136
+ /**
137
+ * Historical Candidate and Review records remain immutable. Before the first
138
+ * explicit rebind event, older Yui releases may have emitted successive
139
+ * control-plane contracts without a rebind event. Preserve that legacy prefix
140
+ * as a forward-only sequence, then require one strict append-only rebind chain.
141
+ * Any Reviewer change, reversion, fork, or post-rebind drift fails closed.
142
+ */
143
+ export function resolveTaskFinalReviewContract(taskId, observations, events) {
144
+ const normalizedTaskId = requireIdentity(taskId, "Task final-review contract Task id");
145
+ const orderedObservations = [...observations]
146
+ .map((observation) => ({
147
+ contract: validateTaskFinalReviewContract(observation.contract),
148
+ createdAt: requireTimestamp(observation.createdAt, "Task final-review observation createdAt"),
149
+ source: requireText(observation.source, "Task final-review observation source")
150
+ }))
151
+ .sort(compareCreatedAt);
152
+ const orderedEvents = events
153
+ .filter(({ type }) => type === TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT)
154
+ .map((event) => ({
155
+ event,
156
+ createdAt: requireTimestamp(event.createdAt, "Task final-review rebind event createdAt"),
157
+ rebind: taskFinalReviewContractRebindFromEvent(event)
158
+ }))
159
+ .sort((left, right) => compareCreatedAt(left, right)
160
+ || left.event.id.localeCompare(right.event.id, undefined, { numeric: true }));
161
+ if (orderedObservations.length === 0) {
162
+ if (orderedEvents.length > 0) {
163
+ throw new Error(`Task ${normalizedTaskId} has a final-review rebind without a stored contract.`);
164
+ }
165
+ return undefined;
166
+ }
167
+ for (const observation of orderedObservations) {
168
+ if (observation.contract.taskId !== normalizedTaskId) {
169
+ throw new Error(`${observation.source} carries a final-review contract for another Task.`);
170
+ }
171
+ }
172
+ const initial = orderedObservations[0].contract;
173
+ const reviewerRoleName = initial.reviewerRoleName;
174
+ const firstRebindAt = orderedEvents[0]?.createdAt;
175
+ const legacyObservations = firstRebindAt === undefined
176
+ ? orderedObservations
177
+ : orderedObservations.filter(({ createdAt }) => createdAt < firstRebindAt);
178
+ if (legacyObservations.length === 0) {
179
+ throw new Error(`Task ${normalizedTaskId} has a final-review rebind without an established source contract.`);
180
+ }
181
+ let effective = initial;
182
+ const legacyContractDigests = new Set([initial.digest]);
183
+ for (const observation of legacyObservations) {
184
+ if (observation.contract.reviewerRoleName !== reviewerRoleName) {
185
+ throw new Error(`${observation.source} changes the Task final-review Reviewer identity.`);
186
+ }
187
+ if (sameTaskFinalReviewContract(observation.contract, effective))
188
+ continue;
189
+ if (legacyContractDigests.has(observation.contract.digest)) {
190
+ throw new Error(`${observation.source} reverts a legacy Task final-review contract.`);
191
+ }
192
+ legacyContractDigests.add(observation.contract.digest);
193
+ effective = observation.contract;
194
+ }
195
+ for (const entry of orderedEvents) {
196
+ const { rebind } = entry;
197
+ if (!sameTaskFinalReviewContract(effective, rebind.fromContract)) {
198
+ throw new Error(`Task final-review rebind chain forks at ${entry.event.id}.`);
199
+ }
200
+ if (rebind.reviewerRoleName !== initial.reviewerRoleName) {
201
+ throw new Error(`Task final-review rebind changes Reviewer identity at ${entry.event.id}.`);
202
+ }
203
+ effective = rebind.toContract;
204
+ }
205
+ for (const observation of orderedObservations) {
206
+ if (firstRebindAt === undefined || observation.createdAt < firstRebindAt)
207
+ continue;
208
+ let expected = legacyObservations.at(-1).contract;
209
+ for (const entry of orderedEvents) {
210
+ if (entry.createdAt > observation.createdAt)
211
+ break;
212
+ expected = entry.rebind.toContract;
213
+ }
214
+ if (!sameTaskFinalReviewContract(observation.contract, expected)) {
215
+ throw new Error(`${observation.source} does not match the effective Task final-review contract at creation.`);
216
+ }
217
+ }
218
+ return Object.freeze({
219
+ initial,
220
+ effective,
221
+ rebinds: Object.freeze(orderedEvents.map(({ rebind }) => rebind))
222
+ });
223
+ }
224
+ /**
225
+ * Builds the immutable release proof while the caller holds the release
226
+ * handover lock. The current exact control descriptor is supplied only by the
227
+ * CLI preflight; caller-provided target identities are checked against it.
228
+ */
229
+ export function prepareTaskFinalReviewContractRebindProof(input) {
230
+ const home = resolve(input.home);
231
+ const taskId = requireIdentity(input.taskId, "Task final-review rebind Task id");
232
+ const fromControlPlaneDigest = requireDigest(input.fromControlPlaneDigest, "Task final-review rebind source control-plane digest");
233
+ const toControlPlaneDigest = requireDigest(input.toControlPlaneDigest, "Task final-review rebind target control-plane digest");
234
+ if (fromControlPlaneDigest === toControlPlaneDigest) {
235
+ throw new Error("Task final-review rebind source and target control planes are identical.");
236
+ }
237
+ if (exactControlPlaneDigest(input.currentControlPlane) !== toControlPlaneDigest) {
238
+ throw new Error("Task final-review rebind target is not the current exact control plane.");
239
+ }
240
+ if (resolve(input.currentControlPlane.yuiHome) !== home) {
241
+ throw new Error("Task final-review rebind target control plane belongs to another Home.");
242
+ }
243
+ const fromReleaseId = requireText(input.fromReleaseId, "Source release id");
244
+ const toReleaseId = requireText(input.toReleaseId, "Target release id");
245
+ if (fromReleaseId === toReleaseId) {
246
+ throw new Error("Task final-review rebind source and target releases are identical.");
247
+ }
248
+ if (readHandoverFence(home) !== null) {
249
+ throw new Error("Task final-review rebind is blocked by an unfinished release handover.");
250
+ }
251
+ const active = readActiveReleasePointer(home);
252
+ if (active === null
253
+ || active.releaseId !== toReleaseId
254
+ || active.buildId !== input.currentControlPlane.buildId
255
+ || active.packageDigest !== input.currentControlPlane.activeReleaseDigest) {
256
+ throw new Error("The active release does not match the requested target release and exact control plane.");
257
+ }
258
+ const sourcePath = join(home, "runtime", "control-plane", `${fromControlPlaneDigest}.json`);
259
+ const sourceControl = parseExactControlPlaneDescriptor(readFileSync(sourcePath, "utf8"));
260
+ if (exactControlPlaneDigest(sourceControl) !== fromControlPlaneDigest) {
261
+ throw new Error("Task final-review rebind source control-plane descriptor digest is invalid.");
262
+ }
263
+ if (resolve(sourceControl.yuiHome) !== home) {
264
+ throw new Error("Task final-review rebind source control plane belongs to another Home.");
265
+ }
266
+ const fromRelease = installedReleaseIdentity(home, fromReleaseId, sourceControl, "source");
267
+ const toRelease = installedReleaseIdentity(home, toReleaseId, input.currentControlPlane, "target");
268
+ const handover = readHandoverReceipt(home);
269
+ if (handover === null
270
+ || handover.outcome !== "completed"
271
+ || handover.activatedReleaseId !== toReleaseId
272
+ || typeof handover.handoverId !== "string"
273
+ || handover.handoverId.length === 0) {
274
+ throw new Error("Task final-review rebind requires the completed release handover that activated the target.");
275
+ }
276
+ return Object.freeze({
277
+ schemaVersion: 1,
278
+ taskId,
279
+ fromControlPlaneDigest,
280
+ toControlPlaneDigest,
281
+ fromRelease,
282
+ toRelease,
283
+ handoverId: handover.handoverId
284
+ });
285
+ }
286
+ function installedReleaseIdentity(home, releaseId, control, label) {
287
+ if (control.buildId === undefined || control.activeReleaseDigest === undefined) {
288
+ throw new Error(`Task final-review rebind ${label} control plane is not an immutable release.`);
289
+ }
290
+ let manifest;
291
+ try {
292
+ manifest = verifyReleaseIntegrity(join(releasesDirectory(home), releaseId));
293
+ }
294
+ catch (error) {
295
+ throw new Error(`Task final-review rebind ${label} release is unknown or invalid: ${releaseId}.`, { cause: error });
296
+ }
297
+ if (releaseDirectoryName(manifest) !== releaseId
298
+ || manifest.buildId !== control.buildId
299
+ || manifest.packageDigest !== control.activeReleaseDigest) {
300
+ throw new Error(`Task final-review rebind ${label} release does not match its control plane.`);
301
+ }
302
+ return validateReleaseIdentity({
303
+ releaseId,
304
+ version: manifest.version,
305
+ buildId: manifest.buildId,
306
+ packageDigest: manifest.packageDigest,
307
+ ...(manifest.sourceCommit === undefined ? {} : { sourceCommit: manifest.sourceCommit })
308
+ }, label);
309
+ }
310
+ function releaseIdentityFromPayload(payload, prefix) {
311
+ const title = prefix === "from" ? "From" : "To";
312
+ return validateReleaseIdentity({
313
+ releaseId: payload[`${prefix}ReleaseId`],
314
+ version: payload[`${prefix}ReleaseVersion`],
315
+ buildId: payload[`${prefix}ReleaseBuildId`],
316
+ packageDigest: payload[`${prefix}ReleasePackageDigest`],
317
+ ...(payload[`${prefix}ReleaseSourceCommit`] === undefined
318
+ ? {}
319
+ : { sourceCommit: payload[`${prefix}ReleaseSourceCommit`] })
320
+ }, title);
321
+ }
322
+ function validateReleaseIdentity(value, label) {
323
+ return Object.freeze({
324
+ releaseId: requireText(value.releaseId, `Task final-review rebind ${label} release id`),
325
+ version: requireText(value.version, `Task final-review rebind ${label} release version`),
326
+ buildId: requireText(value.buildId, `Task final-review rebind ${label} release build id`),
327
+ packageDigest: requireDigest(value.packageDigest, `Task final-review rebind ${label} release package digest`),
328
+ ...(value.sourceCommit === undefined
329
+ ? {}
330
+ : { sourceCommit: requireText(value.sourceCommit, `Task final-review rebind ${label} source commit`) })
331
+ });
332
+ }
333
+ function compareCreatedAt(left, right) {
334
+ return left.createdAt.localeCompare(right.createdAt);
335
+ }
336
+ function requireDigest(value, label) {
337
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
338
+ throw new Error(`${label} is invalid.`);
339
+ }
340
+ return value;
341
+ }
342
+ function requireText(value, label) {
343
+ if (typeof value !== "string" || value.includes("\0")) {
344
+ throw new Error(`${label} is invalid.`);
345
+ }
346
+ const normalized = value.trim();
347
+ if (normalized.length === 0)
348
+ throw new Error(`${label} is required.`);
349
+ return normalized;
350
+ }