@zq-silk/yui 0.6.0 → 0.6.2
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/README.md +5 -5
- package/dist/agent/managedRuntimeEnvironment.js +2 -1
- package/dist/cli/commandCatalog.js +251 -13
- package/dist/cli/updateOrchestrator.js +8 -0
- package/dist/cli/updatePorts.js +76 -22
- package/dist/cli.js +264 -20
- package/dist/commands/configCommands.js +83 -9
- package/dist/commands/controllerCommands.js +103 -0
- package/dist/commands/deliveryGuardPreflight.js +30 -0
- package/dist/commands/durableJobCommands.js +231 -0
- package/dist/commands/executionAuditCommands.js +193 -0
- package/dist/commands/grantCommands.js +374 -0
- package/dist/commands/projectCommands.js +119 -81
- package/dist/commands/releaseCommands.js +444 -0
- package/dist/commands/resourcesCommands.js +274 -0
- package/dist/commands/sessionCommands.js +104 -0
- package/dist/commands/taskActor.js +117 -0
- package/dist/commands/taskChangeSetCommands.js +60 -0
- package/dist/commands/taskCommands.js +618 -202
- package/dist/commands/taskCompletionGate.js +78 -1
- package/dist/commands/taskContextCommand.js +33 -6
- package/dist/commands/taskInputCommands.js +1 -1
- package/dist/commands/taskIntegrationCommands.js +136 -33
- package/dist/commands/taskIntegrationQueueCommands.js +228 -0
- package/dist/commands/taskNextActionCommand.js +100 -0
- package/dist/commands/taskOverlapCommands.js +120 -0
- package/dist/commands/taskOverviewCommand.js +36 -8
- package/dist/commands/telemetryCommands.js +330 -0
- package/dist/commands/workflowCommands.js +415 -0
- package/dist/config/yuiConfig.js +62 -0
- package/dist/controller/clientRuntime.js +42 -1
- package/dist/controller/controller.js +402 -56
- package/dist/controller/controllerMain.js +25 -2
- package/dist/controller/domainIdentity.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +423 -31
- package/dist/controller/handoverCandidate.js +168 -0
- package/dist/controller/jobClient.js +102 -0
- package/dist/controller/jobControl.js +613 -0
- package/dist/controller/jobSupervisor.js +498 -0
- package/dist/controller/providerHookRunFence.js +34 -5
- package/dist/controller/resourceCleanupLinux.js +18 -9
- package/dist/controller/resourceInventoryLinux.js +90 -39
- package/dist/controller/runtime.js +165 -15
- package/dist/controller/runtimeEventInbox.js +234 -57
- package/dist/controller/runtimeEventProcessor.js +297 -58
- package/dist/controller/sessionOwnerReconciliation.js +321 -0
- package/dist/core/controllerServer.js +416 -27
- package/dist/core/controllerTelemetry.js +167 -0
- package/dist/doctor/doctor.js +113 -16
- package/dist/domain/validation.js +9 -0
- package/dist/execution/executionGroup.js +40 -3
- package/dist/executor/agentExecutor.js +6 -3
- package/dist/executor/effectiveLaunch.js +52 -0
- package/dist/executor/executorRegistry.js +50 -0
- package/dist/executor/fileRoleLaunchPlanner.js +61 -6
- package/dist/grant/capabilityGrant.js +282 -0
- package/dist/integration/changeSet.js +16 -3
- package/dist/integration/changeSetManifest.js +46 -0
- package/dist/integration/gitIntegrationService.js +528 -147
- package/dist/integration/integrationAttempt.js +54 -5
- package/dist/integration/integrationQueueEntry.js +221 -0
- package/dist/integration/integrationQueueService.js +955 -0
- package/dist/integration/manifestTags.js +99 -0
- package/dist/integration/overlapDiagnostics.js +211 -0
- package/dist/job/durableJob.js +449 -0
- package/dist/job/jobRunner.js +350 -0
- package/dist/lifecycle/exactRunTerminalization.js +24 -2
- package/dist/lifecycle/providerErrorClass.js +126 -0
- package/dist/message/message.js +16 -3
- package/dist/observability/executionAudit.js +545 -0
- package/dist/observability/faultClassification.js +160 -0
- package/dist/observability/runtimeIdentity.js +367 -0
- package/dist/release/fakeReleasePorts.js +55 -0
- package/dist/release/releaseHandover.js +475 -0
- package/dist/release/releaseIdempotencyStore.js +165 -0
- package/dist/release/releaseWorkflow.js +459 -0
- package/dist/release/releaseWorkflowEngine.js +688 -0
- package/dist/release/releaseWorkflowPorts.js +1720 -0
- package/dist/release/runtimeRelease.js +495 -0
- package/dist/release/workflowFileLock.js +218 -0
- package/dist/repository/gitWorkspace.js +177 -1
- package/dist/repository/projectMaintenanceLock.js +315 -0
- package/dist/repository/taskWorkspaceCoordinator.js +87 -17
- package/dist/repository/taskWorkspacePreparer.js +1091 -517
- package/dist/resources/autoResourceGc.js +116 -0
- package/dist/resources/liveReferences.js +574 -0
- package/dist/resources/resourceDiscovery.js +477 -0
- package/dist/resources/resourceGc.js +645 -0
- package/dist/resources/resourceRegistrar.js +256 -0
- package/dist/resources/resourceRegistry.js +150 -0
- package/dist/resources/resourceRegistryStore.js +41 -0
- package/dist/resources/resourceTypes.js +42 -0
- package/dist/resources/sqliteResourceRegistry.js +111 -0
- package/dist/review/reviewConfig.js +10 -0
- package/dist/review/reviewFinding.js +240 -0
- package/dist/review/reviewFindingLedger.js +545 -0
- package/dist/review/reviewOutcomeClassifier.js +61 -0
- package/dist/review/reviewRound.js +56 -4
- package/dist/run/agentRun.js +80 -4
- package/dist/run/providerRetry.js +84 -0
- package/dist/run/providerRetryConfig.js +63 -0
- package/dist/run/yieldReceipt.js +65 -0
- package/dist/runtime/exactControlPlane.js +79 -2
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/sessionOwnerIdentity.js +269 -0
- package/dist/runtime/sessionOwnerRegistry.js +132 -0
- package/dist/runtime/sessionReconciliation.js +93 -0
- package/dist/runtime/sessionTerminationGuard.js +211 -0
- package/dist/runtime/taskRuntimeIsolation.js +13 -0
- package/dist/runtime/tmuxAdapters.js +34 -1
- package/dist/scheduler/actionability.js +155 -0
- package/dist/scheduler/activeRoleRunDelivery.js +14 -5
- package/dist/scheduler/activeTaskProgress.js +60 -0
- package/dist/scheduler/leaderWakeupProcessor.js +22 -11
- package/dist/scheduler/roleRunStall.js +135 -29
- package/dist/scheduler/taskExecutionProjection.js +11 -0
- package/dist/storage/compatibleTaskStore.js +112 -5
- package/dist/storage/migration/productionRegistry.js +736 -1
- package/dist/storage/sqliteSchema.js +264 -3
- package/dist/storage/sqliteStore.js +487 -13
- package/dist/storage/storeRpc.js +21 -0
- package/dist/storage/taskStore.js +974 -21
- package/dist/storage/upgrade/homeClassification.js +120 -2
- package/dist/storage/upgrade/migrationReceipt.js +67 -0
- package/dist/storage/upgrade/pseudoLayoutRepair.js +241 -0
- package/dist/storage/upgrade/recordVersions.js +10 -1
- package/dist/storage/upgrade/sqliteMigrationTarget.js +58 -6
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +290 -0
- package/dist/storage/upgrade/sqliteStateMigration.js +258 -2
- package/dist/storage/upgrade/upgradeOrchestrator.js +482 -16
- package/dist/task/deliveryGuard.js +226 -0
- package/dist/task/nextAction.js +738 -0
- package/dist/task/repairWave.js +137 -0
- package/dist/task/taskRecordReference.js +6 -1
- package/dist/telemetry/sqliteTelemetryStore.js +387 -0
- package/dist/telemetry/telemetryCompaction.js +251 -0
- package/dist/telemetry/telemetryConfig.js +64 -0
- package/dist/telemetry/telemetryRouter.js +32 -0
- package/dist/telemetry/telemetryStore.js +19 -0
- package/dist/telemetry/telemetryWiring.js +33 -0
- package/dist/tmux/tmuxManager.js +20 -1
- package/dist/tmux/tmuxSocketEndpoint.js +20 -0
- package/dist/verification/gateArtifact.js +216 -0
- package/dist/verification/gateArtifactStore.js +87 -0
- package/dist/verification/verificationGateService.js +414 -0
- package/dist/verification/verificationPlan.js +308 -0
- package/dist/workspace/gitChangeSetCapture.js +12 -2
- package/dist/workspace/workItemChangeSetManager.js +60 -3
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { requireIdentity, requireText, requireTimestamp, normalizedUniqueText } from "../domain/validation.js";
|
|
3
|
+
import { validateTaskRecordReference } from "../task/taskRecordReference.js";
|
|
4
|
+
/** Dispositions that still block Task completion under `enforce`. */
|
|
5
|
+
export const BLOCKING_FINDING_DISPOSITIONS = [
|
|
6
|
+
"open",
|
|
7
|
+
"fixed-pending-review"
|
|
8
|
+
];
|
|
9
|
+
/** Dispositions the Leader may set explicitly. */
|
|
10
|
+
export const LEADER_FINDING_DISPOSITIONS = [
|
|
11
|
+
"fixed-pending-review",
|
|
12
|
+
"verified-fixed",
|
|
13
|
+
"accepted-risk",
|
|
14
|
+
"not-actionable",
|
|
15
|
+
"superseded"
|
|
16
|
+
];
|
|
17
|
+
/** Normalizes the severity vocabularies reviewers may use to P1/P2/P3. */
|
|
18
|
+
export function normalizeReviewFindingSeverity(value) {
|
|
19
|
+
if (typeof value !== "string")
|
|
20
|
+
throw new Error("Review finding severity is required.");
|
|
21
|
+
const normalized = value.trim().toLowerCase();
|
|
22
|
+
if (normalized === "p1" || normalized === "critical" || normalized === "blocker")
|
|
23
|
+
return "p1";
|
|
24
|
+
if (normalized === "p2" || normalized === "high" || normalized === "major")
|
|
25
|
+
return "p2";
|
|
26
|
+
if (normalized === "p3"
|
|
27
|
+
|| normalized === "medium"
|
|
28
|
+
|| normalized === "moderate"
|
|
29
|
+
|| normalized === "low"
|
|
30
|
+
|| normalized === "minor"
|
|
31
|
+
|| normalized === "nit"
|
|
32
|
+
|| normalized === "info"
|
|
33
|
+
|| normalized === "style") {
|
|
34
|
+
return "p3";
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Review finding severity is invalid: ${String(value)}.`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Derives the stable ledger key for one reported finding. The key is
|
|
40
|
+
* deterministic for the same invariant/path/symbol so a repeated finding in a
|
|
41
|
+
* later Round updates the existing record instead of creating a duplicate.
|
|
42
|
+
*/
|
|
43
|
+
export function reviewFindingStableKey(input) {
|
|
44
|
+
const invariant = input.invariant.trim().toLowerCase();
|
|
45
|
+
const primaryPath = (input.primaryPath ?? "").trim().toLowerCase();
|
|
46
|
+
const primarySymbol = (input.primarySymbol ?? "").trim().toLowerCase();
|
|
47
|
+
const basis = [
|
|
48
|
+
invariant,
|
|
49
|
+
primaryPath,
|
|
50
|
+
primarySymbol
|
|
51
|
+
].join("|");
|
|
52
|
+
// Reports without path/symbol provenance cannot safely collapse different
|
|
53
|
+
// titles under one invariant. Use the title as the fallback identity instead
|
|
54
|
+
// of merging every such finding into a single ledger record.
|
|
55
|
+
const material = primaryPath.length === 0 && primarySymbol.length === 0
|
|
56
|
+
? `${invariant}|${input.title.trim().toLowerCase()}`
|
|
57
|
+
: basis;
|
|
58
|
+
return `rf-${createHash("sha256").update(material).digest("hex").slice(0, 16)}`;
|
|
59
|
+
}
|
|
60
|
+
export function createReviewFinding(id, taskId, input, now) {
|
|
61
|
+
const timestamp = now.toISOString();
|
|
62
|
+
return validateReviewFinding({
|
|
63
|
+
schemaVersion: 1,
|
|
64
|
+
id: requireIdentity(id, "ReviewFinding id"),
|
|
65
|
+
taskId: requireIdentity(taskId, "Task id"),
|
|
66
|
+
...(input.sourceId === undefined ? {} : { sourceId: requireIdentity(input.sourceId, "ReviewFinding source id") }),
|
|
67
|
+
stableKey: requireIdentity(input.stableKey, "ReviewFinding stable key"),
|
|
68
|
+
severity: input.severity,
|
|
69
|
+
invariant: requireText(input.invariant, "ReviewFinding invariant"),
|
|
70
|
+
title: requireText(input.title, "ReviewFinding title"),
|
|
71
|
+
affectedPaths: normalizedUniqueText(input.affectedPaths ?? [], "ReviewFinding affected path"),
|
|
72
|
+
affectedSymbols: normalizedUniqueText(input.affectedSymbols ?? [], "ReviewFinding affected symbol"),
|
|
73
|
+
evidence: normalizedUniqueText(input.evidence ?? [], "ReviewFinding evidence"),
|
|
74
|
+
firstReviewRoundId: requireIdentity(input.reviewRoundId, "ReviewFinding first ReviewRound"),
|
|
75
|
+
lastReviewRoundId: requireIdentity(input.reviewRoundId, "ReviewFinding last ReviewRound"),
|
|
76
|
+
disposition: "open",
|
|
77
|
+
createdAt: timestamp,
|
|
78
|
+
updatedAt: timestamp
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Applies one Leader disposition. Terminal dispositions
|
|
83
|
+
* (verified-fixed/accepted-risk/not-actionable/superseded) are explicit Leader
|
|
84
|
+
* decisions and stay terminal; `fixed-pending-review` may still regress to
|
|
85
|
+
* `open` when a later Round reproduces the finding.
|
|
86
|
+
*/
|
|
87
|
+
export function disposeReviewFinding(finding, input) {
|
|
88
|
+
validateReviewFinding(finding);
|
|
89
|
+
if (!LEADER_FINDING_DISPOSITIONS.includes(input.disposition)) {
|
|
90
|
+
throw new Error(`ReviewFinding disposition is not a Leader decision: ${input.disposition}.`);
|
|
91
|
+
}
|
|
92
|
+
if (finding.disposition !== "open" && finding.disposition !== "fixed-pending-review") {
|
|
93
|
+
throw new Error(`ReviewFinding ${finding.id} is already terminal: ${finding.disposition}.`);
|
|
94
|
+
}
|
|
95
|
+
if (input.disposition === "superseded" && input.supersededBy === undefined) {
|
|
96
|
+
throw new Error(`ReviewFinding ${finding.id} supersession requires a successor stable key.`);
|
|
97
|
+
}
|
|
98
|
+
const repair = input.repair === undefined
|
|
99
|
+
? finding.repair
|
|
100
|
+
: validateReviewFindingRepair(input.repair);
|
|
101
|
+
return validateReviewFinding({
|
|
102
|
+
...finding,
|
|
103
|
+
disposition: input.disposition,
|
|
104
|
+
...(repair === undefined ? {} : { repair }),
|
|
105
|
+
dispositionBy: requireIdentity(input.by, "ReviewFinding disposition actor"),
|
|
106
|
+
...(input.note === undefined ? {} : { dispositionNote: requireText(input.note, "ReviewFinding disposition note") }),
|
|
107
|
+
...(input.supersededBy === undefined ? {} : { supersededBy: requireIdentity(input.supersededBy, "ReviewFinding successor stable key") }),
|
|
108
|
+
mergeRequired: false,
|
|
109
|
+
updatedAt: input.now.toISOString()
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Reconciles a re-detection of this finding in a later Round. A regression
|
|
114
|
+
* reopens a `fixed-pending-review` finding; a `verified-fixed` finding that
|
|
115
|
+
* reproduces also reopens (the fix did not hold). Explicit Leader decisions
|
|
116
|
+
* (accepted-risk/not-actionable/superseded) stay terminal but still get their
|
|
117
|
+
* evidence and last-Round pointer refreshed.
|
|
118
|
+
*/
|
|
119
|
+
export function redetectReviewFinding(finding, input) {
|
|
120
|
+
validateReviewFinding(finding);
|
|
121
|
+
const evidence = normalizedUniqueText([...finding.evidence, ...(input.evidence ?? [])], "ReviewFinding evidence");
|
|
122
|
+
const reopen = finding.disposition === "fixed-pending-review"
|
|
123
|
+
|| finding.disposition === "verified-fixed";
|
|
124
|
+
return validateReviewFinding({
|
|
125
|
+
...finding,
|
|
126
|
+
lastReviewRoundId: requireIdentity(input.reviewRoundId, "ReviewFinding last ReviewRound"),
|
|
127
|
+
evidence,
|
|
128
|
+
...(reopen ? { disposition: "open", repair: undefined } : {}),
|
|
129
|
+
...(reopen ? { mergeRequired: finding.mergeRequired } : {}),
|
|
130
|
+
updatedAt: input.now.toISOString()
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** Refreshes last-Round/evidence for a terminal finding without reopening it. */
|
|
134
|
+
export function touchReviewFinding(finding, input) {
|
|
135
|
+
validateReviewFinding(finding);
|
|
136
|
+
const evidence = normalizedUniqueText([...finding.evidence, ...(input.evidence ?? [])], "ReviewFinding evidence");
|
|
137
|
+
if (finding.lastReviewRoundId === input.reviewRoundId
|
|
138
|
+
&& evidence.length === finding.evidence.length) {
|
|
139
|
+
return finding;
|
|
140
|
+
}
|
|
141
|
+
return validateReviewFinding({
|
|
142
|
+
...finding,
|
|
143
|
+
lastReviewRoundId: requireIdentity(input.reviewRoundId, "ReviewFinding last ReviewRound"),
|
|
144
|
+
evidence,
|
|
145
|
+
updatedAt: input.now.toISOString()
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Issue 06: a Reviewer reports a previously-open finding as resolved in a
|
|
150
|
+
* later semantic Round. The claim is only accepted when the ledger already
|
|
151
|
+
* has repair refs; otherwise the old finding remains open (a changed candidate
|
|
152
|
+
* without repair evidence must not advance disposition). With repair refs,
|
|
153
|
+
* this transitions `open` to `fixed-pending-review` (the fix claim is recorded
|
|
154
|
+
* but the Leader still owns the final disposition) and refreshes
|
|
155
|
+
* evidence/last-Round for `fixed-pending-review`. Explicit Leader decisions
|
|
156
|
+
* (accepted-risk/not-actionable/superseded) and `verified-fixed` are untouched.
|
|
157
|
+
*/
|
|
158
|
+
export function resolveReviewFinding(finding, input) {
|
|
159
|
+
validateReviewFinding(finding);
|
|
160
|
+
const evidence = normalizedUniqueText([...finding.evidence, ...(input.evidence ?? [])], "ReviewFinding evidence");
|
|
161
|
+
const transition = finding.disposition === "open" && finding.repair !== undefined;
|
|
162
|
+
if (!transition
|
|
163
|
+
&& finding.lastReviewRoundId === input.reviewRoundId
|
|
164
|
+
&& evidence.length === finding.evidence.length) {
|
|
165
|
+
return finding;
|
|
166
|
+
}
|
|
167
|
+
return validateReviewFinding({
|
|
168
|
+
...finding,
|
|
169
|
+
lastReviewRoundId: requireIdentity(input.reviewRoundId, "ReviewFinding last ReviewRound"),
|
|
170
|
+
evidence,
|
|
171
|
+
...(transition ? { disposition: "fixed-pending-review" } : {}),
|
|
172
|
+
updatedAt: input.now.toISOString()
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
export function isReviewFindingBlocking(finding) {
|
|
176
|
+
return BLOCKING_FINDING_DISPOSITIONS.includes(finding.disposition)
|
|
177
|
+
&& (finding.severity === "p1" || finding.severity === "p2");
|
|
178
|
+
}
|
|
179
|
+
export function validateReviewFinding(finding) {
|
|
180
|
+
if (finding.schemaVersion !== 1) {
|
|
181
|
+
throw new Error("ReviewFinding must use schemaVersion 1.");
|
|
182
|
+
}
|
|
183
|
+
validateTaskRecordReference({ taskId: finding.taskId, localId: finding.id }, "reviewFinding");
|
|
184
|
+
requireIdentity(finding.stableKey, "ReviewFinding stable key");
|
|
185
|
+
if (finding.severity !== "p1" && finding.severity !== "p2" && finding.severity !== "p3") {
|
|
186
|
+
throw new Error(`ReviewFinding severity is invalid: ${String(finding.severity)}.`);
|
|
187
|
+
}
|
|
188
|
+
requireText(finding.invariant, "ReviewFinding invariant");
|
|
189
|
+
requireText(finding.title, "ReviewFinding title");
|
|
190
|
+
normalizedUniqueText(finding.affectedPaths, "ReviewFinding affected path");
|
|
191
|
+
normalizedUniqueText(finding.affectedSymbols, "ReviewFinding affected symbol");
|
|
192
|
+
normalizedUniqueText(finding.evidence, "ReviewFinding evidence");
|
|
193
|
+
validateTaskRecordReference({ taskId: finding.taskId, localId: finding.firstReviewRoundId }, "reviewRound");
|
|
194
|
+
validateTaskRecordReference({ taskId: finding.taskId, localId: finding.lastReviewRoundId }, "reviewRound");
|
|
195
|
+
if (![
|
|
196
|
+
"open",
|
|
197
|
+
"fixed-pending-review",
|
|
198
|
+
"verified-fixed",
|
|
199
|
+
"accepted-risk",
|
|
200
|
+
"not-actionable",
|
|
201
|
+
"superseded"
|
|
202
|
+
].includes(finding.disposition)) {
|
|
203
|
+
throw new Error(`ReviewFinding disposition is invalid: ${String(finding.disposition)}.`);
|
|
204
|
+
}
|
|
205
|
+
if (finding.repair !== undefined)
|
|
206
|
+
validateReviewFindingRepair(finding.repair);
|
|
207
|
+
if (finding.dispositionBy !== undefined) {
|
|
208
|
+
requireIdentity(finding.dispositionBy, "ReviewFinding disposition actor");
|
|
209
|
+
}
|
|
210
|
+
if (finding.dispositionNote !== undefined) {
|
|
211
|
+
requireText(finding.dispositionNote, "ReviewFinding disposition note");
|
|
212
|
+
}
|
|
213
|
+
if (finding.supersededBy !== undefined) {
|
|
214
|
+
requireIdentity(finding.supersededBy, "ReviewFinding successor stable key");
|
|
215
|
+
if (finding.disposition !== "superseded") {
|
|
216
|
+
throw new Error(`ReviewFinding ${finding.id} carries a successor but is not superseded.`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (finding.disposition === "superseded" && finding.supersededBy === undefined) {
|
|
220
|
+
throw new Error(`ReviewFinding ${finding.id} is superseded without a successor.`);
|
|
221
|
+
}
|
|
222
|
+
requireTimestamp(finding.createdAt, "ReviewFinding createdAt");
|
|
223
|
+
requireTimestamp(finding.updatedAt, "ReviewFinding updatedAt");
|
|
224
|
+
return finding;
|
|
225
|
+
}
|
|
226
|
+
function validateReviewFindingRepair(repair) {
|
|
227
|
+
if (repair.workItemId !== undefined) {
|
|
228
|
+
requireIdentity(repair.workItemId, "ReviewFinding repair WorkItem");
|
|
229
|
+
}
|
|
230
|
+
if (repair.commit !== undefined) {
|
|
231
|
+
const commit = requireText(repair.commit, "ReviewFinding repair commit").toLowerCase();
|
|
232
|
+
if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(commit)) {
|
|
233
|
+
throw new Error("ReviewFinding repair commit is invalid.");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (repair.verification !== undefined) {
|
|
237
|
+
requireText(repair.verification, "ReviewFinding repair verification");
|
|
238
|
+
}
|
|
239
|
+
return repair;
|
|
240
|
+
}
|