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
package/README.md
CHANGED
|
@@ -1,169 +1,171 @@
|
|
|
1
|
-
# LoopForge
|
|
2
|
-
|
|
3
|
-
**A context window is not memory. Memory needs a runtime.**
|
|
4
|
-
|
|
5
|
-
> **Version per `package.json`** — `npm install -g loopforge`. Node.js ≥ 18. Zero runtime dependencies.
|
|
6
|
-
> [中文文档](../README.zh-CN.md)
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## The real problems with long-horizon tasks
|
|
11
|
-
|
|
12
|
-
Any AI coding agent, given a long enough task, hits three walls. Not because the model isn't smart enough. Not because the context window isn't large enough. These are **architectural** problems.
|
|
13
|
-
|
|
14
|
-
### Problem 1: The summary cascade
|
|
15
|
-
|
|
16
|
-
Context fills up → compress. Fills up again → compress again. The third compression is a summary of a summary of a summary. Every cycle evaporates information — critical constraints get silently deleted because the summarizer judged them "no longer relevant." Researchers found that after 3–4 summary cycles, constraint violation rates spike from 0% to 59%. The agent doesn't know what it forgot, because the forgetting itself was compressed away.
|
|
17
|
-
|
|
18
|
-
### Problem 2: Self-correction doesn't work
|
|
19
|
-
|
|
20
|
-
Models cannot reliably improve themselves through introspection — they talk themselves out of correct answers. Effective recovery requires an **external verifier** — an independent observer that doesn't share the agent's context, doesn't participate in its reasoning, and only looks at inputs and outputs.
|
|
21
|
-
|
|
22
|
-
### Problem 3: Compound error (p^N)
|
|
23
|
-
|
|
24
|
-
If each step succeeds with probability p, N sequential steps succeed with probability p^N. Frontier agents nail nearly 100% of tasks under 4 minutes but crater to under 10% on tasks over 4 hours. Small errors at each step become the correct input for the next step.
|
|
25
|
-
|
|
26
|
-
**These three problems amplify each other. The summary cascade loses information → lost information becomes errors → the agent can't self-correct because those errors look reasonable within its own context.**
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## What LoopForge does about it
|
|
31
|
-
|
|
32
|
-
LoopForge runs outside the agent and owns the boundary between rounds. Its
|
|
33
|
-
design has two explicit sources:
|
|
34
|
-
|
|
35
|
-
- **One factual source:** committed typed round documents in the Vault.
|
|
36
|
-
- **One cognitive source:** Canonical State compiled from those facts.
|
|
37
|
-
|
|
38
|
-
`loopforge_next` accepts a structured evaluation. The core fields `success`,
|
|
39
|
-
`output_summary`, `constraint_violations`, and `should_continue` are strict;
|
|
40
|
-
optional fields are normalized. Invalid input returns `evaluation_invalid` and
|
|
41
|
-
can be corrected with the same `roundId` without saving session state, writing
|
|
42
|
-
a round, entering either gate, or changing metrics.
|
|
43
|
-
|
|
44
|
-
Valid submissions pass through evidence collection, a verification gate
|
|
45
|
-
organized into four domains (evaluation consistency, evidence integrity, plan
|
|
46
|
-
& contract, progress & recovery), and an enforcement gate driven by one
|
|
47
|
-
ordered strategy table
|
|
48
|
-
|
|
49
|
-
committed history. The shared internal `CommittedRoundView` gives Replay,
|
|
50
|
-
Audit, Metrics, contracts, the compiler, and gate history one decoder and one
|
|
51
|
-
filtering policy. It does not create another storage format.
|
|
52
|
-
|
|
53
|
-
The compiler evolves Canonical State from committed history, then derives
|
|
54
|
-
focus, todo, phase, delegation, and handoff for prompts and status views. The
|
|
55
|
-
next prompt therefore comes from typed cognitive state, not from a summary of
|
|
56
|
-
the previous prompt.
|
|
57
|
-
|
|
58
|
-
```text
|
|
59
|
-
Traditional: prompt -> summary -> next prompt -> another summary
|
|
60
|
-
LoopForge: committed rounds -> Canonical State -> next prompt
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
The verification gate compares claims with Git snapshots, test output, and
|
|
64
|
-
configured command evidence. The enforcement gate can accept, reject,
|
|
65
|
-
backtrack, or terminate. A rejected submission commits nothing. A stalled-
|
|
66
|
-
progress evaluator can restore the last clean round, inject a diagnosis, and
|
|
67
|
-
verify workspace restoration before work continues.
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## What LoopForge is not
|
|
72
|
-
|
|
73
|
-
- **Not a memory database.** The optional state file is derived. Committed
|
|
74
|
-
rounds hold facts, and Canonical State supplies runtime cognition.
|
|
75
|
-
- **Not a context compressor.** It recompiles prompts from typed state instead
|
|
76
|
-
of compressing the prior prompt.
|
|
77
|
-
- **Not a constraint tracker.** Constraints are one input to external judgment.
|
|
78
|
-
- **Not an agent or unattended executor.** The external agent owns planning,
|
|
79
|
-
code changes, and tool use. LoopForge governs round transitions.
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## Install
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
npm install -g loopforge
|
|
87
|
-
loopforge init --client claude
|
|
88
|
-
claude mcp add loopforge -- npx loopforge mcp
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Works with Claude Code, Codex CLI, or any MCP-compatible client.
|
|
92
|
-
|
|
93
|
-
The MCP server path (`loopforge mcp`) is the primary integration. It provides
|
|
94
|
-
the full cognitive infrastructure: verification gate, enforcement gate,
|
|
95
|
-
backtrack, evidence collection, and crash recovery. The engine is also
|
|
96
|
-
available as a library for custom integrations.
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## Architecture
|
|
101
|
-
|
|
102
|
-
```text
|
|
103
|
-
External agent
|
|
104
|
-
-> structured evaluation
|
|
105
|
-
-> evidence / verification / enforcement
|
|
106
|
-
-> accept, reject, backtrack, or terminate
|
|
107
|
-
reject / terminate: no round commit
|
|
108
|
-
accept / stop / backtrack: commit transaction decision
|
|
109
|
-
-> committed round documents [factual source]
|
|
110
|
-
-> CommittedRoundView [shared read model]
|
|
111
|
-
-> Replay [what happened]
|
|
112
|
-
-> Audit / Metrics [evidence and diagnostics]
|
|
113
|
-
-> Canonical State [cognitive source]
|
|
114
|
-
-> DerivedCognitiveFacts
|
|
115
|
-
-> prompt / state file / status
|
|
116
|
-
-> external agent, next round
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## Key capabilities
|
|
122
|
-
|
|
123
|
-
- Structured evaluation with strict core fields, lenient optional normalization,
|
|
124
|
-
and retryable `evaluation_invalid` responses.
|
|
125
|
-
- One committed-round history for the compiler, contracts, gates, Replay,
|
|
126
|
-
Audit, and Metrics.
|
|
127
|
-
- One Canonical State for prompts, the optional state file, and current-status
|
|
128
|
-
cognition.
|
|
129
|
-
- Evidence-backed accept, reject, backtrack, and terminate decisions, including
|
|
130
|
-
Round Contract and workspace-restore checks. A backtrack carries a derived
|
|
131
|
-
Recovery Brief — trigger rule, restore point, redo round ID, failed
|
|
132
|
-
approaches, and falsified assumptions — sourced from committed rounds plus
|
|
133
|
-
the in-flight attempt (never rejected payloads); the redo reuses the round
|
|
134
|
-
ID and its commit retires the brief.
|
|
135
|
-
- L0, L1, and L2 prompt density, five-state sub-goals with explicit
|
|
136
|
-
`subgoal_updates` transitions (active `sg-` IDs only; done/canceled are
|
|
137
|
-
terminal), constraint lifecycle, milestones, and agent-requested emphasis
|
|
138
|
-
confusion points. Prompts and the state file
|
|
139
|
-
capped by policy; the state file is grouped
|
|
140
|
-
Historical Summary tiers with derived metadata
|
|
141
|
-
and is byte-identical when regenerated from the
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
npm
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
# LoopForge
|
|
2
|
+
|
|
3
|
+
**A context window is not memory. Memory needs a runtime.**
|
|
4
|
+
|
|
5
|
+
> **Version per `package.json`** — `npm install -g loopforge`. Node.js ≥ 18. Zero runtime dependencies.
|
|
6
|
+
> [中文文档](../README.zh-CN.md)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The real problems with long-horizon tasks
|
|
11
|
+
|
|
12
|
+
Any AI coding agent, given a long enough task, hits three walls. Not because the model isn't smart enough. Not because the context window isn't large enough. These are **architectural** problems.
|
|
13
|
+
|
|
14
|
+
### Problem 1: The summary cascade
|
|
15
|
+
|
|
16
|
+
Context fills up → compress. Fills up again → compress again. The third compression is a summary of a summary of a summary. Every cycle evaporates information — critical constraints get silently deleted because the summarizer judged them "no longer relevant." Researchers found that after 3–4 summary cycles, constraint violation rates spike from 0% to 59%. The agent doesn't know what it forgot, because the forgetting itself was compressed away.
|
|
17
|
+
|
|
18
|
+
### Problem 2: Self-correction doesn't work
|
|
19
|
+
|
|
20
|
+
Models cannot reliably improve themselves through introspection — they talk themselves out of correct answers. Effective recovery requires an **external verifier** — an independent observer that doesn't share the agent's context, doesn't participate in its reasoning, and only looks at inputs and outputs.
|
|
21
|
+
|
|
22
|
+
### Problem 3: Compound error (p^N)
|
|
23
|
+
|
|
24
|
+
If each step succeeds with probability p, N sequential steps succeed with probability p^N. Frontier agents nail nearly 100% of tasks under 4 minutes but crater to under 10% on tasks over 4 hours. Small errors at each step become the correct input for the next step.
|
|
25
|
+
|
|
26
|
+
**These three problems amplify each other. The summary cascade loses information → lost information becomes errors → the agent can't self-correct because those errors look reasonable within its own context.**
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## What LoopForge does about it
|
|
31
|
+
|
|
32
|
+
LoopForge runs outside the agent and owns the boundary between rounds. Its
|
|
33
|
+
design has two explicit sources:
|
|
34
|
+
|
|
35
|
+
- **One factual source:** committed typed round documents in the Vault.
|
|
36
|
+
- **One cognitive source:** Canonical State compiled from those facts.
|
|
37
|
+
|
|
38
|
+
`loopforge_next` accepts a structured evaluation. The core fields `success`,
|
|
39
|
+
`output_summary`, `constraint_violations`, and `should_continue` are strict;
|
|
40
|
+
optional fields are normalized. Invalid input returns `evaluation_invalid` and
|
|
41
|
+
can be corrected with the same `roundId` without saving session state, writing
|
|
42
|
+
a round, entering either gate, or changing metrics.
|
|
43
|
+
|
|
44
|
+
Valid submissions pass through evidence collection, a verification gate
|
|
45
|
+
organized into four domains (evaluation consistency, evidence integrity, plan
|
|
46
|
+
& contract, progress & recovery), and an enforcement gate driven by one
|
|
47
|
+
ordered strategy table — the row order is the priority, and each row names
|
|
48
|
+
the check it reacts to plus its escalation ladder. Only an allowed round joins
|
|
49
|
+
committed history. The shared internal `CommittedRoundView` gives Replay,
|
|
50
|
+
Audit, Metrics, contracts, the compiler, and gate history one decoder and one
|
|
51
|
+
filtering policy. It does not create another storage format.
|
|
52
|
+
|
|
53
|
+
The compiler evolves Canonical State from committed history, then derives
|
|
54
|
+
focus, todo, phase, delegation, and handoff for prompts and status views. The
|
|
55
|
+
next prompt therefore comes from typed cognitive state, not from a summary of
|
|
56
|
+
the previous prompt.
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
Traditional: prompt -> summary -> next prompt -> another summary
|
|
60
|
+
LoopForge: committed rounds -> Canonical State -> next prompt
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The verification gate compares claims with Git snapshots, test output, and
|
|
64
|
+
configured command evidence. The enforcement gate can accept, reject,
|
|
65
|
+
backtrack, or terminate. A rejected submission commits nothing. A stalled-
|
|
66
|
+
progress evaluator can restore the last clean round, inject a diagnosis, and
|
|
67
|
+
verify workspace restoration before work continues.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## What LoopForge is not
|
|
72
|
+
|
|
73
|
+
- **Not a memory database.** The optional state file is derived. Committed
|
|
74
|
+
rounds hold facts, and Canonical State supplies runtime cognition.
|
|
75
|
+
- **Not a context compressor.** It recompiles prompts from typed state instead
|
|
76
|
+
of compressing the prior prompt.
|
|
77
|
+
- **Not a constraint tracker.** Constraints are one input to external judgment.
|
|
78
|
+
- **Not an agent or unattended executor.** The external agent owns planning,
|
|
79
|
+
code changes, and tool use. LoopForge governs round transitions.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install -g loopforge
|
|
87
|
+
loopforge init --client claude
|
|
88
|
+
claude mcp add loopforge -- npx loopforge mcp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Works with Claude Code, Codex CLI, or any MCP-compatible client.
|
|
92
|
+
|
|
93
|
+
The MCP server path (`loopforge mcp`) is the primary integration. It provides
|
|
94
|
+
the full cognitive infrastructure: verification gate, enforcement gate,
|
|
95
|
+
backtrack, evidence collection, and crash recovery. The engine is also
|
|
96
|
+
available as a library for custom integrations.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Architecture
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
External agent
|
|
104
|
+
-> structured evaluation
|
|
105
|
+
-> evidence / verification / enforcement
|
|
106
|
+
-> accept, reject, backtrack, or terminate
|
|
107
|
+
reject / terminate: no round commit
|
|
108
|
+
accept / stop / backtrack: commit transaction decision
|
|
109
|
+
-> committed round documents [factual source]
|
|
110
|
+
-> CommittedRoundView [shared read model]
|
|
111
|
+
-> Replay [what happened]
|
|
112
|
+
-> Audit / Metrics [evidence and diagnostics]
|
|
113
|
+
-> Canonical State [cognitive source]
|
|
114
|
+
-> DerivedCognitiveFacts
|
|
115
|
+
-> prompt / state file / status
|
|
116
|
+
-> external agent, next round
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Key capabilities
|
|
122
|
+
|
|
123
|
+
- Structured evaluation with strict core fields, lenient optional normalization,
|
|
124
|
+
and retryable `evaluation_invalid` responses.
|
|
125
|
+
- One committed-round history for the compiler, contracts, gates, Replay,
|
|
126
|
+
Audit, and Metrics.
|
|
127
|
+
- One Canonical State for prompts, the optional state file, and current-status
|
|
128
|
+
cognition.
|
|
129
|
+
- Evidence-backed accept, reject, backtrack, and terminate decisions, including
|
|
130
|
+
Round Contract and workspace-restore checks. A backtrack carries a derived
|
|
131
|
+
Recovery Brief — trigger rule, restore point, redo round ID, failed
|
|
132
|
+
approaches, and falsified assumptions — sourced from committed rounds plus
|
|
133
|
+
the in-flight attempt (never rejected payloads); the redo reuses the round
|
|
134
|
+
ID and its commit retires the brief.
|
|
135
|
+
- L0, L1, and L2 prompt density, five-state sub-goals with explicit
|
|
136
|
+
`subgoal_updates` transitions (active `sg-` IDs only; done/canceled are
|
|
137
|
+
terminal), constraint lifecycle, milestones, and agent-requested emphasis
|
|
138
|
+
(by stable id or exact text) or confusion points. Prompts and the state file
|
|
139
|
+
render active sub-goals only, capped by policy; the state file is grouped
|
|
140
|
+
into Current / Recent / Historical Summary tiers with derived metadata
|
|
141
|
+
(round, attempt, state hash) and is byte-identical when regenerated from the
|
|
142
|
+
Vault — its content depends on committed facts, not on which prompt level
|
|
143
|
+
compiled.
|
|
144
|
+
- Separate Replay and Audit views. Replay answers what happened; Audit checks
|
|
145
|
+
evidence and completeness.
|
|
146
|
+
- Durable sessions with atomic round documents, sequence checks, owned locks,
|
|
147
|
+
renewable leases, pause, resume, and idempotent recovery.
|
|
148
|
+
- Nine MCP tools: `start`, `next`, `status`, `stop`, `pause`, `resume`, `replay`,
|
|
149
|
+
`gate_check`, and `gate_resolve`. The two gate tools are opt-in
|
|
150
|
+
(`policy.gate.enabled`, default false): hidden from `tools/list`, structured
|
|
151
|
+
preflight via `gate_check`, round-blocking on unapproved `gate_ids`
|
|
152
|
+
citations.
|
|
153
|
+
- Zero runtime dependencies. Policy controls thresholds, budgets, and intervals.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm run check
|
|
161
|
+
npm test
|
|
162
|
+
npm pack --dry-run --json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The protocol schema in `../loopforge-protocol.json` is generated from `src/protocol.ts`. Do not edit it by hand.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
package/dist/audit.d.ts
CHANGED
|
@@ -4,21 +4,39 @@
|
|
|
4
4
|
* happened per round); audit is the assessment layer (which claims are
|
|
5
5
|
* machine-verified, which gates were decided, is the sequence intact).
|
|
6
6
|
* Everything is derived from committed vault entries — audit never writes.
|
|
7
|
+
*
|
|
8
|
+
* v3.8: the CONTRACT ITEM axis is the primary completion basis. The criterion
|
|
9
|
+
* claims stay as an advisory second view — a criterion claim is an agent
|
|
10
|
+
* statement whose machine backing is mediated by the contract items, so it
|
|
11
|
+
* cannot be the completion signal. Both axes read the same committed-round
|
|
12
|
+
* model and the same item reducer the coordinator and explain use.
|
|
7
13
|
*/
|
|
8
14
|
import type { LoopStore } from "./loop-store.js";
|
|
9
15
|
import type { VaultEntry } from "./loop-store.js";
|
|
16
|
+
import type { ContractItemStatus } from "./protocol.js";
|
|
10
17
|
export interface AuditRound {
|
|
11
18
|
round: number;
|
|
12
19
|
outcome: string;
|
|
13
20
|
claims: Array<{
|
|
14
21
|
text: string;
|
|
15
|
-
status: "verified" | "unverified"
|
|
22
|
+
status: "verified" | "unverified";
|
|
16
23
|
}>;
|
|
17
24
|
checks: Array<{
|
|
18
25
|
check: string;
|
|
19
26
|
severity: string;
|
|
20
27
|
verdict: "failed" | "warn" | "info";
|
|
21
28
|
}>;
|
|
29
|
+
/** v3.8: the contract this round EXECUTED under and each item's status as of
|
|
30
|
+
* this round — the same derivation `explain` renders. */
|
|
31
|
+
contract: {
|
|
32
|
+
id: string;
|
|
33
|
+
declared_at_round: number;
|
|
34
|
+
closure: string;
|
|
35
|
+
items: Array<{
|
|
36
|
+
itemId: string;
|
|
37
|
+
status: ContractItemStatus;
|
|
38
|
+
}>;
|
|
39
|
+
} | null;
|
|
22
40
|
}
|
|
23
41
|
export interface AuditGate {
|
|
24
42
|
gateId: string;
|
|
@@ -28,12 +46,42 @@ export interface AuditGate {
|
|
|
28
46
|
decidedAt: string;
|
|
29
47
|
round: number;
|
|
30
48
|
}
|
|
49
|
+
/** v3.8: One contract's outcome over the loaned loop, keyed by its content-
|
|
50
|
+
* addressed id. Re-declaring the same contract later extends the same entry —
|
|
51
|
+
* the identity says it IS the same contract. */
|
|
52
|
+
export interface AuditContract {
|
|
53
|
+
contract_id: string;
|
|
54
|
+
declared_at_round: number;
|
|
55
|
+
first_active_round: number;
|
|
56
|
+
last_active_round: number;
|
|
57
|
+
closure: "open" | "verified" | "blocked";
|
|
58
|
+
closed_at_round: number | null;
|
|
59
|
+
/** Item statuses as of the last round the contract was execution-relevant. */
|
|
60
|
+
items: Array<{
|
|
61
|
+
itemId: string;
|
|
62
|
+
description: string;
|
|
63
|
+
status: ContractItemStatus;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
export interface AuditContractSummary {
|
|
67
|
+
contracts: AuditContract[];
|
|
68
|
+
/** Contracts whose final closure is still `open`. */
|
|
69
|
+
open: number;
|
|
70
|
+
verifiedItems: number;
|
|
71
|
+
insufficientItems: number;
|
|
72
|
+
contradictedItems: number;
|
|
73
|
+
pendingItems: number;
|
|
74
|
+
}
|
|
31
75
|
export interface AuditResult {
|
|
32
76
|
loopId: string;
|
|
33
77
|
verdict: "passed" | "incomplete" | "contradicted";
|
|
34
78
|
rounds: AuditRound[];
|
|
35
79
|
gates: AuditGate[];
|
|
36
80
|
unresolvedUserGates: string[];
|
|
81
|
+
/** v3.8: the verification skeleton — the primary completion basis. */
|
|
82
|
+
contracts: AuditContractSummary;
|
|
83
|
+
/** Advisory second axis: criterion claims, not the completion signal.
|
|
84
|
+
* `declared` and `evidenced` are both DISTINCT-id counts, so they compare. */
|
|
37
85
|
criteria: {
|
|
38
86
|
declared: number;
|
|
39
87
|
evidenced: number;
|
|
@@ -42,6 +90,10 @@ export interface AuditResult {
|
|
|
42
90
|
sequenceComplete: boolean;
|
|
43
91
|
/** False when no LoopStore was provided (sequence/provenance degraded). */
|
|
44
92
|
provenanceAvailable: boolean;
|
|
93
|
+
/** v3.8: rounds whose persisted transaction carries a legacy schema
|
|
94
|
+
* version. They are NOT part of history (the version break is hard) — this
|
|
95
|
+
* list makes the loss visible instead of letting the loop look complete. */
|
|
96
|
+
legacyRounds: number[];
|
|
45
97
|
}
|
|
46
98
|
/** Build the audit from committed vault entries. Pure, read-only.
|
|
47
99
|
* @param store Optional LoopStore — when provided, sequence integrity is
|
package/dist/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.js
CHANGED
|
@@ -4,36 +4,63 @@
|
|
|
4
4
|
* happened per round); audit is the assessment layer (which claims are
|
|
5
5
|
* machine-verified, which gates were decided, is the sequence intact).
|
|
6
6
|
* Everything is derived from committed vault entries — audit never writes.
|
|
7
|
+
*
|
|
8
|
+
* v3.8: the CONTRACT ITEM axis is the primary completion basis. The criterion
|
|
9
|
+
* claims stay as an advisory second view — a criterion claim is an agent
|
|
10
|
+
* statement whose machine backing is mediated by the contract items, so it
|
|
11
|
+
* cannot be the completion signal. Both axes read the same committed-round
|
|
12
|
+
* model and the same item reducer the coordinator and explain use.
|
|
7
13
|
*/
|
|
8
14
|
import { eventSequence, StorageCorruptionError } from "./loop-store.js";
|
|
9
15
|
import { auditOrder, deriveGate } from "./cognitive-governance.js";
|
|
10
16
|
import { rederiveClaimViewWithFlags, listVerifiedClaims } from "./evidence-claims.js";
|
|
11
17
|
import { isRecord, entryRound } from "./token-utils.js";
|
|
12
|
-
import {
|
|
18
|
+
import { legacyTransactionRounds, machineEvidenceForRound, readOnlyRounds, } from "./committed-round.js";
|
|
19
|
+
import { deriveRoundContractView } from "./round-contract.js";
|
|
20
|
+
import { getPolicy } from "./policy.js";
|
|
13
21
|
/** Build the audit from committed vault entries. Pure, read-only.
|
|
14
22
|
* @param store Optional LoopStore — when provided, sequence integrity is
|
|
15
23
|
* checked and provenanceAvailable becomes true. */
|
|
16
24
|
export function buildAudit(loopId, entries, store) {
|
|
17
25
|
const ordered = auditOrder(entries);
|
|
26
|
+
// v3.8: the shared committed-round read model — ordering, dedup and rollback
|
|
27
|
+
// exclusion come from it, not from a private rule. v3.8.1: the READ-ONLY
|
|
28
|
+
// window, which additionally applies the session frontier while a committed
|
|
29
|
+
// rollback directive is in effect, so a backtracked branch is not audited as
|
|
30
|
+
// this branch's history.
|
|
31
|
+
const views = readOnlyRounds(entries).filter((view) => view.loopId === loopId);
|
|
32
|
+
const commands = getPolicy().evidence.commands ?? [];
|
|
33
|
+
// The executed contract per round, through the ONE derivation explain and
|
|
34
|
+
// the live coordinator also call. Re-derived per round rather than walked
|
|
35
|
+
// incrementally on purpose: a private incremental walker would be a second
|
|
36
|
+
// history interpretation, which is exactly what this project forbids.
|
|
37
|
+
const contractByRound = new Map();
|
|
38
|
+
for (const view of views) {
|
|
39
|
+
contractByRound.set(view.round, deriveRoundContractView({
|
|
40
|
+
rounds: views,
|
|
41
|
+
round: view.round,
|
|
42
|
+
report: view.executionReport,
|
|
43
|
+
observations: view.observationDelta,
|
|
44
|
+
outcome: view.outcome,
|
|
45
|
+
commands,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
18
48
|
// ── Rounds: per-round claims + checks from committed snapshots ─────────
|
|
49
|
+
//
|
|
50
|
+
// v3.8.1: the list is built from the SAME window the contract axis below
|
|
51
|
+
// reads. Scanning the raw entries here applied a second, weaker history
|
|
52
|
+
// rule — no replacement of a round by a later record of the same logical
|
|
53
|
+
// round — so a superseded record was listed twice and could still flip the
|
|
54
|
+
// verdict to `contradicted`, while explain (which reads only the window)
|
|
55
|
+
// reported a different loop. Rollback exclusion comes from the window too.
|
|
19
56
|
const rounds = [];
|
|
20
|
-
for (const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!committed)
|
|
26
|
-
continue;
|
|
27
|
-
// v2.14: rounds committed with action="backtrack" were rolled back —
|
|
28
|
-
// they are not part of the loop's final history. Counting them would
|
|
29
|
-
// inflate the round list and let their error flags flip the verdict.
|
|
30
|
-
if (committed.action === "backtrack")
|
|
31
|
-
continue;
|
|
32
|
-
const round = entryRound(entry);
|
|
33
|
-
const outcome = committed.outcome ?? "unknown";
|
|
34
|
-
const claimView = committed.evaluation
|
|
35
|
-
? rederiveClaimViewWithFlags(committed.evaluation, machineEvidenceForRound(committed), committed.verificationFlags)
|
|
57
|
+
for (const view of views) {
|
|
58
|
+
const round = view.round;
|
|
59
|
+
const outcome = view.outcome ?? "unknown";
|
|
60
|
+
const claimView = view.evaluation
|
|
61
|
+
? rederiveClaimViewWithFlags(view.evaluation, machineEvidenceForRound(view), view.verificationFlags)
|
|
36
62
|
: null;
|
|
63
|
+
const executed = contractByRound.get(round);
|
|
37
64
|
rounds.push({
|
|
38
65
|
round,
|
|
39
66
|
outcome,
|
|
@@ -43,13 +70,57 @@ export function buildAudit(loopId, entries, store) {
|
|
|
43
70
|
status: claim.status === "verified" ? "verified" : "unverified",
|
|
44
71
|
}))
|
|
45
72
|
: [],
|
|
46
|
-
checks:
|
|
73
|
+
checks: view.verificationFlags.map((flag) => ({
|
|
47
74
|
check: flag.check,
|
|
48
75
|
severity: flag.severity,
|
|
49
76
|
verdict: flag.severity === "error" ? "failed" : flag.severity,
|
|
50
77
|
})),
|
|
78
|
+
contract: executed?.contract && executed.statuses.contractId
|
|
79
|
+
? {
|
|
80
|
+
id: executed.statuses.contractId,
|
|
81
|
+
declared_at_round: executed.contract.declared_at_round,
|
|
82
|
+
closure: executed.statuses.closure,
|
|
83
|
+
items: executed.statuses.items.map((item) => ({
|
|
84
|
+
itemId: item.itemId,
|
|
85
|
+
status: item.status,
|
|
86
|
+
})),
|
|
87
|
+
}
|
|
88
|
+
: null,
|
|
51
89
|
});
|
|
52
90
|
}
|
|
91
|
+
// ── Contract item axis — the verification skeleton ─────────────────────
|
|
92
|
+
const contractsById = new Map();
|
|
93
|
+
for (const view of views) {
|
|
94
|
+
const executed = contractByRound.get(view.round);
|
|
95
|
+
if (!executed?.contract || !executed.statuses.contractId)
|
|
96
|
+
continue;
|
|
97
|
+
const id = executed.statuses.contractId;
|
|
98
|
+
const existing = contractsById.get(id);
|
|
99
|
+
contractsById.set(id, {
|
|
100
|
+
contract_id: id,
|
|
101
|
+
declared_at_round: existing?.declared_at_round ?? executed.contract.declared_at_round,
|
|
102
|
+
first_active_round: existing?.first_active_round ?? view.round,
|
|
103
|
+
last_active_round: view.round,
|
|
104
|
+
closure: executed.statuses.closure,
|
|
105
|
+
closed_at_round: executed.statuses.closed_at_round,
|
|
106
|
+
items: executed.statuses.items.map((item) => ({
|
|
107
|
+
itemId: item.itemId,
|
|
108
|
+
description: item.description,
|
|
109
|
+
status: item.status,
|
|
110
|
+
})),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const contracts = [...contractsById.values()]
|
|
114
|
+
.sort((a, b) => a.first_active_round - b.first_active_round || a.contract_id.localeCompare(b.contract_id));
|
|
115
|
+
const flatItems = contracts.flatMap((contract) => contract.items);
|
|
116
|
+
const contractsSummary = {
|
|
117
|
+
contracts,
|
|
118
|
+
open: contracts.filter((contract) => contract.closure === "open").length,
|
|
119
|
+
verifiedItems: flatItems.filter((item) => item.status === "verified").length,
|
|
120
|
+
insufficientItems: flatItems.filter((item) => item.status === "insufficient").length,
|
|
121
|
+
contradictedItems: flatItems.filter((item) => item.status === "contradicted").length,
|
|
122
|
+
pendingItems: flatItems.filter((item) => item.status === "pending").length,
|
|
123
|
+
};
|
|
53
124
|
// ── Gates: latest decision wins; opened-without-decision stays open ────
|
|
54
125
|
const decisionsByGate = new Map();
|
|
55
126
|
const openedGates = new Map();
|
|
@@ -101,15 +172,26 @@ export function buildAudit(loopId, entries, store) {
|
|
|
101
172
|
throw error;
|
|
102
173
|
}
|
|
103
174
|
}
|
|
104
|
-
// ── Criteria accounting + verdict
|
|
175
|
+
// ── Criteria accounting (advisory second axis) + verdict ───────────────
|
|
176
|
+
//
|
|
177
|
+
// v3.8 fix: `declared` used to be a PER-ROUND SUM while `evidenced` was a
|
|
178
|
+
// DEDUPLICATED count — a criterion claimed in three rounds read as three
|
|
179
|
+
// declared against one evidenced, so `missing` was fiction. Both sides are
|
|
180
|
+
// now distinct-id counts over the same history and compare directly.
|
|
105
181
|
const verified = listVerifiedClaims(entries, loopId);
|
|
106
|
-
const
|
|
182
|
+
const declaredIds = new Set(rounds.flatMap((round) => round.claims.map((claim) => claim.text)));
|
|
183
|
+
const declared = declaredIds.size;
|
|
107
184
|
const evidenced = verified.length;
|
|
185
|
+
// v3.8: the contract item axis decides completion. A contradicted item is a
|
|
186
|
+
// machine denial; an open contract is unfinished verification; the claim
|
|
187
|
+
// axis only speaks when no contract item exists to speak for it.
|
|
108
188
|
let verdict = "passed";
|
|
109
|
-
if (rounds.some((round) => round.checks.some((check) => check.verdict === "failed"))
|
|
189
|
+
if (rounds.some((round) => round.checks.some((check) => check.verdict === "failed")) ||
|
|
190
|
+
contractsSummary.contradictedItems > 0) {
|
|
110
191
|
verdict = "contradicted";
|
|
111
192
|
}
|
|
112
|
-
else if (unresolvedUserGates.length > 0 ||
|
|
193
|
+
else if (unresolvedUserGates.length > 0 || contractsSummary.open > 0 ||
|
|
194
|
+
(declared > 0 && evidenced === 0)) {
|
|
113
195
|
verdict = "incomplete";
|
|
114
196
|
}
|
|
115
197
|
return {
|
|
@@ -118,9 +200,11 @@ export function buildAudit(loopId, entries, store) {
|
|
|
118
200
|
rounds,
|
|
119
201
|
gates,
|
|
120
202
|
unresolvedUserGates,
|
|
203
|
+
contracts: contractsSummary,
|
|
121
204
|
criteria: { declared, evidenced, missing: Math.max(0, declared - evidenced) },
|
|
122
205
|
sequenceComplete,
|
|
123
206
|
provenanceAvailable,
|
|
207
|
+
legacyRounds: legacyTransactionRounds(entries).map((item) => item.round),
|
|
124
208
|
};
|
|
125
209
|
}
|
|
126
210
|
//# sourceMappingURL=audit.js.map
|