@zq-silk/yui 0.8.3 → 0.8.7
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 +40 -22
- package/README.md +66 -19
- package/dist/cli/commandCatalog.js +43 -14
- package/dist/cli/operatorWizard.js +10 -20
- package/dist/cli/updatePorts.js +6 -0
- package/dist/cli.js +252 -37
- package/dist/commands/executionAuditCommands.js +30 -0
- package/dist/commands/globalRoleCommands.js +8 -4
- package/dist/commands/operatorCommands.js +42 -1
- package/dist/commands/taskCommands.js +527 -147
- package/dist/commands/taskCompletionGate.js +36 -24
- package/dist/commands/taskContextCommand.js +11 -4
- package/dist/commands/taskInputCommands.js +48 -10
- package/dist/commands/taskNextActionCommand.js +38 -3
- package/dist/commands/taskOverviewCommand.js +2 -1
- package/dist/commands/taskRoleRuntimeStatus.js +2 -1
- package/dist/context/runContextPack.js +9 -5
- package/dist/context/sessionBootstrapManifest.js +158 -11
- package/dist/context/wakeNotification.js +5 -3
- package/dist/controller/clientRuntime.js +15 -15
- package/dist/controller/controller.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +67 -7
- package/dist/controller/handoverCandidate.js +10 -3
- package/dist/controller/sessionNotify.js +4 -22
- package/dist/executor/agentAdapter.js +2 -2
- package/dist/executor/agentExecutor.js +29 -9
- package/dist/executor/fileRoleLaunchPlanner.js +37 -45
- package/dist/integration/gitIntegrationService.js +50 -2
- package/dist/integration/integrationCheckEvidenceReuse.js +53 -0
- package/dist/observability/executionAudit.js +47 -1
- package/dist/observability/faultClassification.js +6 -4
- package/dist/observability/orchestrationMetrics.js +196 -0
- package/dist/operator/operatorSessionHistory.js +36 -0
- package/dist/release/releaseHandover.js +7 -5
- package/dist/release/runtimeRelease.js +15 -0
- package/dist/repository/taskWorkspaceCoordinator.js +13 -10
- package/dist/review/deltaRecheck.js +3 -2
- package/dist/review/reviewFindingLedger.js +5 -4
- package/dist/review/reviewOutcomeClassifier.js +263 -54
- package/dist/review/taskFinalReviewContractEvent.js +1 -0
- package/dist/review/taskFinalReviewContractRebind.js +367 -0
- package/dist/run/runIdentity.js +10 -70
- package/dist/runtime/agentHost.js +3 -4
- package/dist/runtime/codexAppServerRuntime.js +6 -0
- package/dist/runtime/exactControlPlane.js +47 -37
- package/dist/runtime/firstProgressStopLoss.js +54 -0
- package/dist/runtime/launchBroker.js +10 -2
- package/dist/runtime/runtimeDeadlines.js +14 -0
- package/dist/runtime/sessionTitle.js +24 -12
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/runtime/tmuxAdapters.js +10 -3
- package/dist/scheduler/actionability.js +4 -2
- package/dist/scheduler/activeRoleRunDelivery.js +20 -18
- package/dist/scheduler/activeTaskProgress.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +33 -2
- package/dist/scheduler/taskExecutionProjection.js +13 -4
- package/dist/scheduler/wakeReason.js +1 -0
- package/dist/storage/sqliteStore.js +18 -3
- package/dist/storage/taskStore.js +14 -3
- package/dist/task/completionReadiness.js +48 -19
- package/dist/task/deliveryGuard.js +3 -1
- package/dist/task/nextAction.js +153 -55
- package/dist/task/repairWave.js +14 -1
- package/dist/task/task.js +10 -0
- package/dist/task/taskRecordRetirement.js +72 -0
- package/dist/web/webSnapshot.js +7 -1
- package/dist/workItem/workItem.js +6 -4
- package/i18n/README.zh-CN.md +48 -9
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +73 -31
- package/skills/yui-operator/SKILL.md +58 -10
- package/skills/yui-reviewer/SKILL.md +23 -0
- package/skills/yui-runtime/SKILL.md +6 -6
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
import { normalizeRoleAgentSessionText, roleAgentSessionRef, validateRoleSessionSet } from "../executor/agentExecutor.js";
|
|
2
|
+
/** Separates the one selected writer authority from retained conversations. */
|
|
3
|
+
export function projectOperatorStatus(sessions, activeAgentId, activeAdapterId) {
|
|
4
|
+
if (sessions === null) {
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
writer: { state: "unrecorded", agentId: activeAgentId, adapterId: activeAdapterId },
|
|
7
|
+
historicalConversations: []
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
validateRoleSessionSet(sessions);
|
|
11
|
+
// The GlobalRole binding is the writer authority. The SessionSet pointer is
|
|
12
|
+
// retained operational state and may lag a Role update, so it cannot select
|
|
13
|
+
// a second Operator writer.
|
|
14
|
+
const active = sessions.sessions[activeAgentId];
|
|
15
|
+
const matchingActive = active?.adapterId === activeAdapterId ? active : undefined;
|
|
16
|
+
const activeRef = matchingActive === undefined ? undefined : operatorSessionRef(matchingActive);
|
|
17
|
+
const historicalConversations = listOperatorSessions(sessions)
|
|
18
|
+
.filter((entry) => entry.ref !== activeRef)
|
|
19
|
+
.map((entry) => ({ ...entry, state: "history" }));
|
|
20
|
+
if (matchingActive === undefined) {
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
writer: { state: "unrecorded", agentId: activeAgentId, adapterId: activeAdapterId },
|
|
23
|
+
historicalConversations
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
writer: {
|
|
28
|
+
state: matchingActive.status === "stopped" || matchingActive.status === "broken" ? "inactive" : "active",
|
|
29
|
+
agentId: matchingActive.agentId,
|
|
30
|
+
adapterId: matchingActive.adapterId,
|
|
31
|
+
sessionRef: activeRef,
|
|
32
|
+
nativeSessionId: matchingActive.nativeSessionId,
|
|
33
|
+
sessionStatus: matchingActive.status
|
|
34
|
+
},
|
|
35
|
+
historicalConversations
|
|
36
|
+
});
|
|
37
|
+
}
|
|
2
38
|
export function operatorSessionRef(session) {
|
|
3
39
|
return roleAgentSessionRef(session);
|
|
4
40
|
}
|
|
@@ -13,17 +13,19 @@
|
|
|
13
13
|
* the candidate read-only and reports dual-owner; a crashed activator resumes
|
|
14
14
|
* from the recorded phase.
|
|
15
15
|
*/
|
|
16
|
+
import { RELEASE_HANDOVER_PROMOTION_TIMEOUT_MS } from "../runtime/runtimeDeadlines.js";
|
|
16
17
|
import { acquireHandoverLock, isOwnerLive, newHandoverId, readActiveReleasePointer, readCandidateDiscovery, readHandoverFence, readHandoverReceipt, readRuntimeIdentity, removeCandidateDiscovery, removeHandoverFence, writeActiveReleasePointer, writeHandoverFence, writeHandoverReceipt } from "./runtimeRelease.js";
|
|
17
18
|
export const DEFAULT_CANDIDATE_READY_TIMEOUT_MS = 30_000;
|
|
18
|
-
export const DEFAULT_PROMOTION_TIMEOUT_MS =
|
|
19
|
+
export const DEFAULT_PROMOTION_TIMEOUT_MS = RELEASE_HANDOVER_PROMOTION_TIMEOUT_MS;
|
|
19
20
|
export const DEFAULT_POLL_INTERVAL_MS = 100;
|
|
20
21
|
/**
|
|
21
22
|
* Optional confirmation debounce after the candidate latches `dualOwner:
|
|
22
23
|
* true`. Defaults to 0: the candidate's own exit grace
|
|
23
|
-
* (`DEFAULT_DUAL_OWNER_GRACE_MS` in `handoverCandidate.ts
|
|
24
|
-
* authoritative old-owner exit window
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* (`DEFAULT_DUAL_OWNER_GRACE_MS` in `handoverCandidate.ts`) is the single
|
|
25
|
+
* authoritative old-owner exit window. It outlives the complete Controller
|
|
26
|
+
* shutdown/drain boundary, and the activator trusts the candidate's latched
|
|
27
|
+
* signal. A non-zero value only adds a short extra confirmation before reporting
|
|
28
|
+
* dual-owner; it must never be used to re-litigate the exit grace.
|
|
27
29
|
*/
|
|
28
30
|
export const DEFAULT_DUAL_OWNER_GRACE_MS = 0;
|
|
29
31
|
export async function activateRelease(ports, options) {
|
|
@@ -251,6 +251,21 @@ export function writeCandidateDiscovery(home, candidate) {
|
|
|
251
251
|
export function removeCandidateDiscovery(home) {
|
|
252
252
|
rmSync(join(resolve(home), CANDIDATE_DISCOVERY_PATH), { force: true });
|
|
253
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Read-only scheduler fence for the short release/rebind critical section.
|
|
256
|
+
* A stale owner does not block work; an unreadable lock fails closed for
|
|
257
|
+
* bounded Operator diagnosis instead of dispatching across an unknown fence.
|
|
258
|
+
*/
|
|
259
|
+
export function isHandoverLockHeld(home) {
|
|
260
|
+
const lockPath = join(resolve(home), "runtime", "handover.lock");
|
|
261
|
+
try {
|
|
262
|
+
const owner = JSON.parse(readFileSync(lockPath, "utf8"));
|
|
263
|
+
return isHandoverLockLive(owner);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
return !isEnoent(error);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
254
269
|
export function acquireHandoverLock(home) {
|
|
255
270
|
const lockPath = join(resolve(home), "runtime", "handover.lock");
|
|
256
271
|
mkdirSync(dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
@@ -108,6 +108,14 @@ export class TaskWorkspaceCoordinator {
|
|
|
108
108
|
await this.#stopLiveRoles(item.taskId, this.#workItemRoleNames(item));
|
|
109
109
|
return "released";
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Stops one Task Role's physical runtime without changing its workspace.
|
|
113
|
+
* The caller owns the subsequent atomic record retirement and wake.
|
|
114
|
+
*/
|
|
115
|
+
async cleanupTaskRoleRuntime(taskId, roleName) {
|
|
116
|
+
await this.#stopLiveRoles(taskId, [roleName]);
|
|
117
|
+
return "released";
|
|
118
|
+
}
|
|
111
119
|
async cleanupReviewRound(taskId, reviewRoundId) {
|
|
112
120
|
const round = this.store.getReviewRound(taskId, reviewRoundId);
|
|
113
121
|
if (round === null)
|
|
@@ -384,17 +392,12 @@ export class TaskWorkspaceCoordinator {
|
|
|
384
392
|
const observedPanes = inspect?.(taskId);
|
|
385
393
|
const live = targets.filter((roleName) => {
|
|
386
394
|
const sessions = this.store.getTaskRoleSessionSet(taskId, roleName);
|
|
387
|
-
const activeSession = sessions === null
|
|
388
|
-
? undefined
|
|
389
|
-
: sessions.activeAgentId === undefined
|
|
390
|
-
// Narrow test doubles and restored callers predating activeAgentId
|
|
391
|
-
// still conservatively represent any nonterminal record as live.
|
|
392
|
-
? Object.values(sessions.sessions).find(({ status }) => status !== "stopped" && status !== "broken")
|
|
393
|
-
: sessions.sessions[sessions.activeAgentId];
|
|
394
395
|
return observedPanes?.some((pane) => pane.roleName === roleName && !pane.dead) === true
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
396
|
+
// A terminal current Session can still carry a resumable native id or
|
|
397
|
+
// Provider binding. Exact cleanup retires both before a workspace or
|
|
398
|
+
// release-control transition is allowed to wake this Role again.
|
|
399
|
+
|| (sessions !== null && (Object.keys(sessions.sessions).length > 0
|
|
400
|
+
|| sessions.providerBinding !== null));
|
|
398
401
|
});
|
|
399
402
|
if (live.length > 0)
|
|
400
403
|
await this.runtime.stopTaskRoleSessions(taskId, live);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { deltaRecheckMaxChangedFiles, deltaRecheckMaxChangedLines } from "./reviewConfig.js";
|
|
3
3
|
import { validateDeltaRecheckRecord } from "./reviewRound.js";
|
|
4
|
+
import { isSemanticReviewRound } from "./reviewOutcomeClassifier.js";
|
|
4
5
|
/**
|
|
5
6
|
* Assesses whether a delta-recheck may be attempted. Every deterministic
|
|
6
7
|
* gate fails closed here; semantic equivalence is always left to the
|
|
@@ -8,11 +9,11 @@ import { validateDeltaRecheckRecord } from "./reviewRound.js";
|
|
|
8
9
|
*/
|
|
9
10
|
export async function assessDeltaRecheck(input) {
|
|
10
11
|
const { repositoryPaths, previousRound, candidate, git, config } = input;
|
|
11
|
-
if (previousRound
|
|
12
|
+
if (!isSemanticReviewRound(previousRound)
|
|
12
13
|
|| (previousRound.scope ?? "work-item") !== "task") {
|
|
13
14
|
return {
|
|
14
15
|
kind: "ineligible",
|
|
15
|
-
reason: "Delta recheck requires a completed Task-final ReviewRound."
|
|
16
|
+
reason: "Delta recheck requires a semantic completed Task-final ReviewRound."
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
if (previousRound.taskCandidate === undefined) {
|
|
@@ -117,11 +117,11 @@ export function reconcileReviewFindings(store, taskId, roundId, now) {
|
|
|
117
117
|
if (round === null) {
|
|
118
118
|
return { roundId, skipped: true, reason: "ReviewRound not found.", created: [], updated: [], conflicts: [] };
|
|
119
119
|
}
|
|
120
|
-
if (!isSemanticReviewRound(round)) {
|
|
120
|
+
if (!isSemanticReviewRound(round, store)) {
|
|
121
121
|
return {
|
|
122
122
|
roundId,
|
|
123
123
|
skipped: true,
|
|
124
|
-
reason: "ReviewRound is
|
|
124
|
+
reason: "ReviewRound is non-semantic or ambiguous, not a proven semantic report.",
|
|
125
125
|
created: [],
|
|
126
126
|
updated: [],
|
|
127
127
|
conflicts: []
|
|
@@ -464,7 +464,7 @@ export function renderFindingLedgerContext(summary) {
|
|
|
464
464
|
export function reusableTaskReviewEvidence(store, taskId, candidate) {
|
|
465
465
|
const rounds = store.listReviewRounds(taskId)
|
|
466
466
|
.filter((round) => (round.scope ?? "work-item") === "task"
|
|
467
|
-
&& round
|
|
467
|
+
&& isSemanticReviewRound(round, store)
|
|
468
468
|
&& round.evidenceCommit !== undefined
|
|
469
469
|
&& isSameTaskReviewCandidate(round.taskCandidate, candidate))
|
|
470
470
|
.sort((left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }));
|
|
@@ -492,7 +492,8 @@ export function reusableTaskReviewEvidence(store, taskId, candidate) {
|
|
|
492
492
|
*/
|
|
493
493
|
export function buildTaskFinalReviewFindingContext(store, taskId, candidate) {
|
|
494
494
|
const previousSemanticRound = store.listReviewRounds(taskId)
|
|
495
|
-
.filter((round) => (round.scope ?? "work-item") === "task"
|
|
495
|
+
.filter((round) => (round.scope ?? "work-item") === "task"
|
|
496
|
+
&& isSemanticReviewRound(round, store))
|
|
496
497
|
.sort((left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }))
|
|
497
498
|
.at(-1) ?? null;
|
|
498
499
|
const reusableEvidence = reusableTaskReviewEvidence(store, taskId, candidate);
|
|
@@ -1,61 +1,270 @@
|
|
|
1
|
+
import { matchYieldReceipt } from "../run/yieldReceipt.js";
|
|
2
|
+
import { isTaskRecordRetired, operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
1
3
|
const INFRA_SIGNATURES = [
|
|
2
|
-
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
},
|
|
6
|
-
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
|
|
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
|
-
}
|
|
4
|
+
{ kind: "session-not-stopped", pattern: /session must be stopped before workspace migration/iu },
|
|
5
|
+
{ kind: "run-start", pattern: /role run could not start|could not start (?:the )?(?:reviewer|role) run/iu },
|
|
6
|
+
{ kind: "storage-lock", pattern: /\.state\.lock|state lock|lock timeout|storage lock/iu },
|
|
7
|
+
{ kind: "tmux-exit", pattern: /tmux[^\n]{0,80}(?:exited|exit|died|vanished)|pane (?:exited|died)/iu },
|
|
8
|
+
{ kind: "yield-timeout", pattern: /yield timeout|controller yield timeout|yield timed out/iu },
|
|
9
|
+
{ kind: "run-identity", pattern: /wrong run id|unknown run id|run id (?:is )?(?:invalid|unknown|mismatch)/iu },
|
|
10
|
+
{ kind: "policy", pattern: /cyber_?policy|policy denial|permission denied by policy/iu },
|
|
11
|
+
{ kind: "baseline-contamination", pattern: /cross[-\s]?baseline|baseline pollution|contaminated baseline|wrong base sha/iu },
|
|
12
|
+
{ kind: "context-load", pattern: /(?:run )?(?:context|context pack) load (?:failed|unavailable|unauthorized|stale|mismatched|malformed)/iu },
|
|
13
|
+
{ kind: "workspace-binding", pattern: /workspace is not the durable owner|workspace-binding failure/iu }
|
|
34
14
|
];
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
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")
|
|
15
|
+
/** Classify one terminal Round without rewriting it. */
|
|
16
|
+
export function classifyReviewRoundOutcome(round, evidence) {
|
|
17
|
+
if (round.status !== "completed" && round.status !== "failed")
|
|
45
18
|
return null;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
19
|
+
if (evidence !== undefined && round.reviewerRunId !== undefined
|
|
20
|
+
&& isTaskRecordRetired(evidence.listEvents(round.taskId), "agent-run", round.reviewerRunId)) {
|
|
21
|
+
return {
|
|
22
|
+
kind: "non-semantic",
|
|
23
|
+
infraKind: "run-identity",
|
|
24
|
+
reason: `Reviewer Run ${round.reviewerRunId} was retired from operational evidence.`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const infraKind = classifyInfraKind(`${round.summary ?? ""}\n${round.report ?? ""}`);
|
|
28
|
+
if (round.status === "failed") {
|
|
29
|
+
const semanticEvidence = failedRoundSemanticEvidence(round, evidence);
|
|
30
|
+
return semanticEvidence === null
|
|
31
|
+
? {
|
|
32
|
+
kind: "non-semantic",
|
|
33
|
+
infraKind,
|
|
34
|
+
reason: "Failed Review execution carries no semantic report evidence."
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
kind: "ambiguous",
|
|
38
|
+
infraKind,
|
|
39
|
+
reason: semanticEvidence
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!explicitCompletedReviewInfrastructureFailure(round.summary ?? "", round)) {
|
|
43
|
+
return { kind: "semantic", reason: "Reviewer delivered a terminal report." };
|
|
44
|
+
}
|
|
45
|
+
if (evidence === undefined) {
|
|
46
|
+
return {
|
|
47
|
+
kind: "ambiguous",
|
|
48
|
+
infraKind,
|
|
49
|
+
reason: "Completed Round claims an infrastructure failure but corroborating Run/Event evidence was not supplied."
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const corroborationFailure = completedInfrastructureCorroborationFailure(round, evidence);
|
|
53
|
+
return corroborationFailure === null
|
|
54
|
+
? {
|
|
55
|
+
kind: "non-semantic",
|
|
56
|
+
infraKind,
|
|
57
|
+
reason: "Completed Round and yielded Run agree on an explicit pre-review infrastructure failure."
|
|
50
58
|
}
|
|
59
|
+
: {
|
|
60
|
+
kind: "ambiguous",
|
|
61
|
+
infraKind,
|
|
62
|
+
reason: corroborationFailure
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** True only when a Round may consume semantic Review budget or feed findings. */
|
|
66
|
+
export function isSemanticReviewRound(round, evidence) {
|
|
67
|
+
return classifyReviewRoundOutcome(round, evidence)?.kind === "semantic";
|
|
68
|
+
}
|
|
69
|
+
function classifyInfraKind(text) {
|
|
70
|
+
return INFRA_SIGNATURES.find(({ pattern }) => pattern.test(text))?.kind ?? "other-infra";
|
|
71
|
+
}
|
|
72
|
+
function failedRoundSemanticEvidence(round, evidence) {
|
|
73
|
+
if ((round.checks ?? []).length > 0)
|
|
74
|
+
return "Failed Round records review checks.";
|
|
75
|
+
if (round.evidenceCommit !== undefined)
|
|
76
|
+
return "Failed Round records a review evidence commit.";
|
|
77
|
+
if (round.deltaRecheck?.disposition !== undefined || round.deltaRecheck?.reasoning !== undefined) {
|
|
78
|
+
return "Failed Round records a semantic delta-recheck disposition.";
|
|
79
|
+
}
|
|
80
|
+
if (round.report !== round.summary)
|
|
81
|
+
return "Failed Round stores a report distinct from its terminal summary.";
|
|
82
|
+
if (runtimeFailureSummaryHasReviewerOutput(round.report ?? "")) {
|
|
83
|
+
return "Failed Round stores non-empty Reviewer output in its runtime failure summary.";
|
|
51
84
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
if (looksLikeStructuredReviewReport(round.report ?? "")) {
|
|
86
|
+
return "Failed Round stores a structured Reviewer report.";
|
|
87
|
+
}
|
|
88
|
+
const semanticLane = (round.executionGroup?.lanes ?? []).find((lane) => ((lane.result?.checks ?? []).length > 0
|
|
89
|
+
|| (lane.result?.findings ?? []).length > 0
|
|
90
|
+
|| (lane.result?.evidence ?? []).length > 0
|
|
91
|
+
|| lane.result?.evidenceCommit !== undefined
|
|
92
|
+
|| lane.result?.gitSnapshot !== undefined
|
|
93
|
+
|| lane.status === "yielded"
|
|
94
|
+
|| lane.status === "completed"));
|
|
95
|
+
if (semanticLane !== undefined)
|
|
96
|
+
return `Reviewer Lane ${semanticLane.id} delivered semantic evidence.`;
|
|
97
|
+
if (evidence !== undefined) {
|
|
98
|
+
const events = evidence.listEvents(round.taskId);
|
|
99
|
+
const reviewRun = operationalTaskRecords(evidence.listAgentRuns(round.taskId), events, "agent-run").find((run) => (run.purpose === "review"
|
|
100
|
+
&& run.reviewRoundId === round.id
|
|
101
|
+
&& (run.status === "yielded" || runtimeFailureSummaryHasReviewerOutput(run.summary ?? ""))));
|
|
102
|
+
if (reviewRun !== undefined)
|
|
103
|
+
return `Reviewer Run ${reviewRun.id} records Reviewer output.`;
|
|
104
|
+
const finding = evidence.listReviewFindings(round.taskId).find((entry) => (entry.firstReviewRoundId === round.id || entry.lastReviewRoundId === round.id));
|
|
105
|
+
if (finding !== undefined)
|
|
106
|
+
return `Review finding ${finding.id} references the Round.`;
|
|
107
|
+
const completion = events.find((event) => (event.type === "review.completed" && event.payload.reviewRoundId === round.id));
|
|
108
|
+
if (completion !== undefined)
|
|
109
|
+
return `Review completion Event ${completion.id} exists.`;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
57
112
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
113
|
+
function completedInfrastructureCorroborationFailure(round, store) {
|
|
114
|
+
if ((round.checks ?? []).length > 0)
|
|
115
|
+
return "Completed Round records review checks.";
|
|
116
|
+
if (round.evidenceCommit !== round.reviewBaseCommit) {
|
|
117
|
+
return "Completed Round lacks an exact frozen-head evidence commit.";
|
|
118
|
+
}
|
|
119
|
+
if (round.report !== round.summary) {
|
|
120
|
+
return "Completed Round stores a report distinct from its terminal summary.";
|
|
121
|
+
}
|
|
122
|
+
if (runtimeFailureSummaryHasReviewerOutput(round.report ?? "")) {
|
|
123
|
+
return "Completed Round stores non-empty Reviewer output in its runtime failure summary.";
|
|
124
|
+
}
|
|
125
|
+
if (round.deltaRecheck?.disposition !== undefined || round.deltaRecheck?.reasoning !== undefined) {
|
|
126
|
+
return "Completed Round records a semantic delta-recheck disposition.";
|
|
127
|
+
}
|
|
128
|
+
if (looksLikeStructuredReviewReport(round.report ?? "")) {
|
|
129
|
+
return "Completed Round stores a structured Reviewer report.";
|
|
130
|
+
}
|
|
131
|
+
for (const lane of round.executionGroup?.lanes ?? []) {
|
|
132
|
+
if (lane.status === "pending" || lane.status === "running") {
|
|
133
|
+
return `Reviewer Lane ${lane.id} is still active.`;
|
|
134
|
+
}
|
|
135
|
+
if ((lane.result?.checks ?? []).length > 0
|
|
136
|
+
|| (lane.result?.findings ?? []).length > 0
|
|
137
|
+
|| (lane.result?.evidence ?? []).length > 0
|
|
138
|
+
|| (lane.result?.evidenceCommit !== undefined
|
|
139
|
+
&& lane.result.evidenceCommit !== round.reviewBaseCommit)
|
|
140
|
+
|| lane.result?.gitSnapshot !== undefined) {
|
|
141
|
+
return `Reviewer Lane ${lane.id} delivered semantic evidence.`;
|
|
142
|
+
}
|
|
143
|
+
if (lane.status === "yielded" || lane.status === "completed") {
|
|
144
|
+
if (lane.result === undefined
|
|
145
|
+
|| lane.result.summary !== round.summary
|
|
146
|
+
|| lane.result.report !== round.report
|
|
147
|
+
|| lane.result.evidenceCommit !== round.reviewBaseCommit) {
|
|
148
|
+
return `Reviewer Lane ${lane.id} output is absent or differs from the Round.`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
return `Completed Round has non-completed Reviewer Lane ${lane.id}/${lane.status}.`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const allEvents = store.listEvents(round.taskId);
|
|
156
|
+
const runs = operationalTaskRecords(store.listAgentRuns(round.taskId), allEvents, "agent-run").filter((run) => (run.purpose === "review" && run.reviewRoundId === round.id));
|
|
157
|
+
const active = runs.find(({ status }) => status === "active");
|
|
158
|
+
if (active !== undefined)
|
|
159
|
+
return `Reviewer Run ${active.id} is still active.`;
|
|
160
|
+
const output = runs.find((run) => (run.status === "failed" && runtimeFailureSummaryHasReviewerOutput(run.summary ?? "")));
|
|
161
|
+
if (output !== undefined)
|
|
162
|
+
return `Reviewer Run ${output.id} records Reviewer output.`;
|
|
163
|
+
const yielded = runs.filter(({ status }) => status === "yielded");
|
|
164
|
+
if (round.reviewerRunId === undefined
|
|
165
|
+
|| yielded.length !== 1
|
|
166
|
+
|| yielded[0].id !== round.reviewerRunId) {
|
|
167
|
+
return "Completed Round lacks one exact yielded Reviewer Run.";
|
|
168
|
+
}
|
|
169
|
+
const run = yielded[0];
|
|
170
|
+
if (run.roleName !== round.reviewerRoleName
|
|
171
|
+
|| run.summary !== round.summary
|
|
172
|
+
|| run.yieldReceipt === undefined) {
|
|
173
|
+
return `Reviewer Run ${run.id} does not match the non-semantic Round receipt.`;
|
|
174
|
+
}
|
|
175
|
+
const receiptMatch = matchYieldReceipt(run.yieldReceipt, {
|
|
176
|
+
status: "yielded",
|
|
177
|
+
summary: round.summary ?? "",
|
|
178
|
+
reviewResult: {
|
|
179
|
+
report: round.report ?? "",
|
|
180
|
+
checks: round.checks ?? [],
|
|
181
|
+
evidenceCommit: round.reviewBaseCommit
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
if (receiptMatch?.kind !== "replayed") {
|
|
185
|
+
return `Reviewer Run ${run.id} yield receipt does not cover the Round outcome.`;
|
|
186
|
+
}
|
|
187
|
+
const finding = store.listReviewFindings(round.taskId).find((entry) => (entry.firstReviewRoundId === round.id || entry.lastReviewRoundId === round.id));
|
|
188
|
+
if (finding !== undefined)
|
|
189
|
+
return `Review finding ${finding.id} references the Round.`;
|
|
190
|
+
const events = allEvents.filter((event) => (event.type === "review.completed" && event.payload.reviewRoundId === round.id));
|
|
191
|
+
if (events.length !== 1)
|
|
192
|
+
return "Completed Round lacks one exact completion Event.";
|
|
193
|
+
const event = events[0];
|
|
194
|
+
if (event.payload.workItemId !== round.workItemId
|
|
195
|
+
|| event.payload.candidateId !== round.candidateId
|
|
196
|
+
|| event.payload.reviewBaseCommit !== round.reviewBaseCommit
|
|
197
|
+
|| event.payload.evidenceCommit !== round.reviewBaseCommit
|
|
198
|
+
|| event.payload.checks !== "none") {
|
|
199
|
+
return `Review completion Event ${event.id} carries mismatched or semantic evidence.`;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
function explicitCompletedReviewInfrastructureFailure(summary, round) {
|
|
204
|
+
if (exactCompletedReviewInfrastructureFailureReport(summary))
|
|
205
|
+
return true;
|
|
206
|
+
const report = summary.trim();
|
|
207
|
+
if (report === `Role Run workspace is not the durable owner: ${round.taskId}/${round.reviewerRoleName}.`
|
|
208
|
+
|| (round.reviewerRunId !== undefined
|
|
209
|
+
&& report === `Review Run workspace is not the durable owner: ${round.reviewerRunId}.`)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
return /^(?:Run )?(?:Context|Context Pack) load (?:failed|unavailable|unauthorized|stale|mismatched|malformed)(?:: (?:failure|mismatch|unavailable|unauthorized|stale|mismatched|malformed))?\.?$/iu
|
|
213
|
+
.test(report);
|
|
214
|
+
}
|
|
215
|
+
function exactCompletedReviewInfrastructureFailureReport(summary) {
|
|
216
|
+
const lines = summary.trim().split(/\r?\n/u);
|
|
217
|
+
const envelope = [
|
|
218
|
+
/^# Review result: (?:context-load|workspace-binding) failure$/u,
|
|
219
|
+
/^$/u,
|
|
220
|
+
/^The assigned Run context could not be safely matched to this native session, so no candidate review was performed\.$/u,
|
|
221
|
+
/^$/u,
|
|
222
|
+
/^- Run: `[^`\r\n]+`$/u,
|
|
223
|
+
/^- ReviewRound: `[^`\r\n]+`$/u,
|
|
224
|
+
/^- Review base commit: `[0-9a-f]{40}`$/u,
|
|
225
|
+
/^- Frozen target: `[^`\r\n]+`$/u,
|
|
226
|
+
/^- Authorized workspace from the exact Context Pack: `[^`\r\n]+`$/u,
|
|
227
|
+
/^- Session-attached workspace: `[^`\r\n]+`$/u,
|
|
228
|
+
/^- Verification: both paths resolve distinctly and have different filesystem inodes \(`[^`\r\n]+` vs `[^`\r\n]+`\)\.$/u,
|
|
229
|
+
/^$/u,
|
|
230
|
+
/^## Findings$/u,
|
|
231
|
+
/^$/u,
|
|
232
|
+
/^- Verified-fixed findings: none; review did not start\.$/u,
|
|
233
|
+
/^- New findings: none; candidate sources were intentionally not inspected\.$/u,
|
|
234
|
+
/^- Accepted risks: none accepted\.$/u,
|
|
235
|
+
/^- 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,
|
|
236
|
+
/^$/u,
|
|
237
|
+
/^## Checks actually run$/u,
|
|
238
|
+
/^$/u,
|
|
239
|
+
/^- Exact Context API load: passed for Task\/Run\/Role\/purpose\/subject\/snapshot\/adapter\.$/u,
|
|
240
|
+
/^- Workspace binding verification: failed\.$/u,
|
|
241
|
+
/^- Candidate build\/tests\/package checks: not run\.$/u,
|
|
242
|
+
/^- Real-provider E2E: not run and not authorized\.$/u,
|
|
243
|
+
/^$/u,
|
|
244
|
+
/^## Required next action$/u,
|
|
245
|
+
/^$/u,
|
|
246
|
+
/^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
|
|
247
|
+
];
|
|
248
|
+
return lines.length === envelope.length
|
|
249
|
+
&& envelope.every((pattern, index) => pattern.test(lines[index]));
|
|
250
|
+
}
|
|
251
|
+
function runtimeFailureSummaryHasReviewerOutput(summary) {
|
|
252
|
+
const output = /(?:^|\n)last_assistant_message:[ \t]*([\s\S]*)$/u.exec(summary)?.[1];
|
|
253
|
+
return output !== undefined && output.trim().length > 0;
|
|
254
|
+
}
|
|
255
|
+
function looksLikeStructuredReviewReport(report) {
|
|
256
|
+
let parsed;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(report);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
264
|
+
return false;
|
|
265
|
+
const record = parsed;
|
|
266
|
+
return [
|
|
267
|
+
"summary", "report", "checks", "findings", "evidence", "evidenceCommit",
|
|
268
|
+
"deltaDisposition", "deltaReasoning"
|
|
269
|
+
].some((key) => Object.hasOwn(record, key));
|
|
61
270
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TASK_FINAL_REVIEW_CONTRACT_REBOUND_EVENT = "review.task-final-contract-rebound";
|