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
package/dist/canonical-state.js
CHANGED
|
@@ -8,7 +8,29 @@ import { deriveCriterionId, deriveItemId, normalizeText, STABLE_ID_RE, unique, u
|
|
|
8
8
|
import { claimedMetCriteria, claimedRemainingCriteria } from "./self-eval.js";
|
|
9
9
|
import { getPolicy } from "./policy.js";
|
|
10
10
|
import { deriveConfiguredCapability } from "./policy.js";
|
|
11
|
-
|
|
11
|
+
import { formatWorkingMemoryState, hasWorkingMemoryContent, } from "./working-memory.js";
|
|
12
|
+
import { isOpenQuestionId } from "./open-question.js";
|
|
13
|
+
import { MACHINE_EVIDENCE_AUTHORITY, formatEvidencePostureLines, } from "./evidence-posture-view.js";
|
|
14
|
+
import { formatContextBoundaryLabel } from "./round-guidance.js";
|
|
15
|
+
/** v4.0.3: schema 7 adds the previous/retry evidence posture, explicitly split
|
|
16
|
+
* reported progress/issues/warnings, compile context and Working Memory
|
|
17
|
+
* source counts. v4.0.4: schema 8 records the renamed context vocabulary
|
|
18
|
+
* (`fresh_invocation`) in the derived declaration and stateHash. v4.0.5:
|
|
19
|
+
* schema 9 adds the optional `currentUserInstruction` projection; schema 10
|
|
20
|
+
* (v4.0.6) makes the objective the WORKING objective, falls back to it for
|
|
21
|
+
* the Current Task, and removes the Agent-reported test counts. The state
|
|
22
|
+
* file is regenerated every round; an older file is a stale view, not a
|
|
23
|
+
* legacy document to decode. */
|
|
24
|
+
export const CANONICAL_STATE_SCHEMA_VERSION = 10;
|
|
25
|
+
/** v4.0.5: the ONE wording for "the host supplied no instruction". Prompt and
|
|
26
|
+
* state file both render it; neither infers an instruction from the old task
|
|
27
|
+
* or transcript. */
|
|
28
|
+
export const CURRENT_USER_INSTRUCTION_NOT_PROVIDED = "not provided for this invocation (the Objective and Current Task remain operative).";
|
|
29
|
+
/** v4.0.5: the protected section's fixed authority note. The instruction is
|
|
30
|
+
* invocation input; it never edits the loop's objective or acceptance. */
|
|
31
|
+
export const CURRENT_USER_INSTRUCTION_AUTHORITY = "Host-provided current user instruction for this invocation. It does not " +
|
|
32
|
+
"rewrite the Objective, Success Criteria, hard constraints, or the Round " +
|
|
33
|
+
"Contract; a changed objective or acceptance requires a new loop.";
|
|
12
34
|
/** v3.7.1: Presentation view of sub-goals shared by prompts and the state
|
|
13
35
|
* file (one derivation, no second copy). Only ACTIVE items render as rows
|
|
14
36
|
* — pending/in_progress/blocked — ordered blocked → in_progress → pending
|
|
@@ -73,10 +95,12 @@ export function formatCriterionRows(statuses, successCriteria) {
|
|
|
73
95
|
continue;
|
|
74
96
|
seen.add(id);
|
|
75
97
|
ids.push(id);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
lines.push(`- ${criterionIcon(status?.status ?? "unknown")} [\`${id}\`] ${text}`);
|
|
99
|
+
}
|
|
100
|
+
if (lines.length > 0) {
|
|
101
|
+
// v4.0.3: the icons are meaningless without their provenance.
|
|
102
|
+
lines.push("> ✅ machine verified · 🟡 Agent claim · 🟠 insufficient · " +
|
|
103
|
+
"⛔ contradicted · ⬜ Agent-reported remaining · ❔ unknown");
|
|
80
104
|
}
|
|
81
105
|
return { lines, ids };
|
|
82
106
|
}
|
|
@@ -85,8 +109,9 @@ export function formatCriterionRows(statuses, successCriteria) {
|
|
|
85
109
|
* the overflow line lists every remaining `sg-*`. */
|
|
86
110
|
export function formatActiveSubGoals(subGoals, round, cap = getPolicy().evolution.max_active_subgoals) {
|
|
87
111
|
const view = activeSubGoalView([...subGoals], Number.POSITIVE_INFINITY);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
112
|
+
const visible = view.active;
|
|
113
|
+
const shown = visible.slice(0, Math.max(0, cap));
|
|
114
|
+
const hidden = visible.slice(shown.length);
|
|
90
115
|
const lines = [];
|
|
91
116
|
for (const sg of shown) {
|
|
92
117
|
const statusIcon = sg.status === "in_progress" ? "🔄" :
|
|
@@ -105,7 +130,19 @@ export function formatActiveSubGoals(subGoals, round, cap = getPolicy().evolutio
|
|
|
105
130
|
}
|
|
106
131
|
lines.push(`> ${view.total} total · ${view.activeTotal} active · ` +
|
|
107
132
|
`${view.done} done · ${view.canceled} canceled`);
|
|
108
|
-
return { lines, ids:
|
|
133
|
+
return { lines, ids: visible.map((sg) => sg.id) };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The active contract is the Current Task only when the runtime has a
|
|
137
|
+
* machine-verifiable contract capability and this invocation did not provide
|
|
138
|
+
* an explicit instruction. The contract remains a separately rendered
|
|
139
|
+
* committed fact in every other case; this helper keeps prompt and state-file
|
|
140
|
+
* projections on the same rule as state construction.
|
|
141
|
+
*/
|
|
142
|
+
export function contractDrivesCurrentTask(state) {
|
|
143
|
+
return state.roundContract !== undefined &&
|
|
144
|
+
state.currentUserInstruction === undefined &&
|
|
145
|
+
state.capability.contractVerificationAvailable;
|
|
109
146
|
}
|
|
110
147
|
// unique() imported from token-utils.ts
|
|
111
148
|
/** Deterministic JSON serialization used by state and prompt hashes. */
|
|
@@ -178,132 +215,132 @@ function addListWithIds(lines, title, values, prefix) {
|
|
|
178
215
|
function addListWithIdentities(lines, title, values, metadata) {
|
|
179
216
|
if (values.length === 0)
|
|
180
217
|
return;
|
|
181
|
-
const
|
|
218
|
+
const metaByText = new Map(metadata.map((meta) => [meta.text, meta]));
|
|
182
219
|
lines.push(`## ${title}`, "");
|
|
183
220
|
for (const value of values) {
|
|
184
221
|
const trimmed = value.trim();
|
|
185
222
|
if (!trimmed)
|
|
186
223
|
continue;
|
|
224
|
+
const meta = metaByText.get(trimmed);
|
|
187
225
|
if (STABLE_ID_RE.test(trimmed)) {
|
|
188
226
|
lines.push(`- [\`${trimmed}\`] ${trimmed}`);
|
|
189
227
|
}
|
|
190
228
|
else {
|
|
191
|
-
const id =
|
|
192
|
-
|
|
229
|
+
const id = meta?.id ?? `c-${deriveItemId(trimmed)}`;
|
|
230
|
+
const provenance = constraintProvenanceLabel(meta);
|
|
231
|
+
lines.push(`- [\`${id}\`] ${trimmed}${provenance}`);
|
|
193
232
|
}
|
|
194
233
|
}
|
|
195
234
|
lines.push("");
|
|
196
235
|
}
|
|
197
|
-
/** v3.
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
* in full by the sections that own them. Returns "" when no milestone exists,
|
|
203
|
-
* so the caller renders no section at all — bare position is already in the
|
|
204
|
-
* prompt header.
|
|
205
|
-
*
|
|
206
|
-
* Shared by the state-file and prompt renderers so they cannot drift apart
|
|
207
|
-
* (module contract above). Presentation only; never feeds enforcement. */
|
|
208
|
-
export function buildPhaseLine(state) {
|
|
209
|
-
const lastMilestone = state.milestones.length > 0
|
|
210
|
-
? state.milestones[state.milestones.length - 1]
|
|
211
|
-
: null;
|
|
212
|
-
// No phase boundary yet → no phase to report. Position alone ("round 3/200")
|
|
213
|
-
// is already in the prompt header, so rendering it again would be noise.
|
|
214
|
-
if (!lastMilestone)
|
|
236
|
+
/** v3.9.7: the ONE constraint provenance label shared by the state file and
|
|
237
|
+
* the prompt. A discovered/preserved constraint is an Agent report, never a
|
|
238
|
+
* machine-verified fact. */
|
|
239
|
+
export function constraintProvenanceLabel(meta) {
|
|
240
|
+
if (!meta)
|
|
215
241
|
return "";
|
|
216
|
-
const
|
|
217
|
-
?
|
|
218
|
-
:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
242
|
+
const round = meta.source_round !== undefined && meta.source_round >= 1
|
|
243
|
+
? ` R${meta.source_round}`
|
|
244
|
+
: "";
|
|
245
|
+
switch (meta.source) {
|
|
246
|
+
case "discovered":
|
|
247
|
+
return ` (Agent-declared${round}; report, not machine-verified)`;
|
|
248
|
+
case "preserved":
|
|
249
|
+
return ` (Agent discovery preserved by rollback${round}; report)`;
|
|
250
|
+
case "plan":
|
|
251
|
+
return ` (plan/carried${round})`;
|
|
252
|
+
case "unrecorded":
|
|
253
|
+
return " (origin not recorded in committed rounds)";
|
|
254
|
+
default:
|
|
255
|
+
return "";
|
|
225
256
|
}
|
|
226
|
-
return parts.join(" · ");
|
|
227
257
|
}
|
|
228
|
-
|
|
229
|
-
* body — the single formatting source shared by prompts and the state
|
|
230
|
-
* file (module contract above). The original objective is NOT here: it
|
|
231
|
-
* lives in the Objective section. Empty arrays render no line. */
|
|
232
|
-
export function formatRoundContract(contract,
|
|
258
|
+
export function formatRoundContractWithIds(contract,
|
|
233
259
|
/** v3.8: the derived item statuses. When given, each item renders with its
|
|
234
260
|
* machine status — the agent sees the verification debt in its own task. */
|
|
235
261
|
statuses) {
|
|
236
262
|
const lines = [];
|
|
263
|
+
const emittedIds = [contract.id];
|
|
237
264
|
const heading = contract.work_item?.trim();
|
|
238
265
|
lines.push(`**${heading || "Round Contract"}** (${contract.id})`);
|
|
239
266
|
const statusById = new Map((statuses?.items ?? []).map((item) => [item.itemId, item]));
|
|
240
|
-
|
|
267
|
+
const items = contract.items;
|
|
268
|
+
if (items.length > 0) {
|
|
269
|
+
lines.push("- Acceptance conditions:");
|
|
270
|
+
}
|
|
271
|
+
for (const item of items) {
|
|
272
|
+
emittedIds.push(item.id);
|
|
241
273
|
const machine = statusById.get(item.id);
|
|
242
274
|
const mark = machine?.status === "verified" ? "✅"
|
|
243
275
|
: machine?.status === "insufficient" ? "🟠"
|
|
244
276
|
: machine?.status === "contradicted" ? "⛔"
|
|
245
277
|
: machine?.status === "pending" ? "⬜"
|
|
246
278
|
: "";
|
|
247
|
-
|
|
279
|
+
const status = machine ? ` — ${machine.status}` : "";
|
|
280
|
+
lines.push(` - ${mark ? `${mark} ` : ""}[\`${item.id}\`] ${item.description}${status}`);
|
|
248
281
|
if (item.verify_with.length > 0) {
|
|
249
|
-
lines.push(`
|
|
282
|
+
lines.push(` - Verify via: ${item.verify_with.join(", ")}`);
|
|
250
283
|
}
|
|
251
|
-
if (item.criterion_refs.length > 0) {
|
|
252
|
-
lines.push(`
|
|
284
|
+
if ((item.criterion_refs ?? []).length > 0) {
|
|
285
|
+
lines.push(` - Criteria: ${(item.criterion_refs ?? []).join(", ")}`);
|
|
253
286
|
}
|
|
254
287
|
if (machine?.reasons.length) {
|
|
255
|
-
lines.push(`
|
|
288
|
+
lines.push(` - Unverified because: ${machine.reasons[0]}`);
|
|
256
289
|
}
|
|
257
290
|
}
|
|
258
|
-
|
|
259
|
-
|
|
291
|
+
if (contract.scope.length > 0) {
|
|
292
|
+
// v3.9.9: scope is the Agent's declared intent, not a permission.
|
|
293
|
+
lines.push("- Declared scope (Agent report; not enforced):");
|
|
294
|
+
for (const entry of contract.scope) {
|
|
295
|
+
lines.push(` - ${entry}`);
|
|
296
|
+
}
|
|
260
297
|
}
|
|
261
|
-
return lines.join("\n");
|
|
298
|
+
return { text: lines.join("\n"), emittedIds };
|
|
262
299
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
/** Milestone heading line ("**🏁 Round 7** (Rounds 3–7)"). Shared by the
|
|
267
|
-
* L2 Phase History and the detailed L1 renderer; the L1 copy previously
|
|
268
|
-
* rendered the range as "R3–R7", a format only this heading used. */
|
|
269
|
-
export function milestoneHeading(milestone) {
|
|
270
|
-
const kindIcon = milestone.kind === "agent_declared"
|
|
271
|
-
? "🏁"
|
|
272
|
-
: milestone.kind === "criteria_milestone" ? "✅" : "📍";
|
|
273
|
-
return `**${kindIcon} ${milestone.label}** (Rounds ${milestone.round_range.start}–${milestone.round_range.end})`;
|
|
300
|
+
/** Backwards-compatible text-only view used by the state file. */
|
|
301
|
+
export function formatRoundContract(contract, statuses) {
|
|
302
|
+
return formatRoundContractWithIds(contract, statuses).text;
|
|
274
303
|
}
|
|
275
304
|
const STATE_SECTION_TIER = {
|
|
276
305
|
// Current — what the next round must hold in working memory.
|
|
277
306
|
"Loop Objective": "Current",
|
|
278
307
|
"Current Task": "Current",
|
|
308
|
+
"Active Round Contract": "Current",
|
|
309
|
+
"Current User Instruction": "Current",
|
|
279
310
|
"Success Criteria": "Current",
|
|
280
311
|
"Hard Constraints": "Current",
|
|
281
312
|
"Active Constraints": "Current",
|
|
282
|
-
|
|
283
|
-
|
|
313
|
+
// v4.0.3: Agent-reported blockers and remaining claims are one report
|
|
314
|
+
// surface; compile warnings are a separate machine/compile surface.
|
|
315
|
+
"Agent-Reported Blockers": "Current",
|
|
316
|
+
"Agent-Reported Remaining Criteria": "Current",
|
|
317
|
+
"Agent-Reported Resolutions": "Recent",
|
|
284
318
|
"Sub-Goal Dashboard": "Current",
|
|
285
|
-
"External Context": "Current",
|
|
286
319
|
// v3.8.1: the one-line phase position (the only fact the deleted Roadmap
|
|
287
320
|
// section carried that no other section renders). It is orientation for the
|
|
288
321
|
// NEXT round, so it belongs with the working-memory tier — and it must be
|
|
289
322
|
// listed here at all: an unlisted title is not a heading, so `## Phase` was
|
|
290
323
|
// absorbed into whatever section happened to precede it (its body even kept
|
|
291
324
|
// a raw `## ` line) and inherited that section's tier.
|
|
292
|
-
Phase: "Current",
|
|
293
325
|
// Recent — this round's boundary facts (and, in a recovery window, the
|
|
294
326
|
// derived Recovery Brief injected at the top of this tier).
|
|
295
327
|
"Progress Dashboard": "Recent",
|
|
296
328
|
Verification: "Recent",
|
|
297
|
-
|
|
298
|
-
|
|
329
|
+
// v4.0.3: the retry/governance facts and machine evidence live next to the
|
|
330
|
+
// round boundary they describe.
|
|
331
|
+
"Retry Requirements": "Recent",
|
|
332
|
+
"Compile Warnings": "Recent",
|
|
333
|
+
"Machine Evidence": "Recent",
|
|
334
|
+
"Rejected Attempt Evidence": "Recent",
|
|
335
|
+
// v4.0.2: bounded committed working memory. It replaces the former
|
|
336
|
+
// Corrections/Discoveries sections; discovered constraints remain visible
|
|
337
|
+
// through active-constraint provenance.
|
|
338
|
+
"Working Memory": "Recent",
|
|
299
339
|
// Historical Summary — the loop's memory skeleton, kept under existing
|
|
300
340
|
// milestone/window caps.
|
|
301
341
|
"Goal → Criteria": "Historical Summary",
|
|
302
|
-
"Cross-Round Outcomes": "Historical Summary",
|
|
303
342
|
"Recurring Flags": "Historical Summary",
|
|
304
|
-
"Failed Patterns": "Historical Summary",
|
|
305
343
|
"Retired Constraints": "Historical Summary",
|
|
306
|
-
"Phase History": "Historical Summary",
|
|
307
344
|
};
|
|
308
345
|
const STATE_TIER_ORDER = ["Current", "Recent", "Historical Summary"];
|
|
309
346
|
/** Human/Agent-readable materialized view. It is always reproducible from the
|
|
@@ -313,20 +350,34 @@ const STATE_TIER_ORDER = ["Current", "Recent", "Historical Summary"];
|
|
|
313
350
|
* criterion list, round stats, the FULL recurring-flag history and the phase
|
|
314
351
|
* history. The prompt carries only what the next round needs. */
|
|
315
352
|
export function renderCanonicalStateMarkdown(state, options) {
|
|
353
|
+
const context = state.context ?? options?.context;
|
|
316
354
|
const preamble = [
|
|
317
355
|
`# LoopForge State — ${state.loopId}`,
|
|
318
356
|
"",
|
|
319
357
|
`**Schema**: ${state.schemaVersion}`,
|
|
320
358
|
`**Round**: ${state.round}/${state.maxRounds}`,
|
|
321
359
|
`**Goal ID**: ${state.goalId}`,
|
|
360
|
+
...(state.currentUserInstruction
|
|
361
|
+
? []
|
|
362
|
+
: [`**Current user instruction**: ${CURRENT_USER_INSTRUCTION_NOT_PROVIDED}`]),
|
|
363
|
+
...(context
|
|
364
|
+
? [
|
|
365
|
+
`**Context**: ${formatContextBoundaryLabel(context.mode, context.reason, { recoveryBoundary: (state.recoveryBrief?.length ?? 0) > 0 })}, attempt ${context.attempt}`,
|
|
366
|
+
]
|
|
367
|
+
: []),
|
|
322
368
|
"**Derived**: true",
|
|
323
|
-
`**Source**: round ${state.round} (attempt ${options?.attempt ?? 1})`,
|
|
369
|
+
`**Source**: round ${state.round} (attempt ${context?.attempt ?? options?.attempt ?? 1})`,
|
|
370
|
+
"**Authority**: committed typed rounds are the factual source; this file is a derived view.",
|
|
371
|
+
"**Round commit**: a LoopForge round commit is not a Git commit; it does not verify every workspace change.",
|
|
372
|
+
"**Evidence**: machine evidence = providers, command observations and verified contract items. Agent reports = outcome/files/claims.",
|
|
373
|
+
"**Working memory**: bounded; it may be abbreviated; full history via replay/explain or this file when enabled.",
|
|
324
374
|
`**State hash**: ${hashCanonicalState(state).slice(0, 12)}`,
|
|
325
375
|
"",
|
|
326
376
|
];
|
|
327
377
|
const sections = flatStateSections(state);
|
|
328
|
-
|
|
329
|
-
|
|
378
|
+
const recoveryBrief = state.recoveryBrief ?? options?.recoveryBrief;
|
|
379
|
+
if (recoveryBrief && recoveryBrief.length > 0) {
|
|
380
|
+
sections.unshift({ title: "Recovery Brief", body: recoveryBrief });
|
|
330
381
|
}
|
|
331
382
|
const byTier = new Map();
|
|
332
383
|
for (const tier of STATE_TIER_ORDER)
|
|
@@ -356,6 +407,16 @@ export function renderCanonicalStateMarkdown(state, options) {
|
|
|
356
407
|
function flatStateSections(state) {
|
|
357
408
|
const lines = [];
|
|
358
409
|
lines.push("## Loop Objective", "", state.objective, "", "## Current Task", "", state.currentTask);
|
|
410
|
+
if (state.roundContract && !contractDrivesCurrentTask(state)) {
|
|
411
|
+
lines.push("## Active Round Contract", "", formatRoundContract(state.roundContract, state.contractItemStatuses ?? null));
|
|
412
|
+
}
|
|
413
|
+
// The host's invocation instruction renders as its own section only when
|
|
414
|
+
// it is not already visible in Current Task or Objective.
|
|
415
|
+
if (state.currentUserInstruction &&
|
|
416
|
+
state.currentUserInstruction !== state.currentTask.trim() &&
|
|
417
|
+
state.currentUserInstruction !== state.objective.trim()) {
|
|
418
|
+
lines.push("## Current User Instruction", "", state.currentUserInstruction, "", `> ${CURRENT_USER_INSTRUCTION_AUTHORITY}`);
|
|
419
|
+
}
|
|
359
420
|
// v2.11: Success Criteria and Constraints render with stable IDs for exact
|
|
360
421
|
// agent matching. v3.9.4: the three groups are the SAME mutually exclusive
|
|
361
422
|
// partition the prompt uses, so one text never appears with two prefixes.
|
|
@@ -363,38 +424,61 @@ function flatStateSections(state) {
|
|
|
363
424
|
addListWithIds(lines, "Success Criteria", constraintPartition.criteria, "cr");
|
|
364
425
|
addListWithIdentities(lines, "Hard Constraints", constraintPartition.hard, state.constraintMetadata);
|
|
365
426
|
addListWithIdentities(lines, "Active Constraints", constraintPartition.other, state.constraintMetadata);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
427
|
+
if (state.rejectionNotice) {
|
|
428
|
+
lines.push("## Retry Requirements", "", state.rejectionNotice.trim(), "");
|
|
429
|
+
}
|
|
430
|
+
addList(lines, "Compile Warnings", state.warnings);
|
|
431
|
+
if (state.previousEvidencePosture) {
|
|
432
|
+
lines.push("## Machine Evidence", "", ...formatEvidencePostureLines(state.previousEvidencePosture), "", `> ${MACHINE_EVIDENCE_AUTHORITY}`, "");
|
|
433
|
+
}
|
|
434
|
+
if (state.retryEvidencePosture) {
|
|
435
|
+
lines.push("## Rejected Attempt Evidence", "", "> Not committed; it exists only for the current retry.", "", ...formatEvidencePostureLines(state.retryEvidencePosture), "");
|
|
436
|
+
}
|
|
437
|
+
if (hasWorkingMemoryContent(state.workingMemory)) {
|
|
438
|
+
const working = formatWorkingMemoryState(state.workingMemory);
|
|
439
|
+
lines.push("## Working Memory", "", ...working.lines, "");
|
|
440
|
+
}
|
|
441
|
+
const reported = state.reportedProgress;
|
|
442
|
+
const posture = state.previousEvidencePosture;
|
|
443
|
+
if (reported.criteriaMet.length > 0 ||
|
|
444
|
+
reported.criteriaRemaining.length > 0 ||
|
|
445
|
+
reported.filesChanged.length > 0 ||
|
|
446
|
+
state.machineStatus !== undefined ||
|
|
447
|
+
posture !== undefined) {
|
|
448
|
+
const total = reported.criteriaMet.length + reported.criteriaRemaining.length;
|
|
373
449
|
lines.push("## Progress Dashboard", "");
|
|
374
450
|
if (total > 0) {
|
|
375
|
-
lines.push(`**
|
|
451
|
+
lines.push(`**Agent-reported criteria**: ${reported.criteriaMet.length}/${total}`);
|
|
452
|
+
}
|
|
453
|
+
if (state.criterionStatuses.length > 0) {
|
|
454
|
+
const verifiedCount = state.criterionStatuses.filter((criterion) => criterion.status === "verified").length;
|
|
455
|
+
lines.push(`**Machine-verified criteria**: ${verifiedCount}/${state.criterionStatuses.length}`);
|
|
456
|
+
}
|
|
457
|
+
if (reported.filesChanged.length > 0) {
|
|
458
|
+
lines.push("", "**Agent-reported files**:");
|
|
459
|
+
for (const file of reported.filesChanged)
|
|
460
|
+
lines.push(`- ${file}`);
|
|
461
|
+
}
|
|
462
|
+
if (posture) {
|
|
463
|
+
const observed = posture.workspace.status === "observed"
|
|
464
|
+
? `${posture.workspace.changedPathCount}`
|
|
465
|
+
: posture.workspace.status === "unknown" ? "unknown" : "not applicable";
|
|
466
|
+
lines.push(`**Observed changes (last committed round)**: ${observed}`);
|
|
467
|
+
lines.push(`**Files reconciliation**: ${posture.files?.reconciliation ?? "not_applicable"}`);
|
|
376
468
|
}
|
|
377
469
|
// v3.3: machine side of the comparison — git motion over committed rounds
|
|
378
470
|
// (v3.6: the object always carries definite values — no "unavailable" arm).
|
|
379
471
|
if (state.machineStatus) {
|
|
380
472
|
const ms = state.machineStatus;
|
|
381
|
-
const
|
|
382
|
-
? `
|
|
383
|
-
:
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (state.progress.tests) {
|
|
391
|
-
lines.push(`**Tests**: ${state.progress.tests.passed} passed, ` +
|
|
392
|
-
`${state.progress.tests.failed} failed, ${state.progress.tests.skipped} skipped`);
|
|
393
|
-
}
|
|
394
|
-
if (state.progress.filesChanged.length > 0) {
|
|
395
|
-
lines.push("", "**Files Changed**:");
|
|
396
|
-
for (const file of state.progress.filesChanged)
|
|
397
|
-
lines.push(`- ${file}`);
|
|
473
|
+
const providers = ms.providers.length > 0
|
|
474
|
+
? ` (providers: ${ms.providers.join(", ")})`
|
|
475
|
+
: "";
|
|
476
|
+
const progress = ms.machineProgress === "changed"
|
|
477
|
+
? `changes observed in ${ms.changedRounds}/${ms.windowRounds} recent committed rounds${providers}`
|
|
478
|
+
: ms.machineProgress === "unknown"
|
|
479
|
+
? `not observable in the last ${ms.windowRounds} committed rounds${providers}`
|
|
480
|
+
: `no machine-observed progress in the last ${ms.windowRounds} committed rounds${providers}`;
|
|
481
|
+
lines.push(`**Machine progress**: ${progress}`);
|
|
398
482
|
}
|
|
399
483
|
lines.push("");
|
|
400
484
|
}
|
|
@@ -408,20 +492,35 @@ function flatStateSections(state) {
|
|
|
408
492
|
: cs.status === "contradicted" ? "⛔"
|
|
409
493
|
: cs.status === "remaining" ? "⬜" : "❔";
|
|
410
494
|
const idTag = ` [\`${cs.id}\`]`;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
495
|
+
// v4.0.3: the round and the provenance are separate facts. A claimed
|
|
496
|
+
// row names the latest Agent claim; a verified/contradicted/insufficient
|
|
497
|
+
// row names the machine round that reached it.
|
|
498
|
+
const at = cs.status_at_round !== undefined ? ` R${cs.status_at_round}` : "";
|
|
499
|
+
const label = cs.status === "verified" ? `machine verified${at}`
|
|
500
|
+
: cs.status === "claimed" ? `agent claim${at}`
|
|
501
|
+
: cs.status === "insufficient" ? `machine insufficient${at}`
|
|
502
|
+
: cs.status === "contradicted" ? `machine contradicted${at}`
|
|
503
|
+
: cs.status === "remaining" ? `agent reported remaining${at}`
|
|
504
|
+
: "unknown";
|
|
505
|
+
lines.push(`- ${icon}${idTag} ${cs.text} (${label})`);
|
|
506
|
+
}
|
|
507
|
+
lines.push("");
|
|
508
|
+
}
|
|
509
|
+
if (reported.agentReportedRemaining.length > 0) {
|
|
510
|
+
addListWithIds(lines, "Agent-Reported Remaining Criteria", reported.agentReportedRemaining, "cr");
|
|
511
|
+
}
|
|
512
|
+
addList(lines, "Agent-Reported Blockers", state.reportedIssues);
|
|
513
|
+
if (state.resolvedOpenQuestionIds && state.resolvedOpenQuestionIds.length > 0) {
|
|
514
|
+
const shown = state.resolvedOpenQuestionIds.slice(0, 6);
|
|
515
|
+
lines.push("## Agent-Reported Resolutions", "", `- ${state.resolvedOpenQuestionIds.length} open question(s) resolved in the ` +
|
|
516
|
+
"last committed round (Agent report):", ...shown.map((id) => ` - \`${id}\``));
|
|
517
|
+
if (state.resolvedOpenQuestionIds.length > shown.length) {
|
|
518
|
+
lines.push(` - ... and ${state.resolvedOpenQuestionIds.length - shown.length} more ` +
|
|
519
|
+
"(replay/explain shows every id)");
|
|
416
520
|
}
|
|
417
521
|
lines.push("");
|
|
418
522
|
}
|
|
419
|
-
addListWithIds(lines, "Remaining", state.remainingCriteria, "cr");
|
|
420
|
-
addList(lines, "Blockers", state.blockers);
|
|
421
523
|
const recurring = recurringOnly(state.recurringFlags);
|
|
422
|
-
addList(lines, "Discoveries", state.discoveries);
|
|
423
|
-
addList(lines, "Cross-Round Outcomes", state.rollingOutcomes);
|
|
424
|
-
addList(lines, "Failed Patterns", state.failedPatterns);
|
|
425
524
|
// v3.8.1: ONE recurring-facts section. "Recurring Issues" (the last 5
|
|
426
525
|
// rounds' raw violations, no threshold) and "Lessons Learned" (the whole
|
|
427
526
|
// history, count >= 2) were two windows over one fact set rendered as two
|
|
@@ -434,19 +533,21 @@ function flatStateSections(state) {
|
|
|
434
533
|
? "🚫"
|
|
435
534
|
: flag.kind === "verification_error" ? "⚠️" : "ℹ️";
|
|
436
535
|
const subject = flag.ref ? `${flag.subject} [${flag.ref}]` : flag.subject;
|
|
437
|
-
|
|
536
|
+
const source = flag.kind === "constraint_violation"
|
|
537
|
+
? "Agent-reported violation"
|
|
538
|
+
: "machine finding";
|
|
539
|
+
lines.push(`- ${icon} ${subject} — ${flag.count}× (R${flag.rounds.join(", R")}) — ${source}`);
|
|
438
540
|
}
|
|
439
541
|
if (recurring.length > RECURRING_RENDER_CAP) {
|
|
440
542
|
lines.push(`- ... and ${recurring.length - RECURRING_RENDER_CAP} more`);
|
|
441
543
|
}
|
|
442
544
|
lines.push("");
|
|
443
545
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
lines.push("## Phase", "", `- ${phaseLine}`);
|
|
546
|
+
if (state.retiredConstraints.length > 0) {
|
|
547
|
+
lines.push("## Retired Constraints", "");
|
|
548
|
+
for (const constraint of state.retiredConstraints) {
|
|
549
|
+
lines.push(`- ${constraint} (Agent report)`);
|
|
550
|
+
}
|
|
450
551
|
lines.push("");
|
|
451
552
|
}
|
|
452
553
|
if (state.verificationFlags.length > 0) {
|
|
@@ -457,27 +558,6 @@ function flatStateSections(state) {
|
|
|
457
558
|
}
|
|
458
559
|
lines.push("");
|
|
459
560
|
}
|
|
460
|
-
if (state.milestones.length > 0) {
|
|
461
|
-
lines.push("## Phase History", "");
|
|
462
|
-
for (const m of state.milestones) {
|
|
463
|
-
const kindIcon = m.kind === "agent_declared"
|
|
464
|
-
? "🏁"
|
|
465
|
-
: m.kind === "criteria_milestone" ? "✅" : "📍";
|
|
466
|
-
lines.push(`### ${kindIcon} ${m.label}`, `Rounds ${m.round_range.start}–${m.round_range.end}`, "", `**Outcome**: ${m.outcome}`, "");
|
|
467
|
-
if (m.carried_constraints.length > 0) {
|
|
468
|
-
lines.push("**Carried Constraints**:");
|
|
469
|
-
for (const c of m.carried_constraints)
|
|
470
|
-
lines.push(`- ${c}`);
|
|
471
|
-
lines.push("");
|
|
472
|
-
}
|
|
473
|
-
if (m.resolved_constraints.length > 0) {
|
|
474
|
-
lines.push("**Resolved During Phase**:");
|
|
475
|
-
for (const c of m.resolved_constraints)
|
|
476
|
-
lines.push(`- ${c}`);
|
|
477
|
-
lines.push("");
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
561
|
if (state.subGoals.length > 0) {
|
|
482
562
|
// v3.9.4: the SAME formatter the prompt uses. Rows past the display cap
|
|
483
563
|
// keep their ids in an overflow line, so the state file's active id set
|
|
@@ -488,9 +568,6 @@ function flatStateSections(state) {
|
|
|
488
568
|
lines.push("## Sub-Goal Dashboard", "");
|
|
489
569
|
lines.push(...formatActiveSubGoals(state.subGoals, state.round).lines, "");
|
|
490
570
|
}
|
|
491
|
-
if (state.externalContext) {
|
|
492
|
-
lines.push("## External Context", "", state.externalContext, "");
|
|
493
|
-
}
|
|
494
571
|
// Split the flat markdown into titled sections for tier grouping. Every
|
|
495
572
|
// builder above emits `## <Title>` headings over their content — but only
|
|
496
573
|
// the KNOWN section titles are headings. Free-text bodies (objective,
|
|
@@ -532,33 +609,45 @@ export function createCanonicalLoopState(request, response, stateFilePath,
|
|
|
532
609
|
/** v3.3: Display-only derived data. Optional 4th param — callers that
|
|
533
610
|
* predate v3.3 stay on 3-arg calls. */
|
|
534
611
|
derived) {
|
|
535
|
-
const last = derived?.previousRound?.result ??
|
|
612
|
+
const last = derived?.previousRound?.result ?? null;
|
|
536
613
|
const objective = response.loop_objective;
|
|
537
614
|
const verificationFlags = derived?.previousRound?.verificationFlags ??
|
|
538
615
|
request.verification_flags ?? [];
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
616
|
+
// v3.9.5: only committed previous-round reports enter the projection. The
|
|
617
|
+
// compiler no longer has an uncommitted fallback at all.
|
|
618
|
+
const committed = derived?.previousRound?.committed === true;
|
|
619
|
+
// Keep the report projection behind the same boundary as corrections and
|
|
620
|
+
// blockers. The fallback result may still be consumed by compiler
|
|
621
|
+
// evolution, but an uncommitted/rejected attempt cannot become durable
|
|
622
|
+
// progress, discovery, or criterion state.
|
|
623
|
+
const executionReport = committed ? last?.execution_report : undefined;
|
|
624
|
+
// v4.0.3: blockers are Agent reports; warnings are compiler facts. They
|
|
625
|
+
// were previously merged into one `blockers` list.
|
|
626
|
+
const reportedIssues = unique([
|
|
627
|
+
...(committed ? (last?.constraint_violations ?? []) : []),
|
|
628
|
+
...(committed && last?.blocker ? [last.blocker] : []),
|
|
549
629
|
]);
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
//
|
|
630
|
+
const warnings = unique(response.warnings);
|
|
631
|
+
const committedPosture = committed ? derived?.previousRound?.view?.evidencePosture ?? null : null;
|
|
632
|
+
const retryPosture = request.retry_evidence_posture ?? null;
|
|
633
|
+
// v4.0.3: the Agent's resolved-question report is bounded and rendered as a
|
|
634
|
+
// separate provenance line; it never silently disappears into Working
|
|
635
|
+
// Memory's active-set filtering.
|
|
636
|
+
const resolvedOpenQuestionIds = committed
|
|
637
|
+
? unique(last?.resolved_open_question_ids ?? [])
|
|
638
|
+
.filter(isOpenQuestionId)
|
|
639
|
+
: [];
|
|
640
|
+
// v4.0.6: The Agent still chooses when a round ends. The ACTIVE Round
|
|
641
|
+
// Contract (derived from committed rounds) supplies the verifiable work
|
|
642
|
+
// slice for this invocation when verification capability exists. An
|
|
643
|
+
// explicit invocation instruction has priority; without contract capability
|
|
644
|
+
// the Current Task falls back to the working objective.
|
|
561
645
|
const roundContract = derived?.roundFacts?.activeContract ?? null;
|
|
646
|
+
const capability = deriveConfiguredCapability(getPolicy());
|
|
647
|
+
// v4.0.5: the host's explicit invocation input. Empty/whitespace is "not
|
|
648
|
+
// provided", never a second task. Conditional presence keeps the state hash
|
|
649
|
+
// byte-identical when no instruction was given.
|
|
650
|
+
const currentUserInstruction = request.current_user_instruction?.trim() || undefined;
|
|
562
651
|
// v3.9.4: the rendered criterion/hard sets are ACTIVE sets. The compile
|
|
563
652
|
// response's `criterion_statuses` is the active criterion reference space
|
|
564
653
|
// (loop-compiler filters it); `constraints_active` is the active constraint
|
|
@@ -584,47 +673,52 @@ derived) {
|
|
|
584
673
|
maxRounds: request.max_rounds ?? getPolicy().engine.max_rounds,
|
|
585
674
|
goalId: response.goal_id,
|
|
586
675
|
objective: objective?.objective || request.task,
|
|
587
|
-
|
|
588
|
-
currentTask: roundContract
|
|
676
|
+
currentTask: currentUserInstruction ?? (roundContract && capability.contractVerificationAvailable
|
|
589
677
|
? formatRoundContract(roundContract, derived?.roundFacts?.itemStatuses ?? null)
|
|
590
|
-
: request.task,
|
|
678
|
+
: objective?.objective ?? request.task),
|
|
679
|
+
...(currentUserInstruction ? { currentUserInstruction } : {}),
|
|
591
680
|
successCriteria,
|
|
592
681
|
hardConstraints,
|
|
593
682
|
activeConstraints,
|
|
594
683
|
retiredConstraints: unique(response.constraints_retired),
|
|
595
684
|
constraintMetadata: response.constraint_metadata ?? [],
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
685
|
+
rejectionNotice: request.rejection_notice.trim() ? request.rejection_notice : null,
|
|
686
|
+
// v4.0.3: conditional presence keeps hashes stable for a first round.
|
|
687
|
+
...(committedPosture ? { previousEvidencePosture: committedPosture } : {}),
|
|
688
|
+
...(retryPosture ? { retryEvidencePosture: retryPosture } : {}),
|
|
689
|
+
reportedIssues,
|
|
690
|
+
warnings,
|
|
599
691
|
verificationFlags,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
692
|
+
// v4.0.2: conditional presence — no committed working memory, no key.
|
|
693
|
+
...(hasWorkingMemoryContent(derived?.workingMemory)
|
|
694
|
+
? { workingMemory: derived.workingMemory }
|
|
695
|
+
: {}),
|
|
604
696
|
subGoals: response.sub_goals ?? [],
|
|
605
697
|
criterionStatuses: response.criterion_statuses ?? [],
|
|
606
698
|
recurringFlags: response.recurring_flags ?? [],
|
|
607
|
-
externalContext: request.external_context?.trim() ?? "",
|
|
608
699
|
stateFilePath,
|
|
609
|
-
|
|
700
|
+
reportedProgress: {
|
|
610
701
|
criteriaMet: unique(claimedMetCriteria(executionReport)),
|
|
611
702
|
criteriaRemaining: unique(claimedRemainingCriteria(executionReport)),
|
|
703
|
+
agentReportedRemaining: unique(claimedRemainingCriteria(executionReport)),
|
|
612
704
|
filesChanged: unique(executionReport?.files_changed ?? []),
|
|
613
|
-
tests: executionReport?.tests_reported ?? null,
|
|
614
705
|
},
|
|
706
|
+
...(resolvedOpenQuestionIds.length > 0
|
|
707
|
+
? { resolvedOpenQuestionIds }
|
|
708
|
+
: {}),
|
|
709
|
+
...(derived?.context ? { context: derived.context } : {}),
|
|
615
710
|
// v3.3: Conditional presence — empty/absent derived data must not add
|
|
616
711
|
// keys, or every state hash would change for rounds without it.
|
|
712
|
+
...(derived?.recoveryBrief && derived.recoveryBrief.length > 0
|
|
713
|
+
? { recoveryBrief: [...derived.recoveryBrief] }
|
|
714
|
+
: {}),
|
|
617
715
|
...(derived?.machineStatus ? { machineStatus: derived.machineStatus } : {}),
|
|
618
716
|
// v3.3: Conditional presence — no contract, no key (hash stability).
|
|
619
717
|
...(roundContract ? { roundContract } : {}),
|
|
620
718
|
...(derived?.roundFacts?.itemStatuses
|
|
621
719
|
? { contractItemStatuses: derived.roundFacts.itemStatuses }
|
|
622
720
|
: {}),
|
|
623
|
-
|
|
624
|
-
derived.roundFacts.verifiedSubGoals.length > 0
|
|
625
|
-
? { verifiedSubGoals: [...derived.roundFacts.verifiedSubGoals] }
|
|
626
|
-
: {}),
|
|
627
|
-
capability: deriveConfiguredCapability(getPolicy()),
|
|
721
|
+
capability,
|
|
628
722
|
};
|
|
629
723
|
}
|
|
630
724
|
//# sourceMappingURL=canonical-state.js.map
|