loopforge 3.7.1 → 3.8.1
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 +171 -169
- package/dist/audit.d.ts +53 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +106 -22
- package/dist/audit.js.map +1 -1
- package/dist/canonical-state.d.ts +52 -67
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +134 -147
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +103 -4
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-facts.d.ts +13 -0
- package/dist/cognitive-facts.d.ts.map +1 -1
- package/dist/cognitive-facts.js +28 -13
- package/dist/cognitive-facts.js.map +1 -1
- package/dist/committed-round.d.ts +103 -21
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +188 -45
- package/dist/committed-round.js.map +1 -1
- package/dist/contract-items.d.ts +55 -0
- package/dist/contract-items.d.ts.map +1 -0
- package/dist/contract-items.js +180 -0
- package/dist/contract-items.js.map +1 -0
- package/dist/enforcement-gate.d.ts +20 -2
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +197 -386
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +53 -38
- package/dist/engine.js.map +1 -1
- package/dist/evidence-claims.d.ts +4 -4
- package/dist/evidence-claims.d.ts.map +1 -1
- package/dist/evidence-claims.js +16 -14
- package/dist/evidence-claims.js.map +1 -1
- package/dist/evidence-provider.d.ts +85 -89
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +328 -184
- package/dist/evidence-provider.js.map +1 -1
- package/dist/explain.d.ts +76 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +127 -0
- package/dist/explain.js.map +1 -0
- package/dist/generate-schema.js +152 -16
- package/dist/generate-schema.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts +40 -53
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +229 -553
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-extras-parser.d.ts +0 -1
- package/dist/loop-extras-parser.d.ts.map +1 -1
- package/dist/loop-extras-parser.js +0 -4
- package/dist/loop-extras-parser.js.map +1 -1
- package/dist/loop-projection.d.ts.map +1 -1
- package/dist/loop-projection.js +3 -1
- package/dist/loop-projection.js.map +1 -1
- package/dist/mcp/round-lifecycle.d.ts +30 -21
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +47 -103
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +51 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +39 -23
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +148 -163
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/submission-boundary.d.ts +89 -0
- package/dist/mcp/submission-boundary.d.ts.map +1 -0
- package/dist/mcp/submission-boundary.js +203 -0
- package/dist/mcp/submission-boundary.js.map +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +249 -128
- package/dist/mcp/tools.js.map +1 -1
- package/dist/policy-metrics.js +2 -2
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +55 -93
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +149 -38
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts +1 -32
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +282 -435
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-policy.d.ts +1 -4
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +6 -7
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +365 -142
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +21 -66
- package/dist/protocol.js.map +1 -1
- package/dist/replay.js +2 -2
- package/dist/replay.js.map +1 -1
- package/dist/round-contract.d.ts +69 -57
- package/dist/round-contract.d.ts.map +1 -1
- package/dist/round-contract.js +104 -64
- package/dist/round-contract.js.map +1 -1
- package/dist/round-coordinator.d.ts +36 -16
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +166 -102
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +3 -5
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +0 -1
- package/dist/round-driver.js.map +1 -1
- package/dist/round-facts.d.ts +96 -0
- package/dist/round-facts.d.ts.map +1 -0
- package/dist/round-facts.js +216 -0
- package/dist/round-facts.js.map +1 -0
- package/dist/round-transaction.d.ts +19 -9
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +90 -22
- package/dist/round-transaction.js.map +1 -1
- package/dist/self-eval.d.ts +87 -9
- package/dist/self-eval.d.ts.map +1 -1
- package/dist/self-eval.js +396 -27
- package/dist/self-eval.js.map +1 -1
- package/dist/subgoal-state.d.ts +77 -0
- package/dist/subgoal-state.d.ts.map +1 -0
- package/dist/subgoal-state.js +198 -0
- package/dist/subgoal-state.js.map +1 -0
- package/dist/token-utils.d.ts +52 -19
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +83 -72
- package/dist/token-utils.js.map +1 -1
- package/dist/verification-gate.d.ts +14 -31
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +178 -496
- package/dist/verification-gate.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace.d.ts +9 -0
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +17 -0
- package/dist/workspace.js.map +1 -1
- package/loop_policy.json +6 -30
- package/package.json +1 -1
- package/skills/perception/SKILL.md +243 -79
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: perception
|
|
3
|
-
description: Drive a
|
|
3
|
+
description: Drive a multi-round coding task through LoopForge MCP so goals, constraints, evidence, and decisions survive context loss and restarts. Use when a task needs several rounds, must survive context compression or a process restart, or needs an auditable record of what was verified by the machine rather than claimed by the model. The external Agent stays the execution owner.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Perception
|
|
@@ -20,8 +20,15 @@ Call `loopforge_start` with:
|
|
|
20
20
|
|
|
21
21
|
- `task`: the complete user objective.
|
|
22
22
|
- `constraints`: hard boundaries that must survive every round.
|
|
23
|
-
- `maxRounds`: optional positive safety limit. The default is
|
|
23
|
+
- `maxRounds`: optional positive safety limit. The default is 200 from policy.
|
|
24
24
|
- `domain`: optional context label.
|
|
25
|
+
- `loopId`: optional stable loop id. Generated for you when omitted — pass one
|
|
26
|
+
when the caller already knows it, so later calls can address the loop.
|
|
27
|
+
- `planSource`: optional path to a plan document to extract constraints from.
|
|
28
|
+
|
|
29
|
+
Before starting, `loopforge_status` with `view: "all"` lists the loops the
|
|
30
|
+
runtime already holds — reuse an existing session instead of opening a second
|
|
31
|
+
one for the same objective.
|
|
25
32
|
|
|
26
33
|
Keep the returned `sessionId`, `roundId`, prompt, and level. Always pass the
|
|
27
34
|
most recent `roundId` to `loopforge_next`. It anchors the submission to the
|
|
@@ -43,16 +50,16 @@ Choose a round around a coherent, verifiable change:
|
|
|
43
50
|
machine-checkable next boundary. A round may be small when uncertainty or
|
|
44
51
|
risk is high, and larger when the changes are tightly coupled.
|
|
45
52
|
4. Before submitting, check the actual diff, relevant tests, hard constraints,
|
|
46
|
-
and
|
|
47
|
-
is complete.
|
|
53
|
+
and what is still open. Do not mark the whole task complete because one
|
|
54
|
+
slice is complete.
|
|
48
55
|
|
|
49
56
|
For work that spans several rounds, declare a `round_contract` as a proposal for
|
|
50
|
-
the next slice. Keep its `work_item` narrow,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`outcome: "blocked"` with a concrete `blocker`; do
|
|
55
|
-
active contract.
|
|
57
|
+
the next slice. Keep its `work_item` narrow, declare `items` that each bind a
|
|
58
|
+
configured evidence command in `verify_with`, and constrain `scope` to the files
|
|
59
|
+
or directories that slice may touch. The proposal becomes active only after the
|
|
60
|
+
current round commits, and the prompt then renders its `rc-` / `rci-` ids. If
|
|
61
|
+
the slice is blocked, report `outcome: "blocked"` with a concrete `blocker`; do
|
|
62
|
+
not silently replace the active contract.
|
|
56
63
|
|
|
57
64
|
This is a granularity preflight, not another gate: ask whether the slice has a
|
|
58
65
|
clear boundary, evidence, and recovery path. LoopForge cannot see an implicit
|
|
@@ -78,20 +85,41 @@ The four core evaluation fields are strict:
|
|
|
78
85
|
| `should_continue` | Boolean. Use `false` only when the entire task is complete. |
|
|
79
86
|
|
|
80
87
|
All other evaluation fields are optional and normalized by LoopForge. Useful
|
|
81
|
-
fields include `
|
|
82
|
-
`
|
|
88
|
+
fields include `execution_report` (`files_changed`, `tests_reported`,
|
|
89
|
+
`criterion_claims`, `contract_item_claims`, `progress_estimate`),
|
|
90
|
+
`discovered_constraints`, `emerged_subtasks` (the only sub-goal creation
|
|
91
|
+
channel — never an `sg-` ID; at most 50 entries, 500 characters each),
|
|
83
92
|
`subgoal_updates` (explicit status transitions: active `sg-` IDs with a target
|
|
84
|
-
status; done/canceled are terminal and never reopen), `
|
|
85
|
-
`drift_clarification`, `prompt_requests`, `outcome`, `blocker`,
|
|
93
|
+
status; done/canceled are terminal and never reopen), `prompt_requests`,
|
|
86
94
|
`round_contract`, `retroactiveClaims`, `gate_ids` (only when the gate layer is
|
|
87
|
-
enabled), and `worker_results` (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
enabled), and `worker_results` (entries need `agentId`, `subTask`,
|
|
96
|
+
`resultSummary` and an `outcome` of `success` / `partial` / `failed`; entries
|
|
97
|
+
missing any of these are dropped).
|
|
98
|
+
|
|
99
|
+
The objective and the constraint set evolve only through your own reports:
|
|
100
|
+
`objective_refinement` deepens the objective, `revised_success_criteria` (each
|
|
101
|
+
entry `{old, new}`) replaces a criterion, `retracted_constraints` retires a
|
|
102
|
+
constraint you now consider wrong, `wrong_assumptions` records what you
|
|
103
|
+
believed and had to correct, and `compression_checkpoint` with a
|
|
104
|
+
`checkpoint_label` marks a subtask boundary that forces a full-state prompt.
|
|
105
|
+
`outcome` is the declared outcome (`success` / `partial` / `failed` /
|
|
106
|
+
`blocked`) and `blocker` describes a `blocked` outcome; `stop_reason`
|
|
107
|
+
(`"blocked"` or `"needs_human_input"`) is how you stop without claiming
|
|
108
|
+
success. `no_change_reason` is the honest escape hatch for a round that
|
|
109
|
+
genuinely produced no machine-verifiable work: it downgrades the "success with
|
|
110
|
+
no machine evidence" finding to info, but only while NO enabled verification
|
|
111
|
+
command is configured — a configured command closes that escape. It never
|
|
112
|
+
excuses a success claim with no `execution_report` at all, and it never
|
|
113
|
+
excuses a contract check: every successful round still needs `execution_report`
|
|
114
|
+
with `files_changed` and `tests_reported`.
|
|
115
|
+
|
|
116
|
+
Optional malformed values are ignored or defaulted — the Round Contract
|
|
117
|
+
declaration, `contract_item_claims`, and `subgoal_updates` are the strict
|
|
118
|
+
exceptions. They do not turn a valid submission into a transport error. Missing
|
|
119
|
+
or mistyped core fields return `evaluation_invalid` with `details.missing` and
|
|
120
|
+
`details.invalid`. Correct the payload and resubmit the same `roundId`. This
|
|
121
|
+
does not save session state, write a round, enter either gate, increment
|
|
122
|
+
rejection state, or record metrics.
|
|
95
123
|
|
|
96
124
|
Minimal evaluation:
|
|
97
125
|
|
|
@@ -101,18 +129,24 @@ Minimal evaluation:
|
|
|
101
129
|
"output_summary": "Implemented the parser and added 8 passing tests.",
|
|
102
130
|
"constraint_violations": [],
|
|
103
131
|
"should_continue": true,
|
|
104
|
-
"
|
|
132
|
+
"execution_report": {
|
|
105
133
|
"files_changed": ["src/parser.ts", "tests/parser.test.ts"],
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
|
|
134
|
+
"tests_reported": { "passed": 8, "failed": 0, "skipped": 0 },
|
|
135
|
+
"criterion_claims": [
|
|
136
|
+
{ "criterion_id": "cr-b5a6c7d8", "outcome": "remaining" }
|
|
137
|
+
],
|
|
138
|
+
"contract_item_claims": [
|
|
139
|
+
{ "item_id": "rci-9c8b7a65", "outcome": "met" }
|
|
140
|
+
],
|
|
109
141
|
"progress_estimate": 0.4
|
|
110
142
|
}
|
|
111
143
|
}
|
|
112
144
|
```
|
|
113
145
|
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
Everything in `execution_report` is your CLAIM, quoted back for audit — it never
|
|
147
|
+
becomes a verified fact. Only machine observations create facts. Do not put a
|
|
148
|
+
guessed evaluation in free text. The structured object is the authoritative
|
|
149
|
+
submission; `output` is optional supporting context.
|
|
116
150
|
|
|
117
151
|
If a submission warns that its `roundId` no longer matches, the reported round
|
|
118
152
|
already committed. Use the returned held prompt and its `roundId` rather than
|
|
@@ -120,35 +154,116 @@ submitting against a later round.
|
|
|
120
154
|
|
|
121
155
|
## Stable IDs and contracts
|
|
122
156
|
|
|
123
|
-
Prompts render stable IDs beside constraints, criteria,
|
|
157
|
+
Prompts render stable IDs beside constraints, criteria, sub-goals, and contract
|
|
158
|
+
items:
|
|
124
159
|
|
|
125
160
|
- Constraints: `[c-a3f2b1c0] No plaintext passwords`
|
|
126
161
|
- Criteria: `[cr-b5a6c7d8] Unit test coverage >= 90%`
|
|
127
162
|
- Sub-goals: `[sg-f1e2d3c4] Add error handling to login`
|
|
163
|
+
- Contract items: `[rci-9c8b7a65] All parser edge cases covered`
|
|
128
164
|
|
|
129
|
-
Prefer IDs for `constraint_violations
|
|
130
|
-
|
|
165
|
+
Prefer IDs for `constraint_violations` and criterion claims. A sub-goal ID is
|
|
166
|
+
scoped to its declaration event: re-declaring the same text in a later round is
|
|
167
|
+
a NEW sub-goal, so never reuse an old `sg-` ID for a fresh item. Sub-goal status
|
|
131
168
|
changes REQUIRE exact ACTIVE `sg-` IDs via `subgoal_updates` — description
|
|
132
|
-
matching is not a fallback there. Unknown, terminal (done/canceled), or
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
169
|
+
matching is not a fallback there. Unknown, terminal (done/canceled), or illegal
|
|
170
|
+
transitions return `evaluation_invalid` before the round advances: fix the
|
|
171
|
+
payload and resubmit the same `roundId` (nothing is committed, no rejection
|
|
172
|
+
counter moves).
|
|
136
173
|
|
|
137
174
|
An optional `round_contract` is a proposal for the next round. It becomes
|
|
138
|
-
active only after the declaring round commits
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
175
|
+
active only after the declaring round commits, and it is then derived from
|
|
176
|
+
committed rounds on every later compile. A contract is items, not prose:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"work_item": "Harden the parser",
|
|
181
|
+
"scope": ["src/parser.ts", "tests/parser.test.ts"],
|
|
182
|
+
"items": [
|
|
183
|
+
{
|
|
184
|
+
"description": "All parser edge cases covered",
|
|
185
|
+
"criterion_refs": ["cr-b5a6c7d8"],
|
|
186
|
+
"subgoal_refs": ["sg-f1e2d3c4"],
|
|
187
|
+
"verify_with": ["unit-tests"]
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Every item must bind at least one configured, enabled, after-capable evidence
|
|
194
|
+
command in `verify_with`, and `scope` entries must stay inside the workspace.
|
|
195
|
+
The runtime derives one `rci-XXXXXXXX` ID per item and an `rc-XXXXXXXX`
|
|
196
|
+
contract ID from the proposal's content only — restating an unchanged contract
|
|
197
|
+
keeps both, so keep citing the IDs the prompt rendered.
|
|
198
|
+
|
|
199
|
+
`subgoal_refs` sits on the ITEM: an item the machine verifies backs exactly the
|
|
200
|
+
sub-goals it names here. That is what produces a derived verified-sub-goal fact
|
|
201
|
+
— and it never changes `SubGoal.status`, which stays your declaration.
|
|
202
|
+
|
|
203
|
+
Item status is machine-derived: `pending` (never claimed met), `insufficient`
|
|
204
|
+
(claimed `met`, not backed), `contradicted` (a bound command failed, or a
|
|
205
|
+
passing command's entrypoint changed that round), `verified` (you claimed `met`
|
|
206
|
+
AND every bound command was observed passing — after phase, entrypoint
|
|
207
|
+
untampered, same command configuration as at declaration). Claim an item by
|
|
208
|
+
citing its `rci-` ID in `execution_report.contract_item_claims`.
|
|
209
|
+
|
|
210
|
+
A claim never creates a verified fact, and machine evidence can only go stale
|
|
211
|
+
in one direction: the LATEST observation at or after your claim decides, so a
|
|
212
|
+
command that passed early and FAILED in the closing round makes the item
|
|
213
|
+
`contradicted` — a pass cannot outlive a later failure. Claimed-but-unbacked is
|
|
214
|
+
`insufficient`: the round still commits and the debt is surfaced, but once the
|
|
215
|
+
debt persists for `engine.unverified_claim_streak_limit` consecutive rounds
|
|
216
|
+
(default 3) the round is rejected; a repeat of the SAME check rejects again with
|
|
217
|
+
an escalation notice, and the third consecutive same-check rejection terminates
|
|
218
|
+
the loop as `incomplete`.
|
|
219
|
+
|
|
220
|
+
The contract closes when every item is `verified`, or when a round reports
|
|
221
|
+
`outcome: "blocked"` with a `blocker`. While it is open, a DIFFERENT proposal is
|
|
222
|
+
ignored (`contract_premature`) — restate the active contract unchanged to
|
|
223
|
+
continue it. Git changes outside the declared `scope` are scope drift, a machine
|
|
224
|
+
fact no explanation waives: revert the out-of-scope files, or close the contract
|
|
225
|
+
with `outcome: "blocked"` and declare the extended scope in the same submission.
|
|
226
|
+
Repeated drift terminates the loop.
|
|
227
|
+
|
|
228
|
+
Declaration and item claims are a strict structural boundary — `contract_invalid`
|
|
229
|
+
with a same-`roundId` retry and zero state change: a contract with no items, more
|
|
230
|
+
items/scope/refs than the declared limits allow (20 items, 20 `criterion_refs`,
|
|
231
|
+
20 `subgoal_refs` and 20 commands per item, 50 `scope` entries), an item with no
|
|
232
|
+
`verify_with`, an item naming an unknown, disabled, or non-after-capable
|
|
233
|
+
command, a `scope` entry that is not a string or that leaves the workspace, a
|
|
234
|
+
malformed `subgoal_refs` entry, a `subgoal_refs` ID that names no sub-goal of
|
|
235
|
+
this loop (including a sub-goal beyond the 50-entry `emerged_subtasks` intake),
|
|
236
|
+
and a malformed, duplicate, or unknown `contract_item_claims` entry.
|
|
237
|
+
`criterion_claims` are advisory and lenient: an entry with an unknown or
|
|
238
|
+
malformed criterion ID, or an outcome other than `met` / `remaining`, is
|
|
239
|
+
dropped with a warning and never rejects the round.
|
|
240
|
+
|
|
241
|
+
The commands `verify_with` may name are the ones your `loop_policy.json`
|
|
242
|
+
declares under `evidence.commands` and enables there; the runtime runs them
|
|
243
|
+
itself, without a shell, inside the workspace. An item whose bound command
|
|
244
|
+
changed configuration since the contract was declared is `insufficient`, not
|
|
245
|
+
`verified` — the declaration stamps the command configuration it was made
|
|
246
|
+
under.
|
|
142
247
|
|
|
143
248
|
## Backtrack
|
|
144
249
|
|
|
145
|
-
A progress stall
|
|
146
|
-
|
|
147
|
-
committed
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
250
|
+
A progress stall can trigger backtrack to the last clean committed round — the
|
|
251
|
+
most recent committed round with no error-level verification flags. The stall
|
|
252
|
+
window is 3 committed rounds (the breaker tier that follows it uses
|
|
253
|
+
`engine.stall_lookback_rounds`, default 3), and the verdict is MACHINE-first:
|
|
254
|
+
when git observations exist for the window, machine motion alone decides it,
|
|
255
|
+
and your `progress_estimate` does not enter — a rising estimate cannot cancel a
|
|
256
|
+
stall, and git motion can only excuse one (it never creates one). Your reported
|
|
257
|
+
estimates are consulted only when the loop has no machine history to read (no
|
|
258
|
+
git provider, or fewer rounds than the window). One machine fact is exempt by
|
|
259
|
+
construction: a round whose success the machine verified is finishing, not
|
|
260
|
+
churning — the closing round of a loop often changes no files at all. So the way
|
|
261
|
+
out of a stall is real, observable work.
|
|
262
|
+
|
|
263
|
+
The backtrack commits a rollback directive that stays out of final history:
|
|
264
|
+
your next submission is the REDO of round `restorePoint + 1` and must reuse the
|
|
265
|
+
round ID the prompt carries; when it commits, the rollback record is replaced
|
|
266
|
+
and disappears from history views.
|
|
152
267
|
|
|
153
268
|
The backtrack prompt opens with a derived **Recovery Brief**:
|
|
154
269
|
|
|
@@ -157,8 +272,9 @@ The backtrack prompt opens with a derived **Recovery Brief**:
|
|
|
157
272
|
- the failed rounds and their approaches — what must NOT be repeated;
|
|
158
273
|
- falsified assumptions — do not rebuild on these;
|
|
159
274
|
- discoveries from the skipped rounds that remain valid;
|
|
160
|
-
- the files to revert and the
|
|
161
|
-
|
|
275
|
+
- the files to revert and the HEAD the workspace must return to. The prompt
|
|
276
|
+
states these as FACTS and never prescribes a command — the restore is yours
|
|
277
|
+
to perform, by whatever means you judge best.
|
|
162
278
|
|
|
163
279
|
The same brief renders in `.loopforge/state/<loopId>-state.md` under Recent
|
|
164
280
|
while the recovery window is open; it is derived from committed rounds plus
|
|
@@ -174,17 +290,15 @@ When backtrack occurs:
|
|
|
174
290
|
4. Keep valid discoveries from skipped rounds in later evaluations.
|
|
175
291
|
|
|
176
292
|
If the workspace is not restored, the next submission is rejected with
|
|
177
|
-
`backtrack_workspace_not_restored`.
|
|
178
|
-
with `outcome: "blocked"` and a blocker while proposing the revised contract
|
|
179
|
-
in the same submission.
|
|
180
|
-
|
|
181
|
-
## Drift
|
|
293
|
+
`backtrack_workspace_not_restored`.
|
|
182
294
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
`
|
|
186
|
-
|
|
187
|
-
|
|
295
|
+
When the rolled-back rounds executed under an ACTIVE contract, the redo follows
|
|
296
|
+
the contract path instead of a plain retry: close the stalled contract in the
|
|
297
|
+
redo submission with `outcome: "blocked"` plus a `blocker` explaining why it
|
|
298
|
+
stalled, and declare the REVISED contract in the same submission. Restating the
|
|
299
|
+
stalled contract unchanged keeps it active and commits more of the same, and a
|
|
300
|
+
different proposal while an old contract is still open is ignored as
|
|
301
|
+
`contract_premature`.
|
|
188
302
|
|
|
189
303
|
## Interpret results
|
|
190
304
|
|
|
@@ -195,27 +309,66 @@ weak clarifications terminate the loop.
|
|
|
195
309
|
the restored prompt and round.
|
|
196
310
|
- `prompt` is null: inspect `stopReason`.
|
|
197
311
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
312
|
+
Each response also carries the posture of the round that just committed:
|
|
313
|
+
`verificationStatus` (`trusted` when everything you claimed is machine-backed,
|
|
314
|
+
`insufficient` when claims are unbacked but nothing is denied, `contradicted`
|
|
315
|
+
when a machine fact denies a claim — it is never a judgement about the quality
|
|
316
|
+
of your work), `roundSuccess`, `level`, and `warnings`. `enforcementReason`
|
|
317
|
+
explains a rejection or termination. Read these before planning the next round:
|
|
318
|
+
an `insufficient` posture is a statement about evidence, not about effort.
|
|
319
|
+
|
|
320
|
+
Errors arrive as `{ok: false, error: {code, message, retryable, ...}}`. `code` is
|
|
321
|
+
stable — branch on it, not on the prose in `message`. `evaluation_invalid`,
|
|
322
|
+
`contract_invalid`, `policy_invalid`, `round_id_required`, `round_id_mismatch`,
|
|
323
|
+
and `invalid_argument` are `retryable`: fix the payload and resubmit the same
|
|
324
|
+
`roundId`. `state_unavailable` means the record you asked for is not there (no
|
|
325
|
+
audit data for that loop, no such gate record), `session_not_found` that the
|
|
326
|
+
session is gone: state conditions, not payload defects — do not resubmit the
|
|
327
|
+
same payload hoping for a different answer. `gate_disabled` means the gate layer
|
|
328
|
+
is off in this policy, and `loop_already_running` that a session for this loop
|
|
329
|
+
already exists in this process — inspect or resume it instead of starting a
|
|
330
|
+
duplicate.
|
|
331
|
+
|
|
332
|
+
`completed` means the machine verified the completion: success plus every
|
|
333
|
+
contract item `verified`, or no active contract. An explicit `outcome:
|
|
334
|
+
"blocked"` (or `stop_reason: "blocked"` / `"needs_human_input"`) always stops as
|
|
335
|
+
`blocked`, never as `completed`. `incomplete` means you stopped while the
|
|
336
|
+
machine could not verify completion — claims the bound commands did not back;
|
|
337
|
+
contract verification debt that persisted past
|
|
338
|
+
`engine.unverified_claim_streak_limit` also ends here rather than accepting
|
|
339
|
+
another unverified round. `failed` means the Agent gave up. `max_rounds` is the
|
|
340
|
+
safety limit. `stalled` means the runtime could not produce the next round's
|
|
341
|
+
prompt. `enforcement_terminated` means repeated integrity failures reached a
|
|
342
|
+
terminal ladder. `paused` means the durable session remains available for
|
|
343
|
+
resume, and `cancelled` that the session was deleted.
|
|
203
344
|
|
|
204
345
|
## Prompt levels and views
|
|
205
346
|
|
|
206
347
|
L0 is a lean same-round retry, L1 is normal continuation, and L2 is full
|
|
207
348
|
rehydration. They control state density only and never prescribe a reasoning
|
|
208
|
-
technique.
|
|
349
|
+
technique. L2 is chosen from FACTS about the round — the first round, a plan or
|
|
350
|
+
checkpoint boundary, a committed recovery, a machine contradiction, a repeated
|
|
351
|
+
rejection, a changed objective, a previous state the runtime can no longer read
|
|
352
|
+
— never from a round counter, so nothing changes in the prompt just because the
|
|
353
|
+
loop has been running for a while. Protected prompt sections and
|
|
354
|
+
the token budget remain enforced; when the protected content alone exceeds the
|
|
355
|
+
ceiling the prompt records `protectedOverflow` rather than dropping it, and
|
|
356
|
+
which sections were rendered is recorded in the prompt artifact.
|
|
209
357
|
|
|
210
358
|
Use `loopforge_status` with `view: "session"` for the live round and typed
|
|
211
|
-
projection, `view: "loop"` for
|
|
212
|
-
restart,
|
|
213
|
-
|
|
214
|
-
|
|
359
|
+
projection, `view: "loop"` for machine counts over committed rounds, `view: "all"`
|
|
360
|
+
after a restart, `view: "audit"` for the read-only completeness and evidence
|
|
361
|
+
audit, and `view: "explain"` (loopId, optional `round`) for the per-round "why"
|
|
362
|
+
view — the active contract, item statuses, observations, and flags. The same
|
|
363
|
+
view is available from the CLI as `loopforge explain LOOP_ID [--round N]
|
|
364
|
+
[--json]`. Use `loopforge_replay` for the committed timeline and round diffs.
|
|
365
|
+
Replay answers what happened; Audit checks whether facts and claims are
|
|
366
|
+
supported; Explain shows why a round was decided the way it was.
|
|
215
367
|
|
|
216
368
|
The optional `.loopforge/state/<loopId>-state.md` file is derived and can be
|
|
217
|
-
regenerated.
|
|
218
|
-
|
|
369
|
+
regenerated. In L2 the prompt may omit the full markdown state and point at
|
|
370
|
+
that file instead (pointer mode), so read it when the prompt says so — but it
|
|
371
|
+
is a derived view, never an independent source of truth.
|
|
219
372
|
|
|
220
373
|
## Control and delegation
|
|
221
374
|
|
|
@@ -224,15 +377,26 @@ Use `loopforge_pause` before an intentional interruption,
|
|
|
224
377
|
for an intentional terminal stop.
|
|
225
378
|
|
|
226
379
|
The two gate tools are opt-in (`policy.gate.enabled`, default false) and are
|
|
227
|
-
hidden from `tools/list` when disabled
|
|
228
|
-
|
|
229
|
-
`
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
380
|
+
hidden from `tools/list` when disabled (a direct call then returns
|
|
381
|
+
`gate_disabled`). When enabled, preflight a high-risk action with
|
|
382
|
+
`loopforge_gate_check`, whose `action` is a structured descriptor:
|
|
383
|
+
|
|
384
|
+
- `description`: what the action does ("Deploy to production");
|
|
385
|
+
- `scope`: files, systems or services it touches;
|
|
386
|
+
- `effects`: any of `workspace_write`, `production`, `credentials`,
|
|
387
|
+
`data_migration`, `public_api`, `publish`, `payment`,
|
|
388
|
+
`external_communication`, `network`;
|
|
389
|
+
- `reversibility`: `reversible` / `recoverable` / `irreversible` / `unknown`;
|
|
390
|
+
- `authorization`: `agent_allowed` / `user_required` / `unknown`.
|
|
391
|
+
|
|
392
|
+
Classification is conservative: anything not provably safe is `user_required`,
|
|
393
|
+
which returns a gate id and an approval question. Present that question to the
|
|
394
|
+
human, record their decision with `loopforge_gate_resolve`, and cite the
|
|
395
|
+
approved gate id via `evaluation.gate_ids` in the round that performs the
|
|
396
|
+
action — an unapproved or unknown cited gate rejects the round. Safe
|
|
397
|
+
preparation work (investigation, dry-runs, rollback evidence) never needs a
|
|
398
|
+
gate. Remember: approval is recorded through you, the Agent — LoopForge cannot
|
|
399
|
+
machine-verify a human is present.
|
|
236
400
|
|
|
237
401
|
Delegation does not create a separate LoopForge mode. Give each worker a
|
|
238
402
|
self-contained subtask and relevant hard constraints, then place its result in
|