loopforge 3.9.2 → 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 +152 -40
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +382 -206
- 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 +105 -141
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +249 -372
- 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 +32 -67
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +203 -439
- 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 +28 -887
- 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 +167 -27
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +373 -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 +51 -19
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +423 -507
- 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 +285 -81
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +1629 -574
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +61 -19
- 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 +963 -271
- 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 -82
- 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 +763 -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 +15 -40
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +13 -13
- 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 +28 -9
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +592 -603
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-coverage.d.ts +39 -0
- package/dist/prompt-coverage.d.ts.map +1 -0
- package/dist/prompt-coverage.js +80 -0
- package/dist/prompt-coverage.js.map +1 -0
- package/dist/prompt-freshness.d.ts +25 -0
- package/dist/prompt-freshness.d.ts.map +1 -0
- package/dist/prompt-freshness.js +88 -0
- package/dist/prompt-freshness.js.map +1 -0
- package/dist/prompt-policy.d.ts +14 -4
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +29 -10
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +875 -419
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +73 -99
- 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 +45 -91
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +254 -271
- 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 +54 -157
- 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 +856 -39
- package/dist/round-transaction.js.map +1 -1
- package/dist/self-eval.d.ts +40 -60
- package/dist/self-eval.d.ts.map +1 -1
- package/dist/self-eval.js +455 -384
- 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 +43 -21
- package/dist/submission-guidance.d.ts.map +1 -1
- package/dist/submission-guidance.js +368 -262
- 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 +146 -51
- package/dist/submission-receipt.js.map +1 -1
- package/dist/token-utils.d.ts +47 -7
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +66 -15
- 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 +13 -0
- package/dist/verification-actions.d.ts.map +1 -0
- package/dist/verification-actions.js +44 -0
- package/dist/verification-actions.js.map +1 -0
- package/dist/verification-gate.d.ts +26 -46
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +218 -427
- 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 +5 -19
- package/package.json +2 -2
- 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 -370
|
@@ -17,27 +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 {
|
|
27
|
-
import { enforceRound, buildRejectionPrompt, findSafeRestorePoint, buildBacktrackPrompt, findBacktrackTargetSnapshot, } from "./enforcement-gate.js";
|
|
24
|
+
import { declaresBlocked, effectiveOutcome, effectiveSuccess, } from "./round-outcome.js";
|
|
25
|
+
import { deriveRoundProgress, } from "./progress.js";
|
|
26
|
+
import { enforceRound, buildRejectionPrompt, findSafeRestorePoint, } from "./enforcement-gate.js";
|
|
28
27
|
import { logEvent } from "./observability.js";
|
|
29
28
|
import { getPolicy } from "./policy.js";
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
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;
|
|
39
46
|
if (gateContradicted)
|
|
40
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;
|
|
41
51
|
// v3.8: an unverified contract item (or a claimed-but-unbacked success)
|
|
42
52
|
// keeps the round out of the success trajectory — an accepted-but-unverified
|
|
43
53
|
// round is not evidence of progress.
|
|
@@ -69,120 +79,48 @@ function shouldPushSuccess(gateContradicted, verificationFlags, contractStatuses
|
|
|
69
79
|
export function finalizeStopReason(
|
|
70
80
|
/** `undefined` when the disposition named no terminal reason at all (the
|
|
71
81
|
* callers fall back to their own default) — there is nothing to guard. */
|
|
72
|
-
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) {
|
|
73
86
|
if (reason !== "completed")
|
|
74
87
|
return { reason, flags: [] };
|
|
75
88
|
const declared = declaresBlocked(selfEval);
|
|
76
89
|
const blockedContract = contractStatuses?.closure === "blocked";
|
|
77
|
-
if (
|
|
78
|
-
return {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
// ── RoundCoordinator ───────────────────────────────────────────────────────
|
|
95
|
-
/** v3.8.1: The rolled-back branch's facts — the ONE derivation of which rounds
|
|
96
|
-
* failed, which approaches must not be repeated, which assumptions were
|
|
97
|
-
* falsified, and which files (with their failed-round git fingerprints) the
|
|
98
|
-
* agent must restore.
|
|
99
|
-
*
|
|
100
|
-
* Sources are COMMITTED rounds above the restore point; rejected attempts are
|
|
101
|
-
* not durable history and never become a source. The in-flight attempt that
|
|
102
|
-
* triggered the rollback is the one exception — its evaluation lives in this
|
|
103
|
-
* process, not in committed history.
|
|
104
|
-
*
|
|
105
|
-
* Extracted from the coordinator's inline walk so it is unit-testable without
|
|
106
|
-
* driving a whole rollback, and so the prompt, the state file and the
|
|
107
|
-
* committed decision all describe one event. */
|
|
108
|
-
export function deriveBacktrackRecoveryFacts(input) {
|
|
109
|
-
const { currentRound, restoreRound, vaultEntries, selfEval } = input;
|
|
110
|
-
// v2.13: files changed in skipped rounds, for the restore prompt.
|
|
111
|
-
const skippedFiles = [];
|
|
112
|
-
// M3 (v3.7.x): each skipped file's git fingerprint at its failed round.
|
|
113
|
-
// Recorded from the round's own after-evidence (machine data, not the
|
|
114
|
-
// agent's report); later rounds overwrite earlier ones so the map holds the
|
|
115
|
-
// LAST failed state of every file.
|
|
116
|
-
const skippedFingerprints = {};
|
|
117
|
-
const failedRounds = [];
|
|
118
|
-
const approaches = [];
|
|
119
|
-
const wrongAssumptions = [];
|
|
120
|
-
for (let r = restoreRound + 1; r < currentRound; r++) {
|
|
121
|
-
// v3.2.1: match the feedback entry (execution_report lives there) — the
|
|
122
|
-
// compile-time lineage entry for the same round precedes it in the flat
|
|
123
|
-
// view and carries no evidence.
|
|
124
|
-
const entry = vaultEntries.find((e) => entryRound(e) === r && e.task_type !== "loop_lineage");
|
|
125
|
-
const view = entry ? decodeCommittedRound(entry) : null;
|
|
126
|
-
const evaluation = view?.evaluation ?? null;
|
|
127
|
-
if (entry?.execution_report) {
|
|
128
|
-
const ev = entry.execution_report;
|
|
129
|
-
const reported = Array.isArray(ev.files_changed)
|
|
130
|
-
? ev.files_changed.filter((f) => typeof f === "string")
|
|
131
|
-
: [];
|
|
132
|
-
// v3.9.1: the machine delta is the primary set of files the failed
|
|
133
|
-
// round changed; the agent report is unioned in so an ignored file the
|
|
134
|
-
// agent named is not lost from the recovery brief.
|
|
135
|
-
const machineFiles = (view?.observationDelta ?? [])
|
|
136
|
-
.filter((delta) => delta.kind === "git" && delta.changedFiles !== null)
|
|
137
|
-
.flatMap((delta) => delta.changedFiles);
|
|
138
|
-
const files = [...new Set([...machineFiles, ...reported])];
|
|
139
|
-
const git = view?.afterEvidence.find((snapshot) => snapshot.kind === "git" &&
|
|
140
|
-
snapshot.status === "observed" &&
|
|
141
|
-
isRecord(snapshot.data.fingerprints));
|
|
142
|
-
const gitFingerprints = git && git.kind === "git"
|
|
143
|
-
? git.data.fingerprints
|
|
144
|
-
: null;
|
|
145
|
-
for (const f of files) {
|
|
146
|
-
if (!skippedFiles.includes(f))
|
|
147
|
-
skippedFiles.push(f);
|
|
148
|
-
const fp = gitFingerprints?.[f];
|
|
149
|
-
if (typeof fp === "string")
|
|
150
|
-
skippedFingerprints[f] = fp;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const summary = evaluation?.output_summary ?? "";
|
|
154
|
-
if (summary.trim().length > 0) {
|
|
155
|
-
failedRounds.push(r);
|
|
156
|
-
approaches.push(summary.trim().slice(0, 200));
|
|
157
|
-
}
|
|
158
|
-
const assumptions = (evaluation?.wrong_assumptions ?? [])
|
|
159
|
-
.filter((v) => typeof v === "string")
|
|
160
|
-
.map((s) => s.slice(0, 500));
|
|
161
|
-
for (const a of assumptions) {
|
|
162
|
-
if (!wrongAssumptions.includes(a))
|
|
163
|
-
wrongAssumptions.push(a);
|
|
164
|
-
}
|
|
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
|
+
};
|
|
165
105
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
+
};
|
|
177
120
|
}
|
|
178
|
-
return {
|
|
179
|
-
skippedFiles,
|
|
180
|
-
skippedFingerprints,
|
|
181
|
-
failedRounds,
|
|
182
|
-
approaches,
|
|
183
|
-
wrongAssumptions,
|
|
184
|
-
};
|
|
121
|
+
return { reason, flags: [] };
|
|
185
122
|
}
|
|
123
|
+
// ── RoundCoordinator ───────────────────────────────────────────────────────
|
|
186
124
|
export class RoundCoordinator {
|
|
187
125
|
store;
|
|
188
126
|
constructor(store) {
|
|
@@ -199,72 +137,97 @@ export class RoundCoordinator {
|
|
|
199
137
|
* - Transactional feedback commit (accepted rounds only)
|
|
200
138
|
* - State file I/O (both paths, after compiling) */
|
|
201
139
|
processRound(input) {
|
|
202
|
-
const { loopId, task, currentRound, maxRounds, selfEval,
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
// the
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
?
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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;
|
|
216
158
|
// ── 2. Verification gate ────────────────────────────────────────────
|
|
217
|
-
|
|
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 ?? [],
|
|
218
179
|
// v3.7.1: gate records live under the gate: prefix — outside the round
|
|
219
180
|
// entries above — and are passed separately to the gate check.
|
|
220
|
-
|
|
221
|
-
? queryLoopEntries(this.store, loopId, { prefix: `loop:${loopId}:gate:` })
|
|
222
|
-
: [],
|
|
181
|
+
history.entries.filter((entry) => String(entry.task_id ?? "").startsWith(`loop:${loopId}:gate:`)),
|
|
223
182
|
// v3.8.3: the absolute arm of the entrypoint check. Derived here, not in
|
|
224
183
|
// the gate, because it needs the filesystem and the in-flight trust map
|
|
225
184
|
// captured when this round was prepared — the gate stays a pure function
|
|
226
185
|
// of its inputs.
|
|
227
|
-
driftedEntrypoints(input.entrypointTrust),
|
|
228
|
-
// v3.9: the
|
|
229
|
-
//
|
|
230
|
-
input.
|
|
186
|
+
driftedEntrypoints(input.entrypointTrust),
|
|
187
|
+
// v3.9.4: the SAME manifest basis the receipt uses. An omitted
|
|
188
|
+
// files_changed field is not silently treated as "a report exists".
|
|
189
|
+
input.submittedFields
|
|
190
|
+
? input.submittedFields.includes("execution_report.files_changed")
|
|
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);
|
|
231
195
|
const verificationFlags = verifyResult.flags;
|
|
232
196
|
const gateContradicted = verifyResult.verdict === "contradicted";
|
|
233
|
-
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
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({
|
|
241
209
|
round: currentRound,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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,
|
|
246
230
|
});
|
|
247
|
-
// v3.8.3: the legal exit from scope drift — a round that declares itself
|
|
248
|
-
// blocked may switch contracts, but only by declaring a proposal whose
|
|
249
|
-
// scope covers EVERY file it changed out of scope. Computed here because
|
|
250
|
-
// this is the only place where the round's git delta, the active
|
|
251
|
-
// contract's scope and the submitted proposal are all in reach; the
|
|
252
|
-
// enforcement gate receives the verdict, not the inputs.
|
|
253
|
-
//
|
|
254
|
-
// `selfEval.round_contract` is already the parsed proposal (buildSelfEvaluation
|
|
255
|
-
// runs parseRoundContract), so the coverage test reads the same object the
|
|
256
|
-
// commit path will read back.
|
|
257
|
-
//
|
|
258
|
-
// The coverage requirement is what keeps this from being a universal
|
|
259
|
-
// escape: without it "declare blocked" would dissolve a machine fact
|
|
260
|
-
// without declaring anything. The drift FLAG is untouched — it still lands
|
|
261
|
-
// on the committed round, so audit/explain keep reporting it.
|
|
262
|
-
const driftedFiles = roundDriftFiles(activeContract, evidenceDeltas ?? []);
|
|
263
|
-
const proposal = selfEval.round_contract;
|
|
264
|
-
const scopeDriftWaiver = driftedFiles !== null && driftedFiles.length > 0 &&
|
|
265
|
-
declaresBlocked(selfEval) &&
|
|
266
|
-
proposal !== undefined &&
|
|
267
|
-
collectOutOfScopeFiles(driftedFiles, proposal.scope).length === 0;
|
|
268
231
|
if (gateContradicted) {
|
|
269
232
|
logEvent("gate_contradicted", {
|
|
270
233
|
loopId,
|
|
@@ -278,11 +241,11 @@ export class RoundCoordinator {
|
|
|
278
241
|
// transaction reaches enforcement with a fully extracted self-evaluation.
|
|
279
242
|
// The old v2.5 "heuristic partial enforcement" plumbing (extractionSucceeded
|
|
280
243
|
// flag, enforcement skipEvidenceRules mode) was removed as unreachable.
|
|
281
|
-
const enforceResult = enforceRound(selfEval, verifyResult, currentRound, vaultEntries, consecutiveRejections, input.lastRejectionCheck ?? "",
|
|
282
|
-
// v3.9: what THIS round moved,
|
|
283
|
-
// committed
|
|
284
|
-
//
|
|
285
|
-
|
|
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 ?? []));
|
|
286
249
|
if (enforceResult.action === "reject") {
|
|
287
250
|
const newRejections = consecutiveRejections + 1;
|
|
288
251
|
const rejectionPrompt = buildRejectionPrompt(currentRound, task, enforceResult, verificationFlags);
|
|
@@ -293,21 +256,18 @@ export class RoundCoordinator {
|
|
|
293
256
|
check: enforceResult.check ?? "",
|
|
294
257
|
consecutiveRejections: newRejections,
|
|
295
258
|
});
|
|
296
|
-
return {
|
|
259
|
+
return evaluationResult({
|
|
297
260
|
action: "reject",
|
|
298
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
299
261
|
rejectionPrompt,
|
|
300
262
|
verificationFlags,
|
|
301
263
|
enforcementAction: "reject",
|
|
302
264
|
enforcementReason: enforceResult.reason,
|
|
303
265
|
rejectionCheck: enforceResult.check,
|
|
304
|
-
|
|
266
|
+
agentSuccessClaim,
|
|
305
267
|
gateContradicted,
|
|
306
268
|
newConsecutiveRejections: newRejections,
|
|
307
|
-
// Don't update lastSelfEval on reject — the agent redoes the same round
|
|
308
|
-
newLastSelfEval: undefined,
|
|
309
269
|
shouldPushSuccessTrajectory: false,
|
|
310
|
-
};
|
|
270
|
+
});
|
|
311
271
|
}
|
|
312
272
|
if (enforceResult.action === "terminate") {
|
|
313
273
|
// v3.8: a row may name the stop reason it terminates with (the
|
|
@@ -319,100 +279,129 @@ export class RoundCoordinator {
|
|
|
319
279
|
stopReason: terminateReason,
|
|
320
280
|
round: currentRound,
|
|
321
281
|
});
|
|
322
|
-
return {
|
|
282
|
+
return evaluationResult({
|
|
323
283
|
action: "terminate",
|
|
324
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
325
284
|
stopReason: terminateReason,
|
|
326
285
|
verificationFlags,
|
|
327
286
|
enforcementAction: "terminate",
|
|
328
287
|
enforcementReason: enforceResult.reason,
|
|
329
288
|
rejectionCheck: enforceResult.check,
|
|
330
|
-
|
|
289
|
+
agentSuccessClaim,
|
|
331
290
|
gateContradicted,
|
|
332
291
|
newConsecutiveRejections: 0,
|
|
333
|
-
newLastSelfEval: selfEval,
|
|
334
292
|
shouldPushSuccessTrajectory: false,
|
|
335
|
-
};
|
|
293
|
+
});
|
|
336
294
|
}
|
|
337
|
-
// ── v2.10: Backtrack
|
|
295
|
+
// ── v2.10 / v3.9.7: Backtrack ─────────────────────────────────────────
|
|
338
296
|
if (enforceResult.action === "backtrack") {
|
|
339
297
|
const maxDepth = getPolicy().engine.backtrack_max_depth;
|
|
340
|
-
|
|
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);
|
|
341
323
|
if (!restorePoint) {
|
|
342
|
-
// 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) {
|
|
343
348
|
logEvent("session_end", {
|
|
344
349
|
loopId,
|
|
345
350
|
stopReason: "enforcement_terminated",
|
|
346
351
|
round: currentRound,
|
|
347
352
|
});
|
|
348
|
-
return {
|
|
353
|
+
return evaluationResult({
|
|
349
354
|
action: "terminate",
|
|
350
|
-
verificationStatus: roundVerificationStatus(activeContractStatuses, selfEval.execution_report ?? null),
|
|
351
355
|
stopReason: "enforcement_terminated",
|
|
352
356
|
verificationFlags,
|
|
353
357
|
enforcementAction: "terminate",
|
|
354
|
-
enforcementReason: `${enforceResult.reason} (backtrack failed:
|
|
358
|
+
enforcementReason: `${enforceResult.reason} (backtrack failed: restore point manifest unavailable)`,
|
|
355
359
|
rejectionCheck: enforceResult.check,
|
|
356
|
-
|
|
360
|
+
agentSuccessClaim,
|
|
357
361
|
gateContradicted,
|
|
358
362
|
newConsecutiveRejections: 0,
|
|
359
|
-
newLastSelfEval: selfEval,
|
|
360
363
|
shouldPushSuccessTrajectory: false,
|
|
361
|
-
};
|
|
364
|
+
});
|
|
362
365
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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,
|
|
372
382
|
});
|
|
373
|
-
// v2.12: Capture the restore point's machine snapshot — the commit the
|
|
374
|
-
// workspace must return to and what was already dirty there — so the
|
|
375
|
-
// next round's verification can confirm the workspace returned to this
|
|
376
|
-
// point without reading unrelated work in progress as a failed restore.
|
|
377
|
-
const targetSnapshot = findBacktrackTargetSnapshot(restorePoint.round, vaultEntries);
|
|
378
|
-
const targetGitHead = targetSnapshot?.head ?? null;
|
|
379
|
-
const backtrackPrompt = buildBacktrackPrompt(currentRound, restorePoint.round, enforceResult.check ?? "progress_stall", getPolicy().engine.backtrack_preserve_discoveries
|
|
380
|
-
? restorePoint.skippedDiscoveries
|
|
381
|
-
: [], skippedFiles, targetGitHead ?? undefined, makeRoundId(loopId, restorePoint.round + 1), { failedRounds, approaches, wrongAssumptions });
|
|
382
383
|
logEvent("enforcement_backtrack", {
|
|
383
384
|
loopId,
|
|
384
385
|
round: currentRound,
|
|
385
386
|
reason: enforceResult.reason.slice(0, 120),
|
|
386
387
|
check: enforceResult.check ?? "",
|
|
387
|
-
backtrackTarget:
|
|
388
|
-
skippedDiscoveries:
|
|
388
|
+
backtrackTarget: targetRef.redoRound,
|
|
389
|
+
skippedDiscoveries: recovery.skipped_discoveries.length,
|
|
389
390
|
});
|
|
390
|
-
return {
|
|
391
|
+
return rollbackResult({
|
|
391
392
|
action: "backtrack",
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
backtrackSkippedDiscoveries: getPolicy().engine.backtrack_preserve_discoveries
|
|
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
|