loopforge 3.9.4 → 4.0.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/README.md +236 -112
- package/dist/audit.d.ts +73 -15
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +131 -61
- package/dist/audit.js.map +1 -1
- package/dist/canonical-state.d.ts +114 -40
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +276 -182
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +41 -12
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-governance.d.ts +0 -2
- package/dist/cognitive-governance.d.ts.map +1 -1
- package/dist/cognitive-governance.js +0 -4
- package/dist/cognitive-governance.js.map +1 -1
- package/dist/committed-round.d.ts +97 -156
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +223 -419
- package/dist/committed-round.js.map +1 -1
- package/dist/constraint-lifecycle.d.ts +73 -0
- package/dist/constraint-lifecycle.d.ts.map +1 -0
- package/dist/constraint-lifecycle.js +174 -0
- package/dist/constraint-lifecycle.js.map +1 -0
- package/dist/context-boundary.d.ts +83 -0
- package/dist/context-boundary.d.ts.map +1 -0
- package/dist/context-boundary.js +101 -0
- package/dist/context-boundary.js.map +1 -0
- package/dist/contract-items.d.ts +12 -8
- package/dist/contract-items.d.ts.map +1 -1
- package/dist/contract-items.js +31 -26
- package/dist/contract-items.js.map +1 -1
- package/dist/contract-requirement.d.ts +47 -0
- package/dist/contract-requirement.d.ts.map +1 -0
- package/dist/contract-requirement.js +51 -0
- package/dist/contract-requirement.js.map +1 -0
- package/dist/criterion-status.d.ts +35 -0
- package/dist/criterion-status.d.ts.map +1 -0
- package/dist/criterion-status.js +89 -0
- package/dist/criterion-status.js.map +1 -0
- package/dist/enforcement-gate.d.ts +26 -38
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +196 -319
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +9 -103
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +27 -897
- package/dist/engine.js.map +1 -1
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/evidence-claims.d.ts +33 -31
- package/dist/evidence-claims.d.ts.map +1 -1
- package/dist/evidence-claims.js +58 -72
- package/dist/evidence-claims.js.map +1 -1
- package/dist/evidence-posture-view.d.ts +19 -0
- package/dist/evidence-posture-view.d.ts.map +1 -0
- package/dist/evidence-posture-view.js +116 -0
- package/dist/evidence-posture-view.js.map +1 -0
- package/dist/evidence-posture.d.ts +73 -0
- package/dist/evidence-posture.d.ts.map +1 -0
- package/dist/evidence-posture.js +281 -0
- package/dist/evidence-posture.js.map +1 -0
- package/dist/evidence-provider.d.ts +55 -37
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +182 -332
- package/dist/evidence-provider.js.map +1 -1
- package/dist/explain.d.ts +166 -27
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +372 -58
- package/dist/explain.js.map +1 -1
- package/dist/field-accounting.d.ts.map +1 -1
- package/dist/field-accounting.js +3 -22
- package/dist/field-accounting.js.map +1 -1
- package/dist/generate-schema.js +13 -8
- package/dist/generate-schema.js.map +1 -1
- package/dist/git-boundary.d.ts +128 -0
- package/dist/git-boundary.d.ts.map +1 -0
- package/dist/git-boundary.js +625 -0
- package/dist/git-boundary.js.map +1 -0
- package/dist/git-facts.d.ts +22 -0
- package/dist/git-facts.d.ts.map +1 -0
- package/dist/git-facts.js +29 -0
- package/dist/git-facts.js.map +1 -0
- package/dist/git-motion.d.ts +37 -0
- package/dist/git-motion.d.ts.map +1 -0
- package/dist/git-motion.js +59 -0
- package/dist/git-motion.js.map +1 -0
- package/dist/history.d.ts +44 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +77 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +16 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +80 -14
- package/dist/init.js.map +1 -1
- package/dist/loop-compiler.d.ts +39 -23
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +368 -565
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +6 -5
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +153 -35
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/round-lifecycle.d.ts +255 -81
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +1552 -539
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +42 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +81 -39
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +956 -282
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/submission-boundary.d.ts +23 -31
- package/dist/mcp/submission-boundary.d.ts.map +1 -1
- package/dist/mcp/submission-boundary.js +242 -92
- package/dist/mcp/submission-boundary.js.map +1 -1
- package/dist/mcp/submission-handle.d.ts +66 -0
- package/dist/mcp/submission-handle.d.ts.map +1 -0
- package/dist/mcp/submission-handle.js +173 -0
- package/dist/mcp/submission-handle.js.map +1 -0
- package/dist/mcp/tools.d.ts +17 -7
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +707 -244
- package/dist/mcp/tools.js.map +1 -1
- package/dist/open-question.d.ts +7 -0
- package/dist/open-question.d.ts.map +1 -0
- package/dist/open-question.js +14 -0
- package/dist/open-question.js.map +1 -0
- package/dist/policy-metrics.d.ts +31 -21
- package/dist/policy-metrics.d.ts.map +1 -1
- package/dist/policy-metrics.js +117 -64
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +11 -39
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +12 -12
- package/dist/policy.js.map +1 -1
- package/dist/progress.d.ts +45 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +136 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-assembler.d.ts +20 -6
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +475 -479
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-coverage.d.ts +0 -6
- package/dist/prompt-coverage.d.ts.map +1 -1
- package/dist/prompt-coverage.js +4 -8
- package/dist/prompt-coverage.js.map +1 -1
- package/dist/prompt-freshness.d.ts +6 -17
- package/dist/prompt-freshness.d.ts.map +1 -1
- package/dist/prompt-freshness.js +27 -22
- package/dist/prompt-freshness.js.map +1 -1
- package/dist/prompt-policy.d.ts +14 -7
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +21 -5
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +843 -424
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +73 -102
- package/dist/protocol.js.map +1 -1
- package/dist/recovery-guidance.d.ts +13 -0
- package/dist/recovery-guidance.d.ts.map +1 -0
- package/dist/recovery-guidance.js +15 -0
- package/dist/recovery-guidance.js.map +1 -0
- package/dist/recovery.d.ts +68 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +234 -0
- package/dist/recovery.js.map +1 -0
- package/dist/replay.d.ts +42 -8
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +311 -84
- package/dist/replay.js.map +1 -1
- package/dist/result-facts.d.ts +45 -0
- package/dist/result-facts.d.ts.map +1 -0
- package/dist/result-facts.js +86 -0
- package/dist/result-facts.js.map +1 -0
- package/dist/round-contract.d.ts +38 -8
- package/dist/round-contract.d.ts.map +1 -1
- package/dist/round-contract.js +71 -31
- package/dist/round-contract.js.map +1 -1
- package/dist/round-coordinator.d.ts +41 -89
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +250 -267
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +56 -16
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +225 -39
- package/dist/round-driver.js.map +1 -1
- package/dist/round-facts.d.ts +33 -52
- package/dist/round-facts.d.ts.map +1 -1
- package/dist/round-facts.js +51 -160
- package/dist/round-facts.js.map +1 -1
- package/dist/round-guidance.d.ts +56 -0
- package/dist/round-guidance.d.ts.map +1 -0
- package/dist/round-guidance.js +161 -0
- package/dist/round-guidance.js.map +1 -0
- package/dist/round-outcome.d.ts +21 -0
- package/dist/round-outcome.d.ts.map +1 -0
- package/dist/round-outcome.js +31 -0
- package/dist/round-outcome.js.map +1 -0
- package/dist/round-transaction.d.ts +168 -21
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +837 -38
- package/dist/round-transaction.js.map +1 -1
- package/dist/self-eval.d.ts +37 -61
- package/dist/self-eval.d.ts.map +1 -1
- package/dist/self-eval.js +413 -392
- package/dist/self-eval.js.map +1 -1
- package/dist/storage.d.ts +16 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +54 -4
- package/dist/storage.js.map +1 -1
- package/dist/subgoal-state.d.ts +2 -3
- package/dist/subgoal-state.d.ts.map +1 -1
- package/dist/subgoal-state.js +2 -3
- package/dist/subgoal-state.js.map +1 -1
- package/dist/submission-guidance.d.ts +29 -21
- package/dist/submission-guidance.d.ts.map +1 -1
- package/dist/submission-guidance.js +308 -260
- package/dist/submission-guidance.js.map +1 -1
- package/dist/submission-receipt.d.ts +16 -8
- package/dist/submission-receipt.d.ts.map +1 -1
- package/dist/submission-receipt.js +145 -51
- package/dist/submission-receipt.js.map +1 -1
- package/dist/token-utils.d.ts +28 -7
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +27 -11
- package/dist/token-utils.js.map +1 -1
- package/dist/transaction-identity.d.ts +9 -0
- package/dist/transaction-identity.d.ts.map +1 -0
- package/dist/transaction-identity.js +69 -0
- package/dist/transaction-identity.js.map +1 -0
- package/dist/verification-actions.d.ts.map +1 -1
- package/dist/verification-actions.js +10 -11
- package/dist/verification-actions.js.map +1 -1
- package/dist/verification-gate.d.ts +23 -47
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +202 -423
- package/dist/verification-gate.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +187 -0
- package/dist/working-memory.d.ts.map +1 -0
- package/dist/working-memory.js +767 -0
- package/dist/working-memory.js.map +1 -0
- package/dist/workspace-checkpoint.d.ts +88 -0
- package/dist/workspace-checkpoint.d.ts.map +1 -0
- package/dist/workspace-checkpoint.js +326 -0
- package/dist/workspace-checkpoint.js.map +1 -0
- package/dist/workspace-restore.d.ts +68 -0
- package/dist/workspace-restore.d.ts.map +1 -0
- package/dist/workspace-restore.js +604 -0
- package/dist/workspace-restore.js.map +1 -0
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +0 -17
- package/dist/workspace.js.map +1 -1
- package/loop_policy.json +4 -18
- package/package.json +68 -68
- package/skills/forge/SKILL.md +212 -0
- package/skills/forge/references/evidence-and-contracts.md +204 -0
- package/skills/forge/references/recovery-and-views.md +188 -0
- package/dist/cognitive-facts.d.ts +0 -30
- package/dist/cognitive-facts.d.ts.map +0 -1
- package/dist/cognitive-facts.js +0 -113
- package/dist/cognitive-facts.js.map +0 -1
- package/dist/loop-extras-parser.d.ts +0 -81
- package/dist/loop-extras-parser.d.ts.map +0 -1
- package/dist/loop-extras-parser.js +0 -213
- package/dist/loop-extras-parser.js.map +0 -1
- package/dist/loop-projection.d.ts +0 -6
- package/dist/loop-projection.d.ts.map +0 -1
- package/dist/loop-projection.js +0 -28
- package/dist/loop-projection.js.map +0 -1
- package/skills/perception/SKILL.md +0 -389
|
@@ -17,26 +17,37 @@
|
|
|
17
17
|
* Persistence is owned by round-transaction.ts so reject paths remain
|
|
18
18
|
* side-effect free and accepted decisions can be replayed idempotently.
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
import { CHECK_SUCCESS_WITHOUT_VERIFIED_EVIDENCE,
|
|
22
|
-
import {
|
|
20
|
+
import { emptyCommittedHistory, historyWindow, loadCommittedHistory, } from "./history.js";
|
|
21
|
+
import { CHECK_SUCCESS_WITHOUT_VERIFIED_EVIDENCE, verifySelfEvaluation, } from "./verification-gate.js";
|
|
22
|
+
import { checkpointForRoundBoundary, deriveRecoveryDirective, validateCheckpointManifest, } from "./recovery.js";
|
|
23
23
|
import { driftedEntrypoints } from "./evidence-provider.js";
|
|
24
|
-
import { declaresBlocked, effectiveOutcome, effectiveSuccess } from "./
|
|
25
|
-
import {
|
|
26
|
-
import { enforceRound, buildRejectionPrompt, findSafeRestorePoint,
|
|
24
|
+
import { declaresBlocked, effectiveOutcome, effectiveSuccess, } from "./round-outcome.js";
|
|
25
|
+
import { deriveRoundProgress, } from "./progress.js";
|
|
26
|
+
import { enforceRound, buildRejectionPrompt, findSafeRestorePoint, } from "./enforcement-gate.js";
|
|
27
27
|
import { logEvent } from "./observability.js";
|
|
28
28
|
import { getPolicy } from "./policy.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
29
|
+
import { deriveRoundFacts } from "./round-facts.js";
|
|
30
|
+
import { deriveEvidencePosture, rollbackEvidencePosture, } from "./evidence-posture.js";
|
|
31
|
+
import { deriveFilesReconciliation } from "./files-reconciliation.js";
|
|
32
|
+
/** v3.2/v3.6/v4.0.3: Whether a round's success enters the success
|
|
33
|
+
* trajectory.
|
|
34
|
+
*
|
|
35
|
+
* The trajectory counts MACHINE-BACKED success only: the Agent must
|
|
36
|
+
* effectively claim success AND the verification gate must have found a
|
|
37
|
+
* passed, untampered after command (`machineBackedSuccess`). A partial or
|
|
38
|
+
* blocked outcome, an unbacked success claim, a contradicted gate, a
|
|
39
|
+
* redirect round and an unverified contract item all keep the round out.
|
|
40
|
+
* This does not change `completed` — it only keeps progress metrics honest. */
|
|
41
|
+
function shouldPushSuccess(selfEval, gateContradicted, verificationFlags, machineBackedSuccess, contractStatuses) {
|
|
42
|
+
if (!effectiveSuccess(selfEval))
|
|
43
|
+
return false;
|
|
44
|
+
if (!machineBackedSuccess)
|
|
45
|
+
return false;
|
|
38
46
|
if (gateContradicted)
|
|
39
47
|
return false;
|
|
48
|
+
// v3.9.9: a redirect round abandons a slice; it is never success evidence.
|
|
49
|
+
if (contractStatuses?.closure === "superseded")
|
|
50
|
+
return false;
|
|
40
51
|
// v3.8: an unverified contract item (or a claimed-but-unbacked success)
|
|
41
52
|
// keeps the round out of the success trajectory — an accepted-but-unverified
|
|
42
53
|
// round is not evidence of progress.
|
|
@@ -68,120 +79,48 @@ function shouldPushSuccess(gateContradicted, verificationFlags, contractStatuses
|
|
|
68
79
|
export function finalizeStopReason(
|
|
69
80
|
/** `undefined` when the disposition named no terminal reason at all (the
|
|
70
81
|
* callers fall back to their own default) — there is nothing to guard. */
|
|
71
|
-
reason, selfEval, contractStatuses
|
|
82
|
+
reason, selfEval, contractStatuses,
|
|
83
|
+
/** v3.9.9/v4.0.6: the current round's redirect. A committed redirect can
|
|
84
|
+
* never produce `completed` even if the old items were all verified. */
|
|
85
|
+
redirect) {
|
|
72
86
|
if (reason !== "completed")
|
|
73
87
|
return { reason, flags: [] };
|
|
74
88
|
const declared = declaresBlocked(selfEval);
|
|
75
89
|
const blockedContract = contractStatuses?.closure === "blocked";
|
|
76
|
-
if (
|
|
77
|
-
return {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
// ── RoundCoordinator ───────────────────────────────────────────────────────
|
|
94
|
-
/** v3.8.1: The rolled-back branch's facts — the ONE derivation of which rounds
|
|
95
|
-
* failed, which approaches must not be repeated, which assumptions were
|
|
96
|
-
* falsified, and which files (with their failed-round git fingerprints) the
|
|
97
|
-
* agent must restore.
|
|
98
|
-
*
|
|
99
|
-
* Sources are COMMITTED rounds above the restore point; rejected attempts are
|
|
100
|
-
* not durable history and never become a source. The in-flight attempt that
|
|
101
|
-
* triggered the rollback is the one exception — its evaluation lives in this
|
|
102
|
-
* process, not in committed history.
|
|
103
|
-
*
|
|
104
|
-
* Extracted from the coordinator's inline walk so it is unit-testable without
|
|
105
|
-
* driving a whole rollback, and so the prompt, the state file and the
|
|
106
|
-
* committed decision all describe one event. */
|
|
107
|
-
export function deriveBacktrackRecoveryFacts(input) {
|
|
108
|
-
const { currentRound, restoreRound, vaultEntries, selfEval } = input;
|
|
109
|
-
// v2.13: files changed in skipped rounds, for the restore prompt.
|
|
110
|
-
const skippedFiles = [];
|
|
111
|
-
// M3 (v3.7.x): each skipped file's git fingerprint at its failed round.
|
|
112
|
-
// Recorded from the round's own after-evidence (machine data, not the
|
|
113
|
-
// agent's report); later rounds overwrite earlier ones so the map holds the
|
|
114
|
-
// LAST failed state of every file.
|
|
115
|
-
const skippedFingerprints = {};
|
|
116
|
-
const failedRounds = [];
|
|
117
|
-
const approaches = [];
|
|
118
|
-
const wrongAssumptions = [];
|
|
119
|
-
for (let r = restoreRound + 1; r < currentRound; r++) {
|
|
120
|
-
// v3.2.1: match the feedback entry (execution_report lives there) — the
|
|
121
|
-
// compile-time lineage entry for the same round precedes it in the flat
|
|
122
|
-
// view and carries no evidence.
|
|
123
|
-
const entry = vaultEntries.find((e) => entryRound(e) === r && e.task_type !== "loop_lineage");
|
|
124
|
-
const view = entry ? decodeCommittedRound(entry) : null;
|
|
125
|
-
const evaluation = view?.evaluation ?? null;
|
|
126
|
-
if (entry?.execution_report) {
|
|
127
|
-
const ev = entry.execution_report;
|
|
128
|
-
const reported = Array.isArray(ev.files_changed)
|
|
129
|
-
? ev.files_changed.filter((f) => typeof f === "string")
|
|
130
|
-
: [];
|
|
131
|
-
// v3.9.1: the machine delta is the primary set of files the failed
|
|
132
|
-
// round changed; the agent report is unioned in so an ignored file the
|
|
133
|
-
// agent named is not lost from the recovery brief.
|
|
134
|
-
const machineFiles = (view?.observationDelta ?? [])
|
|
135
|
-
.filter((delta) => delta.kind === "git" && delta.changedFiles !== null)
|
|
136
|
-
.flatMap((delta) => delta.changedFiles);
|
|
137
|
-
const files = [...new Set([...machineFiles, ...reported])];
|
|
138
|
-
const git = view?.afterEvidence.find((snapshot) => snapshot.kind === "git" &&
|
|
139
|
-
snapshot.status === "observed" &&
|
|
140
|
-
isRecord(snapshot.data.fingerprints));
|
|
141
|
-
const gitFingerprints = git && git.kind === "git"
|
|
142
|
-
? git.data.fingerprints
|
|
143
|
-
: null;
|
|
144
|
-
for (const f of files) {
|
|
145
|
-
if (!skippedFiles.includes(f))
|
|
146
|
-
skippedFiles.push(f);
|
|
147
|
-
const fp = gitFingerprints?.[f];
|
|
148
|
-
if (typeof fp === "string")
|
|
149
|
-
skippedFingerprints[f] = fp;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const summary = evaluation?.output_summary ?? "";
|
|
153
|
-
if (summary.trim().length > 0) {
|
|
154
|
-
failedRounds.push(r);
|
|
155
|
-
approaches.push(summary.trim().slice(0, 200));
|
|
156
|
-
}
|
|
157
|
-
const assumptions = (evaluation?.wrong_assumptions ?? [])
|
|
158
|
-
.filter((v) => typeof v === "string")
|
|
159
|
-
.map((s) => s.slice(0, 500));
|
|
160
|
-
for (const a of assumptions) {
|
|
161
|
-
if (!wrongAssumptions.includes(a))
|
|
162
|
-
wrongAssumptions.push(a);
|
|
163
|
-
}
|
|
90
|
+
if (declared || blockedContract) {
|
|
91
|
+
return {
|
|
92
|
+
reason: "blocked",
|
|
93
|
+
flags: [{
|
|
94
|
+
severity: "warn",
|
|
95
|
+
field: declared ? "outcome" : "round_contract",
|
|
96
|
+
check: "completion_truth_downgraded",
|
|
97
|
+
detail: declared
|
|
98
|
+
? "The round declared a blocked outcome but the stop decision produced " +
|
|
99
|
+
"`completed`; the declaration wins and the stop reason was downgraded " +
|
|
100
|
+
"to `blocked`."
|
|
101
|
+
: "The active contract closed on a blocked outcome but the stop decision " +
|
|
102
|
+
"produced `completed`; the stop reason was downgraded to `blocked`.",
|
|
103
|
+
}],
|
|
104
|
+
};
|
|
164
105
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
106
|
+
const superseded = contractStatuses?.closure === "superseded" ||
|
|
107
|
+
Boolean(redirect);
|
|
108
|
+
if (superseded) {
|
|
109
|
+
return {
|
|
110
|
+
reason: "incomplete",
|
|
111
|
+
flags: [{
|
|
112
|
+
severity: "warn",
|
|
113
|
+
field: "round_contract.supersedes",
|
|
114
|
+
check: "completion_truth_downgraded",
|
|
115
|
+
detail: "The round superseded its active contract, so the old slice is not " +
|
|
116
|
+
"verified and the round cannot be `completed`; the stop reason was " +
|
|
117
|
+
"downgraded to `incomplete`.",
|
|
118
|
+
}],
|
|
119
|
+
};
|
|
176
120
|
}
|
|
177
|
-
return {
|
|
178
|
-
skippedFiles,
|
|
179
|
-
skippedFingerprints,
|
|
180
|
-
failedRounds,
|
|
181
|
-
approaches,
|
|
182
|
-
wrongAssumptions,
|
|
183
|
-
};
|
|
121
|
+
return { reason, flags: [] };
|
|
184
122
|
}
|
|
123
|
+
// ── RoundCoordinator ───────────────────────────────────────────────────────
|
|
185
124
|
export class RoundCoordinator {
|
|
186
125
|
store;
|
|
187
126
|
constructor(store) {
|
|
@@ -198,77 +137,97 @@ export class RoundCoordinator {
|
|
|
198
137
|
* - Transactional feedback commit (accepted rounds only)
|
|
199
138
|
* - State file I/O (both paths, after compiling) */
|
|
200
139
|
processRound(input) {
|
|
201
|
-
const { loopId, task, currentRound, maxRounds, selfEval,
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
// the
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
?
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
140
|
+
const { loopId, task, currentRound, maxRounds, selfEval, consecutiveRejections, evidenceDeltas, } = input;
|
|
141
|
+
const agentSuccessClaim = effectiveSuccess(selfEval);
|
|
142
|
+
const agentOutcome = effectiveOutcome(selfEval);
|
|
143
|
+
// ── 1. Load committed history once ──────────────────────────────────
|
|
144
|
+
// The verification gate and the contract reducer read the same flat entry
|
|
145
|
+
// view and the same committed window; one store read serves both. The
|
|
146
|
+
// advance path passes the history it already loaded for its own
|
|
147
|
+
// pre-commit facts.
|
|
148
|
+
const history = input.history ?? (this.store
|
|
149
|
+
? loadCommittedHistory(this.store, loopId)
|
|
150
|
+
: emptyCommittedHistory(loopId));
|
|
151
|
+
const vaultEntries = history.entries;
|
|
152
|
+
// v3.9.6: the previous evaluation is the last committed branch round,
|
|
153
|
+
// never a session copy.
|
|
154
|
+
const committedRounds = historyWindow(history, currentRound);
|
|
155
|
+
const previousCommitted = committedRounds.length > 0
|
|
156
|
+
? committedRounds[committedRounds.length - 1].evaluation
|
|
157
|
+
: null;
|
|
215
158
|
// ── 2. Verification gate ────────────────────────────────────────────
|
|
216
|
-
|
|
159
|
+
// v3.9.7: the old post-backtrack restore checks/R9 are gone. LoopForge
|
|
160
|
+
// verifies the restore before issuing the redo prompt; a redo submission
|
|
161
|
+
// is ordinary work, so its after boundary must never be compared to the
|
|
162
|
+
// restore target.
|
|
163
|
+
// v4.0.3: ONE RoundFacts derivation for this round. The verification
|
|
164
|
+
// gate, the stop decision, the success trajectory and the evidence
|
|
165
|
+
// posture all consume this bundle; none derives the contract again.
|
|
166
|
+
const commands = getPolicy().evidence.commands ?? [];
|
|
167
|
+
const roundFacts = deriveRoundFacts({
|
|
168
|
+
rounds: committedRounds,
|
|
169
|
+
currentRound,
|
|
170
|
+
inFlight: {
|
|
171
|
+
currentReport: selfEval.execution_report ?? null,
|
|
172
|
+
currentDeltas: evidenceDeltas ?? [],
|
|
173
|
+
currentBlocked: declaresBlocked(selfEval),
|
|
174
|
+
currentRedirect: selfEval.round_contract?.supersedes ?? null,
|
|
175
|
+
},
|
|
176
|
+
commands,
|
|
177
|
+
});
|
|
178
|
+
const verifyResult = verifySelfEvaluation(selfEval, currentRound, vaultEntries, previousCommitted, evidenceDeltas ?? [],
|
|
217
179
|
// v3.7.1: gate records live under the gate: prefix — outside the round
|
|
218
180
|
// entries above — and are passed separately to the gate check.
|
|
219
|
-
|
|
220
|
-
? queryLoopEntries(this.store, loopId, { prefix: `loop:${loopId}:gate:` })
|
|
221
|
-
: [],
|
|
181
|
+
history.entries.filter((entry) => String(entry.task_id ?? "").startsWith(`loop:${loopId}:gate:`)),
|
|
222
182
|
// v3.8.3: the absolute arm of the entrypoint check. Derived here, not in
|
|
223
183
|
// the gate, because it needs the filesystem and the in-flight trust map
|
|
224
184
|
// captured when this round was prepared — the gate stays a pure function
|
|
225
185
|
// of its inputs.
|
|
226
|
-
driftedEntrypoints(input.entrypointTrust),
|
|
227
|
-
// v3.9: the restore point's own dirty files — the baseline the restored
|
|
228
|
-
// workspace is compared against.
|
|
229
|
-
input.backtrackTargetDirty,
|
|
186
|
+
driftedEntrypoints(input.entrypointTrust),
|
|
230
187
|
// v3.9.4: the SAME manifest basis the receipt uses. An omitted
|
|
231
188
|
// files_changed field is not silently treated as "a report exists".
|
|
232
189
|
input.submittedFields
|
|
233
190
|
? input.submittedFields.includes("execution_report.files_changed")
|
|
234
|
-
: undefined
|
|
191
|
+
: undefined,
|
|
192
|
+
// v4.0.3: the one derivation, computed above. Passing it removes the
|
|
193
|
+
// gate's private contract/item/claim derivation.
|
|
194
|
+
roundFacts, maxRounds);
|
|
235
195
|
const verificationFlags = verifyResult.flags;
|
|
236
196
|
const gateContradicted = verifyResult.verdict === "contradicted";
|
|
237
|
-
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
197
|
+
const { activeContract, itemStatuses: activeContractStatuses } = roundFacts;
|
|
198
|
+
// v4.0.3: the evidence posture is derived ONCE here, from the same raw
|
|
199
|
+
// facts every other branch reads. It is persisted with an accepted round;
|
|
200
|
+
// a rejected/terminated attempt carries only the transient copy.
|
|
201
|
+
const verificationConfigured = commands.some((command) => command.enabled === true &&
|
|
202
|
+
(command.phase === "after" || command.phase === "both"));
|
|
203
|
+
const filesReconciliation = deriveFilesReconciliation(selfEval.execution_report ?? null, evidenceDeltas ?? [], input.submittedFields
|
|
204
|
+
? {
|
|
205
|
+
attempted: input.submittedFields.includes("execution_report.files_changed"),
|
|
206
|
+
}
|
|
207
|
+
: {});
|
|
208
|
+
const evaluationPosture = deriveEvidencePosture({
|
|
245
209
|
round: currentRound,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
210
|
+
kind: "evaluation",
|
|
211
|
+
selfEval,
|
|
212
|
+
afterEvidence: input.afterEvidence ?? [],
|
|
213
|
+
evidenceDeltas: evidenceDeltas ?? [],
|
|
214
|
+
verificationFlags,
|
|
215
|
+
machineBackedSuccess: verifyResult.machineBackedSuccess,
|
|
216
|
+
activeContract,
|
|
217
|
+
contractStatuses: activeContractStatuses,
|
|
218
|
+
verificationConfigured,
|
|
219
|
+
filesReconciliation,
|
|
220
|
+
});
|
|
221
|
+
const evaluationResult = (result) => ({
|
|
222
|
+
...result,
|
|
223
|
+
evidencePosture: evaluationPosture,
|
|
224
|
+
agentOutcome,
|
|
225
|
+
});
|
|
226
|
+
const rollbackResult = (result) => ({
|
|
227
|
+
...result,
|
|
228
|
+
evidencePosture: rollbackEvidencePosture(currentRound),
|
|
229
|
+
agentOutcome: null,
|
|
250
230
|
});
|
|
251
|
-
// v3.8.3: the legal exit from scope drift — a round that declares itself
|
|
252
|
-
// blocked may switch contracts, but only by declaring a proposal whose
|
|
253
|
-
// scope covers EVERY file it changed out of scope. Computed here because
|
|
254
|
-
// this is the only place where the round's git delta, the active
|
|
255
|
-
// contract's scope and the submitted proposal are all in reach; the
|
|
256
|
-
// enforcement gate receives the verdict, not the inputs.
|
|
257
|
-
//
|
|
258
|
-
// `selfEval.round_contract` is already the parsed proposal (buildSelfEvaluation
|
|
259
|
-
// runs parseRoundContract), so the coverage test reads the same object the
|
|
260
|
-
// commit path will read back.
|
|
261
|
-
//
|
|
262
|
-
// The coverage requirement is what keeps this from being a universal
|
|
263
|
-
// escape: without it "declare blocked" would dissolve a machine fact
|
|
264
|
-
// without declaring anything. The drift FLAG is untouched — it still lands
|
|
265
|
-
// on the committed round, so audit/explain keep reporting it.
|
|
266
|
-
const driftedFiles = roundDriftFiles(activeContract, evidenceDeltas ?? []);
|
|
267
|
-
const proposal = selfEval.round_contract;
|
|
268
|
-
const scopeDriftWaiver = driftedFiles !== null && driftedFiles.length > 0 &&
|
|
269
|
-
declaresBlocked(selfEval) &&
|
|
270
|
-
proposal !== undefined &&
|
|
271
|
-
collectOutOfScopeFiles(driftedFiles, proposal.scope).length === 0;
|
|
272
231
|
if (gateContradicted) {
|
|
273
232
|
logEvent("gate_contradicted", {
|
|
274
233
|
loopId,
|
|
@@ -282,11 +241,11 @@ export class RoundCoordinator {
|
|
|
282
241
|
// transaction reaches enforcement with a fully extracted self-evaluation.
|
|
283
242
|
// The old v2.5 "heuristic partial enforcement" plumbing (extractionSucceeded
|
|
284
243
|
// flag, enforcement skipEvidenceRules mode) was removed as unreachable.
|
|
285
|
-
const enforceResult = enforceRound(selfEval, verifyResult, currentRound, vaultEntries, consecutiveRejections, input.lastRejectionCheck ?? "",
|
|
286
|
-
// v3.9: what THIS round moved,
|
|
287
|
-
// committed
|
|
288
|
-
//
|
|
289
|
-
|
|
244
|
+
const enforceResult = enforceRound(selfEval, verifyResult, currentRound, vaultEntries, consecutiveRejections, input.lastRejectionCheck ?? "",
|
|
245
|
+
// v3.9.9: what THIS round moved, under the SAME Git workspace selector
|
|
246
|
+
// the committed window uses. Unknown/not_applicable never gains the
|
|
247
|
+
// stall excuse, and no other provider's fact can change this verdict.
|
|
248
|
+
deriveRoundProgress(evidenceDeltas ?? []));
|
|
290
249
|
if (enforceResult.action === "reject") {
|
|
291
250
|
const newRejections = consecutiveRejections + 1;
|
|
292
251
|
const rejectionPrompt = buildRejectionPrompt(currentRound, task, enforceResult, verificationFlags);
|
|
@@ -297,21 +256,18 @@ export class RoundCoordinator {
|
|
|
297
256
|
check: enforceResult.check ?? "",
|
|
298
257
|
consecutiveRejections: newRejections,
|
|
299
258
|
});
|
|
300
|
-
return {
|
|
259
|
+
return evaluationResult({
|
|
301
260
|
action: "reject",
|
|
302
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
303
261
|
rejectionPrompt,
|
|
304
262
|
verificationFlags,
|
|
305
263
|
enforcementAction: "reject",
|
|
306
264
|
enforcementReason: enforceResult.reason,
|
|
307
265
|
rejectionCheck: enforceResult.check,
|
|
308
|
-
|
|
266
|
+
agentSuccessClaim,
|
|
309
267
|
gateContradicted,
|
|
310
268
|
newConsecutiveRejections: newRejections,
|
|
311
|
-
// Don't update lastSelfEval on reject — the agent redoes the same round
|
|
312
|
-
newLastSelfEval: undefined,
|
|
313
269
|
shouldPushSuccessTrajectory: false,
|
|
314
|
-
};
|
|
270
|
+
});
|
|
315
271
|
}
|
|
316
272
|
if (enforceResult.action === "terminate") {
|
|
317
273
|
// v3.8: a row may name the stop reason it terminates with (the
|
|
@@ -323,96 +279,129 @@ export class RoundCoordinator {
|
|
|
323
279
|
stopReason: terminateReason,
|
|
324
280
|
round: currentRound,
|
|
325
281
|
});
|
|
326
|
-
return {
|
|
282
|
+
return evaluationResult({
|
|
327
283
|
action: "terminate",
|
|
328
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
329
284
|
stopReason: terminateReason,
|
|
330
285
|
verificationFlags,
|
|
331
286
|
enforcementAction: "terminate",
|
|
332
287
|
enforcementReason: enforceResult.reason,
|
|
333
288
|
rejectionCheck: enforceResult.check,
|
|
334
|
-
|
|
289
|
+
agentSuccessClaim,
|
|
335
290
|
gateContradicted,
|
|
336
291
|
newConsecutiveRejections: 0,
|
|
337
|
-
newLastSelfEval: selfEval,
|
|
338
292
|
shouldPushSuccessTrajectory: false,
|
|
339
|
-
};
|
|
293
|
+
});
|
|
340
294
|
}
|
|
341
|
-
// ── v2.10: Backtrack
|
|
295
|
+
// ── v2.10 / v3.9.7: Backtrack ─────────────────────────────────────────
|
|
342
296
|
if (enforceResult.action === "backtrack") {
|
|
343
297
|
const maxDepth = getPolicy().engine.backtrack_max_depth;
|
|
344
|
-
|
|
298
|
+
// The state after round T is the before-image of round T+1. A candidate
|
|
299
|
+
// is eligible only when that image was materialized and its manifest is
|
|
300
|
+
// readable; there is no verification-only fallback.
|
|
301
|
+
const resolveRef = (restoreRound) => {
|
|
302
|
+
const redoRound = restoreRound + 1;
|
|
303
|
+
const ref = checkpointForRoundBoundary({
|
|
304
|
+
entries: vaultEntries,
|
|
305
|
+
heldRound: currentRound,
|
|
306
|
+
heldBeforeCheckpoint: input.beforeCheckpoint,
|
|
307
|
+
redoRound,
|
|
308
|
+
});
|
|
309
|
+
if (!ref)
|
|
310
|
+
return null;
|
|
311
|
+
if (input.resolveCheckpointManifest) {
|
|
312
|
+
const manifest = input.resolveCheckpointManifest(ref);
|
|
313
|
+
// Machine target selection must prove the ref and manifest are
|
|
314
|
+
// self-consistent BEFORE committing an immutable directive. Blob
|
|
315
|
+
// and HEAD checks still run in the post-commit preflight.
|
|
316
|
+
if (!manifest || !validateCheckpointManifest(ref, manifest).ok) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return ref;
|
|
321
|
+
};
|
|
322
|
+
const restorePoint = findSafeRestorePoint(currentRound, vaultEntries, maxDepth, (restoreRound) => resolveRef(restoreRound) !== null);
|
|
345
323
|
if (!restorePoint) {
|
|
346
|
-
// No clean round
|
|
324
|
+
// No clean round with a materialized restore point within maxDepth.
|
|
325
|
+
logEvent("session_end", {
|
|
326
|
+
loopId,
|
|
327
|
+
stopReason: "enforcement_terminated",
|
|
328
|
+
round: currentRound,
|
|
329
|
+
});
|
|
330
|
+
return evaluationResult({
|
|
331
|
+
action: "terminate",
|
|
332
|
+
stopReason: "enforcement_terminated",
|
|
333
|
+
verificationFlags,
|
|
334
|
+
enforcementAction: "terminate",
|
|
335
|
+
enforcementReason: `${enforceResult.reason} (backtrack failed: no materialized restore point within ${maxDepth} rounds)`,
|
|
336
|
+
rejectionCheck: enforceResult.check,
|
|
337
|
+
agentSuccessClaim,
|
|
338
|
+
gateContradicted,
|
|
339
|
+
newConsecutiveRejections: 0,
|
|
340
|
+
shouldPushSuccessTrajectory: false,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const targetRef = resolveRef(restorePoint.round);
|
|
344
|
+
const targetManifest = targetRef && input.resolveCheckpointManifest
|
|
345
|
+
? input.resolveCheckpointManifest(targetRef)
|
|
346
|
+
: null;
|
|
347
|
+
if (!targetRef || !targetManifest) {
|
|
347
348
|
logEvent("session_end", {
|
|
348
349
|
loopId,
|
|
349
350
|
stopReason: "enforcement_terminated",
|
|
350
351
|
round: currentRound,
|
|
351
352
|
});
|
|
352
|
-
return {
|
|
353
|
+
return evaluationResult({
|
|
353
354
|
action: "terminate",
|
|
354
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
355
355
|
stopReason: "enforcement_terminated",
|
|
356
356
|
verificationFlags,
|
|
357
357
|
enforcementAction: "terminate",
|
|
358
|
-
enforcementReason: `${enforceResult.reason} (backtrack failed:
|
|
358
|
+
enforcementReason: `${enforceResult.reason} (backtrack failed: restore point manifest unavailable)`,
|
|
359
359
|
rejectionCheck: enforceResult.check,
|
|
360
|
-
|
|
360
|
+
agentSuccessClaim,
|
|
361
361
|
gateContradicted,
|
|
362
362
|
newConsecutiveRejections: 0,
|
|
363
|
-
newLastSelfEval: selfEval,
|
|
364
363
|
shouldPushSuccessTrajectory: false,
|
|
365
|
-
};
|
|
364
|
+
});
|
|
366
365
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
366
|
+
const abandonedRounds = committedRounds
|
|
367
|
+
.filter((view) => view.round >= targetRef.redoRound);
|
|
368
|
+
const recovery = deriveRecoveryDirective({
|
|
369
|
+
origin: "machine",
|
|
370
|
+
reason: enforceResult.reason,
|
|
371
|
+
...(enforceResult.check ? { triggerRule: enforceResult.check } : {}),
|
|
372
|
+
// The transaction layer fills the directive transaction id, which
|
|
373
|
+
// depends on the prompt hash/branch identity.
|
|
374
|
+
transactionId: "",
|
|
375
|
+
targetRef,
|
|
376
|
+
targetManifest,
|
|
377
|
+
abandonedRounds,
|
|
378
|
+
currentAfterEvidence: input.afterEvidence ?? [],
|
|
379
|
+
triggerEvaluation: selfEval,
|
|
380
|
+
triggerRound: currentRound,
|
|
381
|
+
triggerFindings: verificationFlags,
|
|
376
382
|
});
|
|
377
|
-
// v2.12: Capture the restore point's machine snapshot — the commit the
|
|
378
|
-
// workspace must return to and what was already dirty there — so the
|
|
379
|
-
// next round's verification can confirm the workspace returned to this
|
|
380
|
-
// point without reading unrelated work in progress as a failed restore.
|
|
381
|
-
const targetSnapshot = findBacktrackTargetSnapshot(restorePoint.round, vaultEntries);
|
|
382
|
-
const targetGitHead = targetSnapshot?.head ?? null;
|
|
383
383
|
logEvent("enforcement_backtrack", {
|
|
384
384
|
loopId,
|
|
385
385
|
round: currentRound,
|
|
386
386
|
reason: enforceResult.reason.slice(0, 120),
|
|
387
387
|
check: enforceResult.check ?? "",
|
|
388
|
-
backtrackTarget:
|
|
389
|
-
skippedDiscoveries:
|
|
388
|
+
backtrackTarget: targetRef.redoRound,
|
|
389
|
+
skippedDiscoveries: recovery.skipped_discoveries.length,
|
|
390
390
|
});
|
|
391
|
-
return {
|
|
391
|
+
return rollbackResult({
|
|
392
392
|
action: "backtrack",
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
? restorePoint.skippedDiscoveries
|
|
397
|
-
: [],
|
|
398
|
-
backtrackSkippedFiles: skippedFiles,
|
|
399
|
-
backtrackSkippedFingerprints: skippedFingerprints,
|
|
400
|
-
backtrackTargetGitHead: targetGitHead ?? undefined,
|
|
401
|
-
backtrackTargetDirty: targetSnapshot?.dirtyFiles,
|
|
402
|
-
backtrackTriggerRule: enforceResult.check,
|
|
403
|
-
backtrackFailedRounds: failedRounds,
|
|
404
|
-
backtrackApproaches: approaches,
|
|
405
|
-
backtrackWrongAssumptions: wrongAssumptions,
|
|
393
|
+
origin: "machine",
|
|
394
|
+
reason: enforceResult.reason,
|
|
395
|
+
recovery,
|
|
406
396
|
verificationFlags,
|
|
407
397
|
enforcementAction: "backtrack",
|
|
408
398
|
enforcementReason: enforceResult.reason,
|
|
409
399
|
rejectionCheck: enforceResult.check,
|
|
410
|
-
|
|
400
|
+
agentSuccessClaim,
|
|
411
401
|
gateContradicted,
|
|
412
402
|
newConsecutiveRejections: 0, // reset — fresh start
|
|
413
|
-
newLastSelfEval: undefined,
|
|
414
403
|
shouldPushSuccessTrajectory: false,
|
|
415
|
-
};
|
|
404
|
+
});
|
|
416
405
|
}
|
|
417
406
|
// Accept: reset rejection counter
|
|
418
407
|
// (consecutiveRejections reset to 0 — caller persists)
|
|
@@ -432,24 +421,24 @@ export class RoundCoordinator {
|
|
|
432
421
|
// the declaration arm below already owns that posture.
|
|
433
422
|
const contractClosed = activeContractStatuses.contractId === "" ||
|
|
434
423
|
activeContractStatuses.closure === "verified";
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
//
|
|
441
|
-
// contradicted
|
|
442
|
-
//
|
|
443
|
-
// before this point.
|
|
424
|
+
// v4.0.6: the stop mapping reads ONE outcome field.
|
|
425
|
+
// blocked → blocked
|
|
426
|
+
// success + contract closed → completed
|
|
427
|
+
// success + open contract → incomplete
|
|
428
|
+
// partial → incomplete
|
|
429
|
+
// failed → failed
|
|
430
|
+
// A contradicted item also never reaches `completed` (closure is
|
|
431
|
+
// `verified` only when EVERY item is); the reject/terminate path for it
|
|
432
|
+
// is the enforcement gate's, evaluated before this point.
|
|
444
433
|
const declaredBlocked = declaresBlocked(selfEval);
|
|
445
434
|
let reason;
|
|
446
435
|
if (declaredBlocked) {
|
|
447
436
|
reason = "blocked";
|
|
448
437
|
}
|
|
449
|
-
else if (
|
|
438
|
+
else if (agentOutcome === "success" && contractClosed) {
|
|
450
439
|
reason = "completed";
|
|
451
440
|
}
|
|
452
|
-
else if (
|
|
441
|
+
else if (agentOutcome === "success" || agentOutcome === "partial") {
|
|
453
442
|
reason = "incomplete";
|
|
454
443
|
}
|
|
455
444
|
else {
|
|
@@ -459,50 +448,44 @@ export class RoundCoordinator {
|
|
|
459
448
|
// is written into the committed round document below, so it must be
|
|
460
449
|
// truthful at the moment it becomes a fact, not only when it is
|
|
461
450
|
// returned to the client.
|
|
462
|
-
const finalized = finalizeStopReason(reason, selfEval, activeContractStatuses);
|
|
463
|
-
return {
|
|
451
|
+
const finalized = finalizeStopReason(reason, selfEval, activeContractStatuses, selfEval.round_contract?.supersedes ?? null);
|
|
452
|
+
return evaluationResult({
|
|
464
453
|
action: "stop",
|
|
465
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
466
454
|
stopReason: finalized.reason,
|
|
467
455
|
verificationFlags: finalized.flags.length > 0
|
|
468
456
|
? [...verificationFlags, ...finalized.flags]
|
|
469
457
|
: verificationFlags,
|
|
470
|
-
|
|
458
|
+
agentSuccessClaim,
|
|
471
459
|
gateContradicted,
|
|
472
460
|
newConsecutiveRejections: 0,
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
};
|
|
461
|
+
shouldPushSuccessTrajectory: shouldPushSuccess(selfEval, gateContradicted, verificationFlags, verifyResult.machineBackedSuccess, activeContractStatuses),
|
|
462
|
+
});
|
|
476
463
|
}
|
|
477
464
|
// 5c. Max rounds reached
|
|
478
465
|
//
|
|
479
466
|
// Progress stalls are handled before this point by the R4 delta tier and
|
|
480
467
|
// R5 flatline tier. Max rounds is an independent hard boundary.
|
|
481
468
|
if (currentRound >= maxRounds) {
|
|
482
|
-
return {
|
|
469
|
+
return evaluationResult({
|
|
483
470
|
action: "stop",
|
|
484
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
485
471
|
stopReason: "max_rounds",
|
|
486
472
|
verificationFlags,
|
|
487
|
-
|
|
473
|
+
agentSuccessClaim,
|
|
488
474
|
gateContradicted,
|
|
489
475
|
newConsecutiveRejections: 0,
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
};
|
|
476
|
+
shouldPushSuccessTrajectory: shouldPushSuccess(selfEval, gateContradicted, verificationFlags, verifyResult.machineBackedSuccess, activeContractStatuses),
|
|
477
|
+
});
|
|
493
478
|
}
|
|
494
479
|
// ── 6. Continue — caller compiles next round ────────────────────────
|
|
495
|
-
return {
|
|
480
|
+
return evaluationResult({
|
|
496
481
|
action: "continue",
|
|
497
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
498
482
|
verificationFlags,
|
|
499
483
|
enforcementAction: "accept",
|
|
500
|
-
|
|
484
|
+
agentSuccessClaim,
|
|
501
485
|
gateContradicted,
|
|
502
486
|
newConsecutiveRejections: 0,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
};
|
|
487
|
+
shouldPushSuccessTrajectory: shouldPushSuccess(selfEval, gateContradicted, verificationFlags, verifyResult.machineBackedSuccess, activeContractStatuses),
|
|
488
|
+
});
|
|
506
489
|
}
|
|
507
490
|
}
|
|
508
491
|
//# sourceMappingURL=round-coordinator.js.map
|