@zq-silk/yui 0.14.0 → 0.14.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/ARCHITECTURE.md +64 -35
- package/README.md +258 -125
- package/dist/agent/argumentPolicy.js +2 -1
- package/dist/agent/managedRuntimeEnvironment.js +5 -8
- package/dist/cli/commandCatalog.js +74 -109
- package/dist/cli/helpRenderer.js +1 -2
- package/dist/cli/interactionCandidates.js +10 -0
- package/dist/cli/interactionPolicy.js +76 -37
- package/dist/cli/roleWizard.js +5 -6
- package/dist/cli/updateOrchestrator.js +10 -8
- package/dist/cli/updatePorts.js +17 -13
- package/dist/cli/upgradeCommand.js +2 -5
- package/dist/cli.js +268 -231
- package/dist/commands/agentCommands.js +13 -0
- package/dist/commands/configCommands.js +7 -19
- package/dist/commands/configOverview.js +3 -2
- package/dist/commands/durableJobCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +4 -9
- package/dist/commands/globalRoleCommands.js +17 -0
- package/dist/commands/profileCommands.js +200 -30
- package/dist/commands/sessionCommands.js +1 -1
- package/dist/commands/taskActor.js +29 -127
- package/dist/commands/taskChangeSetCommands.js +1 -3
- package/dist/commands/taskCommands.js +1771 -2107
- package/dist/commands/taskCompletionGate.js +27 -103
- package/dist/commands/taskContextCommand.js +47 -80
- package/dist/commands/taskExecutionCommands.js +9 -27
- package/dist/commands/taskInputCommands.js +10 -10
- package/dist/commands/taskIntegrationCommands.js +69 -61
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -29
- package/dist/commands/taskOverviewCommand.js +25 -3
- package/dist/commands/taskPublicationCommands.js +151 -85
- package/dist/commands/taskPublicationVerifyCommand.js +181 -0
- package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
- package/dist/commands/taskRoleRuntimeStatus.js +5 -6
- package/dist/commands/taskUpstreamCommands.js +79 -95
- package/dist/config/configCatalog.js +1 -1
- package/dist/context/contextSnapshot.js +17 -2
- package/dist/context/sessionBootstrapManifest.js +18 -1
- package/dist/context/sourceTurnContext.js +30 -0
- package/dist/context/turnContextPack.js +107 -48
- package/dist/context/turnInputContract.js +11 -4
- package/dist/context/wakeNotification.js +84 -22
- package/dist/controller/agentRuntimeObserver.js +9 -12
- package/dist/controller/clientRuntime.js +13 -13
- package/dist/controller/controller.js +11 -11
- package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
- package/dist/controller/jobControl.js +11 -10
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +2 -2
- package/dist/controller/runtime.js +18 -61
- package/dist/controller/runtimeEventInbox.js +57 -34
- package/dist/controller/runtimeEventProcessor.js +36 -96
- package/dist/controller/runtimeHookTurnFence.js +33 -57
- package/dist/controller/runtimeLaunchCoordinator.js +44 -40
- package/dist/controller/runtimeObservationHook.js +18 -12
- package/dist/controller/sessionNotify.js +43 -28
- package/dist/controller/sessionOwnerReconciliation.js +21 -21
- package/dist/controller/structuredProviderObservation.js +13 -16
- package/dist/coordination/workMailboxQueue.js +132 -0
- package/dist/core/controllerServer.js +1 -0
- package/dist/domain/agentResultTransport.js +60 -0
- package/dist/execution/executionHealth.js +111 -118
- package/dist/execution/reviewMainTurn.js +161 -0
- package/dist/execution/workItemExecution.js +454 -0
- package/dist/execution/workItemExecutionProjection.js +309 -0
- package/dist/execution/workItemMainTurn.js +164 -0
- package/dist/executor/agentExecutor.js +19 -19
- package/dist/executor/executorRegistry.js +7 -28
- package/dist/executor/fileRoleLaunchPlanner.js +51 -83
- package/dist/external/githubPublicationVerifier.js +114 -0
- package/dist/external/gitlabPublicationVerifier.js +158 -0
- package/dist/external/pinnedCommandRunner.js +73 -0
- package/dist/integration/changeSetManifest.js +0 -3
- package/dist/integration/deliveryObligation.js +43 -6
- package/dist/integration/gitIntegrationService.js +284 -101
- package/dist/integration/integrationAttempt.js +64 -14
- package/dist/integration/integrationQueueEntry.js +5 -35
- package/dist/integration/integrationQueueService.js +65 -194
- package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
- package/dist/lifecycle/exactTurnTerminalization.js +100 -97
- package/dist/message/message.js +16 -0
- package/dist/observability/executionAudit.js +4 -25
- package/dist/observability/faultClassification.js +58 -70
- package/dist/observability/orchestrationMetrics.js +8 -50
- package/dist/profile/agentProfile.js +60 -15
- package/dist/profile/agentProfileRuntime.js +100 -0
- package/dist/release/releaseWorkflowPorts.js +5 -68
- package/dist/repository/executionLaneGitSnapshot.js +80 -0
- package/dist/repository/gitWorkspace.js +9 -67
- package/dist/repository/taskBaseFreshness.js +3 -1
- package/dist/repository/taskWorkspaceCoordinator.js +32 -4
- package/dist/repository/taskWorkspacePreparer.js +252 -411
- package/dist/resources/liveReferences.js +6 -55
- package/dist/resources/resourceDiscovery.js +1 -30
- package/dist/resources/resourceRegistrar.js +0 -23
- package/dist/review/deltaRecheck.js +8 -65
- package/dist/review/reviewAcceptance.js +22 -19
- package/dist/review/reviewConfig.js +0 -10
- package/dist/review/reviewDecision.js +2 -2
- package/dist/review/reviewRound.js +147 -273
- package/dist/review/reviewerAvailability.js +13 -23
- package/dist/role/role.js +3 -0
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +51 -51
- package/dist/runtime/builtinAgentDrivers.js +9 -2
- package/dist/runtime/codexAppServerRuntime.js +23 -5
- package/dist/runtime/exactControlPlane.js +2 -267
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/launchBroker.js +11 -11
- package/dist/runtime/lifecycleReservation.js +2 -2
- package/dist/runtime/managedCaller.js +111 -0
- package/dist/runtime/ports.js +16 -3
- package/dist/runtime/preallocatedNativeSession.js +2 -2
- package/dist/runtime/processExitObservation.js +3 -2
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/runtimeBinding.js +1 -1
- package/dist/runtime/runtimeObservation.js +33 -16
- package/dist/runtime/runtimeSessionCandidate.js +2 -2
- package/dist/runtime/runtimeStopReceipt.js +11 -11
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/sessionOwnerIdentity.js +9 -9
- package/dist/runtime/sessionOwnerRegistry.js +13 -13
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTerminationGuard.js +9 -9
- package/dist/runtime/sessionTokenMetrics.js +5 -7
- package/dist/runtime/structuredProviderHost.js +5 -5
- package/dist/runtime/taskRuntimeIsolation.js +24 -24
- package/dist/runtime/tmuxAdapters.js +32 -30
- package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
- package/dist/scheduler/leaderWakeupProcessor.js +78 -55
- package/dist/scheduler/roleTurnLiveness.js +6 -6
- package/dist/scheduler/roleTurnStall.js +8 -9
- package/dist/scheduler/taskExecutionProjection.js +19 -41
- package/dist/scheduler/taskObservabilityProjection.js +75 -122
- package/dist/storage/sqliteSchema.js +1 -21
- package/dist/storage/sqliteStore.js +62 -47
- package/dist/storage/storageSchema.js +1 -8
- package/dist/storage/storageVersions.js +7 -4
- package/dist/storage/taskStore.js +8 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
- package/dist/task/completionReadiness.js +17 -98
- package/dist/task/deliveryGuard.js +9 -11
- package/dist/task/draftPlan.js +142 -0
- package/dist/task/nextAction.js +179 -477
- package/dist/task/publicationVerification.js +1 -0
- package/dist/task/remoteDelivery.js +233 -0
- package/dist/task/task.js +108 -4
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/turn/turn.js +109 -16
- package/dist/verification/verificationGateService.js +0 -26
- package/dist/web/assets/client/components.js +99 -121
- package/dist/web/assets/client/i18n.js +136 -58
- package/dist/web/assets/client/view.js +44 -0
- package/dist/web/assets/styles/cards.js +3 -15
- package/dist/web/assets/styles/widgets.js +4 -0
- package/dist/web/webSnapshot.js +29 -14
- package/dist/workItem/dependencyGate.js +34 -0
- package/dist/workItem/workItem.js +99 -266
- package/dist/workspace/workItemChangeSetManager.js +15 -59
- package/docs/task-local-identity.md +1 -1
- package/i18n/README.zh-CN.md +165 -59
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +272 -782
- package/skills/yui-operator/SKILL.md +190 -365
- package/skills/yui-reviewer/SKILL.md +40 -33
- package/skills/yui-runtime/SKILL.md +27 -6
- package/skills/yui-worker/SKILL.md +19 -6
- package/dist/execution/candidateConvergence.js +0 -623
- package/dist/execution/executionGroup.js +0 -854
- package/dist/execution/resourceBroker.js +0 -426
- package/dist/executor/launchPlan.js +0 -16
- package/dist/review/reviewFinding.js +0 -240
- package/dist/review/reviewFindingLedger.js +0 -550
- package/dist/review/reviewOutcomeClassifier.js +0 -255
- package/dist/scheduler/resourceQueueProjection.js +0 -71
- package/dist/task/repairWave.js +0 -150
|
@@ -10,7 +10,6 @@ import { createHash } from "node:crypto";
|
|
|
10
10
|
import { existsSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
|
|
11
11
|
import { join, resolve } from "node:path";
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
|
-
import { parseExactTaskRuntimeDescriptor } from "../runtime/exactControlPlane.js";
|
|
14
13
|
import { readRuntimeIdentity, releasesDirectory, resolveActiveRelease } from "../release/runtimeRelease.js";
|
|
15
14
|
import { isControllerSocketPathForHome } from "../core/controllerEndpoint.js";
|
|
16
15
|
import { parseControllerDiscovery } from "../core/protocol.js";
|
|
@@ -105,20 +104,18 @@ export async function scanLiveReferences(input) {
|
|
|
105
104
|
}
|
|
106
105
|
if (discovery.diagnostic !== undefined)
|
|
107
106
|
diagnostics.push(discovery.diagnostic);
|
|
108
|
-
// 4. Runtime identity
|
|
109
|
-
//
|
|
110
|
-
//
|
|
107
|
+
// 4. Runtime identity is a durable live-ownership claim. Resolved runtime
|
|
108
|
+
// paths stay protected while their owning process identity is alive.
|
|
109
|
+
// Managed workspaces are claimed from their durable records below, which
|
|
110
|
+
// is the same authority the Controller schedules from.
|
|
111
111
|
const runtimeClaims = readRuntimeIdentityClaims(home);
|
|
112
112
|
diagnostics.push(...runtimeClaims.diagnostics);
|
|
113
|
-
const descriptorClaims = readExactTaskRuntimeClaims(home);
|
|
114
|
-
diagnostics.push(...descriptorClaims.diagnostics);
|
|
115
113
|
const activeReleaseClaims = readActiveReleaseClaims(home);
|
|
116
114
|
diagnostics.push(...activeReleaseClaims.diagnostics);
|
|
117
115
|
const sessionOwnerClaims = readSessionOwnerClaims(home);
|
|
118
116
|
diagnostics.push(...sessionOwnerClaims.diagnostics);
|
|
119
117
|
for (const claim of [
|
|
120
118
|
...runtimeClaims.claims,
|
|
121
|
-
...descriptorClaims.claims,
|
|
122
119
|
...activeReleaseClaims.claims,
|
|
123
120
|
...sessionOwnerClaims.claims
|
|
124
121
|
]) {
|
|
@@ -209,52 +206,6 @@ function readRuntimeIdentityClaims(home) {
|
|
|
209
206
|
});
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
|
-
function readExactTaskRuntimeClaims(home) {
|
|
213
|
-
const directory = join(home, "runtime", "exact-task-runtime");
|
|
214
|
-
let entries;
|
|
215
|
-
try {
|
|
216
|
-
entries = readdirSync(directory, { withFileTypes: true });
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
if (error.code === "ENOENT") {
|
|
220
|
-
return Object.freeze({ claims: Object.freeze([]), diagnostics: Object.freeze([]) });
|
|
221
|
-
}
|
|
222
|
-
return Object.freeze({
|
|
223
|
-
claims: Object.freeze([]),
|
|
224
|
-
diagnostics: Object.freeze([{
|
|
225
|
-
source: "controller",
|
|
226
|
-
severity: "error",
|
|
227
|
-
message: `exact Task runtime descriptors are unreadable: ${error instanceof Error ? error.message : "unknown error"}`
|
|
228
|
-
}])
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
const claims = [];
|
|
232
|
-
const diagnostics = [];
|
|
233
|
-
for (const entry of entries) {
|
|
234
|
-
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
235
|
-
continue;
|
|
236
|
-
const path = join(directory, entry.name);
|
|
237
|
-
try {
|
|
238
|
-
const descriptor = parseExactTaskRuntimeDescriptor(readFileSync(path, "utf8"));
|
|
239
|
-
claims.push({ path, token: `exact-task-runtime:${descriptor.taskId}` });
|
|
240
|
-
claims.push({
|
|
241
|
-
path: resolve(descriptor.workspace),
|
|
242
|
-
token: `exact-task-runtime:workspace:${descriptor.taskId}`
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
diagnostics.push({
|
|
247
|
-
source: "controller",
|
|
248
|
-
severity: "error",
|
|
249
|
-
message: `exact Task runtime descriptor is unreadable at ${path}: ${error instanceof Error ? error.message : "unknown error"}`
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return Object.freeze({
|
|
254
|
-
claims: Object.freeze(claims),
|
|
255
|
-
diagnostics: Object.freeze(diagnostics)
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
209
|
function readActiveReleaseClaims(home) {
|
|
259
210
|
try {
|
|
260
211
|
const active = resolveActiveRelease(home);
|
|
@@ -281,7 +232,7 @@ function readActiveReleaseClaims(home) {
|
|
|
281
232
|
}
|
|
282
233
|
}
|
|
283
234
|
/**
|
|
284
|
-
* Read Session owner records (`runtime/session-owners/<
|
|
235
|
+
* Read Session owner records (`runtime/session-owners/<runtimeGenerationId>.json`). A
|
|
285
236
|
* record whose Provider root is physically alive with a matching start
|
|
286
237
|
* identity protects its runtime root. A dead PID is stale (reconciliation
|
|
287
238
|
* removes those records); an identity conflict or unreadable record fails
|
|
@@ -350,7 +301,7 @@ function readSessionOwnerClaims(home) {
|
|
|
350
301
|
if (typeof record.runtimeRoot === "string" && record.runtimeRoot.length > 0) {
|
|
351
302
|
claims.push({
|
|
352
303
|
path: resolve(record.runtimeRoot),
|
|
353
|
-
token: `session-owner:${typeof record.
|
|
304
|
+
token: `session-owner:${typeof record.runtimeGenerationId === "string" ? record.runtimeGenerationId : entry.name}`
|
|
354
305
|
});
|
|
355
306
|
}
|
|
356
307
|
}
|
|
@@ -164,36 +164,7 @@ export async function discoverResources(input) {
|
|
|
164
164
|
function discoverRuntimeArtifacts(home, input) {
|
|
165
165
|
const discovered = [];
|
|
166
166
|
const now = input.now;
|
|
167
|
-
// 3a.
|
|
168
|
-
const exactDirectory = join(home, "runtime", "exact-task-runtime");
|
|
169
|
-
for (const entry of safeReaddir(exactDirectory)) {
|
|
170
|
-
if (!entry.name.endsWith(".json"))
|
|
171
|
-
continue;
|
|
172
|
-
const path = join(exactDirectory, entry.name);
|
|
173
|
-
const descriptor = readJsonFile(path);
|
|
174
|
-
const taskId = typeof descriptor?.taskId === "string" ? descriptor.taskId : undefined;
|
|
175
|
-
const owner = {
|
|
176
|
-
home,
|
|
177
|
-
...(taskId === undefined ? {} : { taskId }),
|
|
178
|
-
basis: taskId === undefined ? "unattributed" : "descriptor"
|
|
179
|
-
};
|
|
180
|
-
const taskStatus = taskId === undefined ? undefined : input.taskStatusById.get(taskId);
|
|
181
|
-
discovered.push({
|
|
182
|
-
record: createResourceRecord({
|
|
183
|
-
kind: "runtime-artifact",
|
|
184
|
-
path: resolve(path),
|
|
185
|
-
owner,
|
|
186
|
-
...(sizeOf(path) === undefined ? {} : { sizeBytes: sizeOf(path) }),
|
|
187
|
-
cleanliness: "n/a",
|
|
188
|
-
activeRefs: [],
|
|
189
|
-
disposition: "active"
|
|
190
|
-
}, now),
|
|
191
|
-
ownerTerminal: taskId === undefined
|
|
192
|
-
? false
|
|
193
|
-
: isTerminalTaskStatus(taskStatus)
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
// 3b. Task runtime isolation roots: <home>.task-runtimes/<taskId>/<owner>/<launch>/
|
|
167
|
+
// 3a. Task runtime isolation roots: <home>.task-runtimes/<taskId>/<owner>/<launch>/
|
|
197
168
|
const runtimeRoot = `${resolve(home)}.task-runtimes`;
|
|
198
169
|
if (existsSync(runtimeRoot)) {
|
|
199
170
|
for (const taskEntry of safeReaddir(runtimeRoot)) {
|
|
@@ -80,29 +80,6 @@ export class ResourceRegistrar {
|
|
|
80
80
|
cleanliness: "n/a"
|
|
81
81
|
})));
|
|
82
82
|
}
|
|
83
|
-
registerExactTaskRuntimeDescriptor(descriptor, sourcePath) {
|
|
84
|
-
try {
|
|
85
|
-
this.#registerExactTaskRuntimeDescriptor(descriptor, sourcePath);
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
// Best-effort receipt; descriptor writes must proceed regardless.
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
#registerExactTaskRuntimeDescriptor(descriptor, sourcePath) {
|
|
92
|
-
const path = resolve(sourcePath);
|
|
93
|
-
if (!existsSync(path) || isReleaseNamespacePath(this.#home, path))
|
|
94
|
-
return;
|
|
95
|
-
this.#save([this.#record({
|
|
96
|
-
kind: "runtime-artifact",
|
|
97
|
-
path,
|
|
98
|
-
owner: {
|
|
99
|
-
home: this.#home,
|
|
100
|
-
taskId: descriptor.taskId,
|
|
101
|
-
basis: "descriptor"
|
|
102
|
-
},
|
|
103
|
-
cleanliness: "n/a"
|
|
104
|
-
})]);
|
|
105
|
-
}
|
|
106
83
|
registerSessionContext(path, owner) {
|
|
107
84
|
try {
|
|
108
85
|
this.#registerSessionContext(path, owner);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { validateDeltaRecheckRecord } from "./reviewRound.js";
|
|
3
|
-
import { isSemanticReviewRound } from "./reviewOutcomeClassifier.js";
|
|
4
3
|
/**
|
|
5
4
|
* Assesses whether a trustworthy delta can be constructed. Only technical
|
|
6
5
|
* unavailability fails closed; Agents retain the semantic judgment.
|
|
7
6
|
*/
|
|
8
7
|
export async function assessDeltaRecheck(input) {
|
|
9
8
|
const { repositoryPaths, previousRound, candidate, git } = input;
|
|
10
|
-
if (
|
|
9
|
+
if (previousRound.status !== "completed"
|
|
10
|
+
|| previousRound.reviewerTurnId === undefined
|
|
11
11
|
|| (previousRound.scope ?? "work-item") !== "task") {
|
|
12
12
|
return {
|
|
13
13
|
kind: "ineligible",
|
|
@@ -114,7 +114,7 @@ export function verifyDeltaRecheckDiff(record, diffByProject) {
|
|
|
114
114
|
}
|
|
115
115
|
/** Builds the delta-recheck dispatch context block. */
|
|
116
116
|
export function buildDeltaRecheckDispatchContext(input) {
|
|
117
|
-
const { round, previousRound, diffByProject
|
|
117
|
+
const { round, previousRound, diffByProject } = input;
|
|
118
118
|
const record = round.deltaRecheck;
|
|
119
119
|
if (record === undefined) {
|
|
120
120
|
throw new Error(`ReviewRound is not a delta-recheck: ${round.id}.`);
|
|
@@ -129,7 +129,8 @@ export function buildDeltaRecheckDispatchContext(input) {
|
|
|
129
129
|
" semantic change, evidence doubt). This is the safe default.",
|
|
130
130
|
"Yui verified only the technical delta boundary; a Task-control Agent selected this mode.",
|
|
131
131
|
`Previous accepted ReviewRound: ${previousRound.id}@${record.previousBaseCommit}`,
|
|
132
|
-
`Previous
|
|
132
|
+
`Previous Reviewer Turn: ${previousRound.reviewerTurnId ?? "unavailable"}`,
|
|
133
|
+
"Read that exact source Turn's original result from the frozen Context Snapshot.",
|
|
133
134
|
...(round.taskCandidate?.projects.map((project) => {
|
|
134
135
|
const previous = previousRound.taskCandidate?.projects
|
|
135
136
|
.find((entry) => entry.projectId === project.projectId)?.commit;
|
|
@@ -143,73 +144,15 @@ export function buildDeltaRecheckDispatchContext(input) {
|
|
|
143
144
|
...Object.entries(diffByProject).flatMap(([projectId, diff]) => (diff.trim().length === 0
|
|
144
145
|
? []
|
|
145
146
|
: [`--- ${projectId} ---`, diff])),
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"Previous evidence references:",
|
|
150
|
-
...previousEvidenceReferences(previousRound).map((entry) => `- ${entry}`)
|
|
151
|
-
]),
|
|
152
|
-
ledgerContext,
|
|
153
|
-
"Report JSON with deltaDisposition (one of the three values above) and deltaReasoning",
|
|
154
|
-
"(the explicit proof or the reason a full Review is required). Findings, checks, and",
|
|
155
|
-
"evidence use the same fields as a full Review."
|
|
147
|
+
"Return one complete result with the chosen disposition, reasoning, findings,",
|
|
148
|
+
"verification, uncertainty, and recommended next action. Markdown or JSON is acceptable;",
|
|
149
|
+
"Yui Core preserves the original text and does not parse or validate its structure."
|
|
156
150
|
];
|
|
157
151
|
return lines.join("\n");
|
|
158
152
|
}
|
|
159
|
-
function compact(text, limit = 600) {
|
|
160
|
-
const flattened = text.replace(/\s+/gu, " ").trim();
|
|
161
|
-
return flattened.length <= limit ? flattened : `${flattened.slice(0, limit)}...`;
|
|
162
|
-
}
|
|
163
153
|
function digestDiff(diffByProject) {
|
|
164
154
|
const canonical = JSON.stringify(Object.entries(diffByProject)
|
|
165
155
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
166
156
|
.map(([projectId, diff]) => [projectId, diff]));
|
|
167
157
|
return createHash("sha256").update(canonical).digest("hex");
|
|
168
158
|
}
|
|
169
|
-
/** All path-like evidence references from Markdown or JSON Review reports. */
|
|
170
|
-
function previousEvidenceReferences(round) {
|
|
171
|
-
const report = round.report ?? "";
|
|
172
|
-
return [...new Set([
|
|
173
|
-
...extractDeclaredEvidence(report),
|
|
174
|
-
...extractEvidenceReferences(report)
|
|
175
|
-
])];
|
|
176
|
-
}
|
|
177
|
-
/** Preserve the free-form evidence array that older JSON reports exposed. */
|
|
178
|
-
function extractDeclaredEvidence(report) {
|
|
179
|
-
let parsed;
|
|
180
|
-
try {
|
|
181
|
-
parsed = JSON.parse(report);
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
return [];
|
|
185
|
-
}
|
|
186
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
187
|
-
return [];
|
|
188
|
-
const evidence = parsed.evidence;
|
|
189
|
-
if (!Array.isArray(evidence))
|
|
190
|
-
return [];
|
|
191
|
-
return evidence.flatMap((entry) => (typeof entry === "string" && entry.trim().length > 0 ? [entry.trim()] : []));
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Reviewer reports intentionally accept clear Markdown or JSON without a
|
|
195
|
-
* fixed schema. Extract likely repo-relative references from the preserved
|
|
196
|
-
* report so the next Reviewer can inspect the earlier evidence directly.
|
|
197
|
-
*/
|
|
198
|
-
function extractEvidenceReferences(report) {
|
|
199
|
-
const references = report.match(/(?:\.{0,2}\/)?[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+|[A-Za-z0-9_.-]+\.[A-Za-z0-9]{1,12}/gu) ?? [];
|
|
200
|
-
return [...new Set(references.map((entry) => entry.replace(/^\.\//u, "")))]
|
|
201
|
-
.filter((entry) => isPathLike(entry));
|
|
202
|
-
}
|
|
203
|
-
function isPathLike(value) {
|
|
204
|
-
if (value.length === 0 || value.length > 512)
|
|
205
|
-
return false;
|
|
206
|
-
if (/\s/u.test(value))
|
|
207
|
-
return false;
|
|
208
|
-
if (value.includes("://"))
|
|
209
|
-
return false;
|
|
210
|
-
// A repo-relative path: at least one slash or a known file extension, no
|
|
211
|
-
// shell/argument metacharacters.
|
|
212
|
-
if (/[;&|`$(){}!<>]/u.test(value))
|
|
213
|
-
return false;
|
|
214
|
-
return value.includes("/") || /\.[A-Za-z0-9]{1,12}$/u.test(value);
|
|
215
|
-
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
export function isCompletedReviewExecutionFromTurns(round, turns) {
|
|
2
|
+
if (round.status !== "completed" || round.reviewerTurnId === undefined)
|
|
3
|
+
return false;
|
|
4
|
+
const turn = turns.find(({ id }) => id === round.reviewerTurnId);
|
|
5
|
+
return turn !== undefined
|
|
6
|
+
&& turn.status === "completed"
|
|
7
|
+
&& turn.result !== undefined
|
|
8
|
+
&& turn.purpose === "review"
|
|
9
|
+
&& turn.taskId === round.taskId
|
|
10
|
+
&& turn.reviewRoundId === round.id
|
|
11
|
+
&& turn.roleName === round.reviewerRoleName
|
|
12
|
+
&& turn.executionGroupId === undefined
|
|
13
|
+
&& turn.effective.reviewBaseCommit === round.reviewBaseCommit;
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
|
-
* Whether a
|
|
4
|
-
*
|
|
5
|
-
* delta disposition or still owns an open material finding.
|
|
16
|
+
* Whether a Task-final ReviewRound has one exact completed main Reviewer Turn.
|
|
17
|
+
* This is structural evidence only and never means the Leader accepted it.
|
|
6
18
|
*/
|
|
7
|
-
export function
|
|
8
|
-
return
|
|
19
|
+
export function isCompletedTaskReviewEvidence(store, round) {
|
|
20
|
+
return isCompletedTaskReviewEvidenceFromTurns(round, store.listTurns(round.taskId));
|
|
9
21
|
}
|
|
10
|
-
|
|
11
|
-
export function isAcceptedTaskReviewBaselineFromEvidence(round, evidence) {
|
|
22
|
+
export function isCompletedTaskReviewEvidenceFromTurns(round, turns) {
|
|
12
23
|
if ((round.scope ?? "work-item") !== "task"
|
|
13
|
-
|| round.status !== "completed"
|
|
14
24
|
|| round.taskCandidate === undefined
|
|
15
|
-
|| round.taskCandidate.projects.length === 0
|
|
16
|
-
|| !isSemanticReviewRound(round, evidence))
|
|
17
|
-
return false;
|
|
18
|
-
if ((round.checks ?? []).some(({ outcome }) => outcome === "failed"))
|
|
19
|
-
return false;
|
|
20
|
-
if (round.deltaRecheck !== undefined
|
|
21
|
-
&& round.deltaRecheck.disposition !== "equivalent-and-accepted")
|
|
25
|
+
|| round.taskCandidate.projects.length === 0) {
|
|
22
26
|
return false;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
&& (finding.disposition === "open" || finding.disposition === "fixed-pending-review")));
|
|
27
|
+
}
|
|
28
|
+
return isCompletedReviewExecutionFromTurns(round, turns);
|
|
26
29
|
}
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import { requireIdentity } from "../domain/validation.js";
|
|
2
2
|
export const REVIEW_TRIGGERS = ["always", "leader", "final"];
|
|
3
|
-
/**
|
|
4
|
-
* Issue 06 feature flag. `shadow` records the finding ledger and serves Leader
|
|
5
|
-
* commands without changing completion behavior; `enforce` additionally fails
|
|
6
|
-
* Task completion closed on undispositioned open P1/P2 findings.
|
|
7
|
-
*/
|
|
8
|
-
export const REVIEW_FINDING_LEDGER_MODES = ["shadow", "enforce"];
|
|
9
3
|
export function validateReviewConfig(config) {
|
|
10
4
|
requireIdentity(config.roleName, "Review Role");
|
|
11
5
|
if (!REVIEW_TRIGGERS.includes(config.trigger)) {
|
|
12
6
|
throw new Error(`Review trigger is invalid: ${String(config.trigger)}.`);
|
|
13
7
|
}
|
|
14
|
-
if (config.findingLedger !== undefined
|
|
15
|
-
&& !REVIEW_FINDING_LEDGER_MODES.includes(config.findingLedger)) {
|
|
16
|
-
throw new Error(`Review finding ledger mode is invalid: ${String(config.findingLedger)}.`);
|
|
17
|
-
}
|
|
18
8
|
return config;
|
|
19
9
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
-
import {
|
|
2
|
+
import { isCompletedTaskReviewEvidence } from "./reviewAcceptance.js";
|
|
3
3
|
import { projectReviewerAvailability } from "./reviewerAvailability.js";
|
|
4
4
|
/** Read-only facts for Leader judgment; this projection never blocks an action. */
|
|
5
5
|
export function projectReviewDecision(input) {
|
|
6
6
|
const { store, task, roles, turns, rounds, reviewConfig, currentCandidate } = input;
|
|
7
7
|
const taskRounds = rounds.filter((round) => (round.scope ?? "work-item") === "task");
|
|
8
8
|
const accepted = [...taskRounds]
|
|
9
|
-
.filter((round) =>
|
|
9
|
+
.filter((round) => isCompletedTaskReviewEvidence(store, round))
|
|
10
10
|
.sort(compareRoundIdentity)
|
|
11
11
|
.at(-1);
|
|
12
12
|
const latestAcceptedBaseline = accepted?.taskCandidate === undefined
|