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.
Files changed (144) hide show
  1. package/README.md +171 -169
  2. package/dist/audit.d.ts +53 -1
  3. package/dist/audit.d.ts.map +1 -1
  4. package/dist/audit.js +106 -22
  5. package/dist/audit.js.map +1 -1
  6. package/dist/canonical-state.d.ts +52 -67
  7. package/dist/canonical-state.d.ts.map +1 -1
  8. package/dist/canonical-state.js +134 -147
  9. package/dist/canonical-state.js.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +103 -4
  12. package/dist/cli.js.map +1 -1
  13. package/dist/cognitive-facts.d.ts +13 -0
  14. package/dist/cognitive-facts.d.ts.map +1 -1
  15. package/dist/cognitive-facts.js +28 -13
  16. package/dist/cognitive-facts.js.map +1 -1
  17. package/dist/committed-round.d.ts +103 -21
  18. package/dist/committed-round.d.ts.map +1 -1
  19. package/dist/committed-round.js +188 -45
  20. package/dist/committed-round.js.map +1 -1
  21. package/dist/contract-items.d.ts +55 -0
  22. package/dist/contract-items.d.ts.map +1 -0
  23. package/dist/contract-items.js +180 -0
  24. package/dist/contract-items.js.map +1 -0
  25. package/dist/enforcement-gate.d.ts +20 -2
  26. package/dist/enforcement-gate.d.ts.map +1 -1
  27. package/dist/enforcement-gate.js +197 -386
  28. package/dist/enforcement-gate.js.map +1 -1
  29. package/dist/engine.d.ts +1 -1
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +53 -38
  32. package/dist/engine.js.map +1 -1
  33. package/dist/evidence-claims.d.ts +4 -4
  34. package/dist/evidence-claims.d.ts.map +1 -1
  35. package/dist/evidence-claims.js +16 -14
  36. package/dist/evidence-claims.js.map +1 -1
  37. package/dist/evidence-provider.d.ts +85 -89
  38. package/dist/evidence-provider.d.ts.map +1 -1
  39. package/dist/evidence-provider.js +328 -184
  40. package/dist/evidence-provider.js.map +1 -1
  41. package/dist/explain.d.ts +76 -0
  42. package/dist/explain.d.ts.map +1 -0
  43. package/dist/explain.js +127 -0
  44. package/dist/explain.js.map +1 -0
  45. package/dist/generate-schema.js +152 -16
  46. package/dist/generate-schema.js.map +1 -1
  47. package/dist/index.d.ts +6 -6
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +5 -5
  50. package/dist/index.js.map +1 -1
  51. package/dist/loop-compiler.d.ts +40 -53
  52. package/dist/loop-compiler.d.ts.map +1 -1
  53. package/dist/loop-compiler.js +229 -553
  54. package/dist/loop-compiler.js.map +1 -1
  55. package/dist/loop-extras-parser.d.ts +0 -1
  56. package/dist/loop-extras-parser.d.ts.map +1 -1
  57. package/dist/loop-extras-parser.js +0 -4
  58. package/dist/loop-extras-parser.js.map +1 -1
  59. package/dist/loop-projection.d.ts.map +1 -1
  60. package/dist/loop-projection.js +3 -1
  61. package/dist/loop-projection.js.map +1 -1
  62. package/dist/mcp/round-lifecycle.d.ts +30 -21
  63. package/dist/mcp/round-lifecycle.d.ts.map +1 -1
  64. package/dist/mcp/round-lifecycle.js +47 -103
  65. package/dist/mcp/round-lifecycle.js.map +1 -1
  66. package/dist/mcp/server.d.ts.map +1 -1
  67. package/dist/mcp/server.js +51 -7
  68. package/dist/mcp/server.js.map +1 -1
  69. package/dist/mcp/session.d.ts +39 -23
  70. package/dist/mcp/session.d.ts.map +1 -1
  71. package/dist/mcp/session.js +148 -163
  72. package/dist/mcp/session.js.map +1 -1
  73. package/dist/mcp/submission-boundary.d.ts +89 -0
  74. package/dist/mcp/submission-boundary.d.ts.map +1 -0
  75. package/dist/mcp/submission-boundary.js +203 -0
  76. package/dist/mcp/submission-boundary.js.map +1 -0
  77. package/dist/mcp/tools.d.ts.map +1 -1
  78. package/dist/mcp/tools.js +249 -128
  79. package/dist/mcp/tools.js.map +1 -1
  80. package/dist/policy-metrics.js +2 -2
  81. package/dist/policy-metrics.js.map +1 -1
  82. package/dist/policy.d.ts +55 -93
  83. package/dist/policy.d.ts.map +1 -1
  84. package/dist/policy.js +149 -38
  85. package/dist/policy.js.map +1 -1
  86. package/dist/prompt-assembler.d.ts +1 -32
  87. package/dist/prompt-assembler.d.ts.map +1 -1
  88. package/dist/prompt-assembler.js +282 -435
  89. package/dist/prompt-assembler.js.map +1 -1
  90. package/dist/prompt-policy.d.ts +1 -4
  91. package/dist/prompt-policy.d.ts.map +1 -1
  92. package/dist/prompt-policy.js +6 -7
  93. package/dist/prompt-policy.js.map +1 -1
  94. package/dist/protocol.d.ts +365 -142
  95. package/dist/protocol.d.ts.map +1 -1
  96. package/dist/protocol.js +21 -66
  97. package/dist/protocol.js.map +1 -1
  98. package/dist/replay.js +2 -2
  99. package/dist/replay.js.map +1 -1
  100. package/dist/round-contract.d.ts +69 -57
  101. package/dist/round-contract.d.ts.map +1 -1
  102. package/dist/round-contract.js +104 -64
  103. package/dist/round-contract.js.map +1 -1
  104. package/dist/round-coordinator.d.ts +36 -16
  105. package/dist/round-coordinator.d.ts.map +1 -1
  106. package/dist/round-coordinator.js +166 -102
  107. package/dist/round-coordinator.js.map +1 -1
  108. package/dist/round-driver.d.ts +3 -5
  109. package/dist/round-driver.d.ts.map +1 -1
  110. package/dist/round-driver.js +0 -1
  111. package/dist/round-driver.js.map +1 -1
  112. package/dist/round-facts.d.ts +96 -0
  113. package/dist/round-facts.d.ts.map +1 -0
  114. package/dist/round-facts.js +216 -0
  115. package/dist/round-facts.js.map +1 -0
  116. package/dist/round-transaction.d.ts +19 -9
  117. package/dist/round-transaction.d.ts.map +1 -1
  118. package/dist/round-transaction.js +90 -22
  119. package/dist/round-transaction.js.map +1 -1
  120. package/dist/self-eval.d.ts +87 -9
  121. package/dist/self-eval.d.ts.map +1 -1
  122. package/dist/self-eval.js +396 -27
  123. package/dist/self-eval.js.map +1 -1
  124. package/dist/subgoal-state.d.ts +77 -0
  125. package/dist/subgoal-state.d.ts.map +1 -0
  126. package/dist/subgoal-state.js +198 -0
  127. package/dist/subgoal-state.js.map +1 -0
  128. package/dist/token-utils.d.ts +52 -19
  129. package/dist/token-utils.d.ts.map +1 -1
  130. package/dist/token-utils.js +83 -72
  131. package/dist/token-utils.js.map +1 -1
  132. package/dist/verification-gate.d.ts +14 -31
  133. package/dist/verification-gate.d.ts.map +1 -1
  134. package/dist/verification-gate.js +178 -496
  135. package/dist/verification-gate.js.map +1 -1
  136. package/dist/version.d.ts +1 -1
  137. package/dist/version.js +1 -1
  138. package/dist/workspace.d.ts +9 -0
  139. package/dist/workspace.d.ts.map +1 -1
  140. package/dist/workspace.js +17 -0
  141. package/dist/workspace.js.map +1 -1
  142. package/loop_policy.json +6 -30
  143. package/package.json +1 -1
  144. 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 across four action classes (evidence contradiction,
48
- contract & scope, plan drift, progress recovery). 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 or
138
- confusion points. Prompts and the state file render active sub-goals only,
139
- capped by policy; the state file is grouped into Current / Recent /
140
- Historical Summary tiers with derived metadata (round, attempt, state hash)
141
- and is byte-identical when regenerated from the Vault.
142
- - Separate Replay and Audit views. Replay answers what happened; Audit checks
143
- evidence and completeness.
144
- - Durable sessions with atomic round documents, sequence checks, owned locks,
145
- renewable leases, pause, resume, and idempotent recovery.
146
- - Nine MCP tools: `start`, `next`, `status`, `stop`, `pause`, `resume`, `replay`,
147
- `gate_check`, and `gate_resolve`. The two gate tools are opt-in
148
- (`policy.gate.enabled`, default false): hidden from `tools/list`, structured
149
- preflight via `gate_check`, round-blocking on unapproved `gate_ids`
150
- citations.
151
- - Zero runtime dependencies. Policy controls thresholds, budgets, and intervals.
152
-
153
- ---
154
-
155
- ## Development
156
-
157
- ```bash
158
- npm run check
159
- npm test
160
- npm pack --dry-run --json
161
- ```
162
-
163
- The protocol schema in `../loopforge-protocol.json` is generated from `src/protocol.ts`. Do not edit it by hand.
164
-
165
- ---
166
-
167
- ## License
168
-
169
- MIT
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" | "no_evidence";
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
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIlD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,aAAa,CAAA;KAAE,CAAC,CAAC;IACnF,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,GAAG,YAAY,GAAG,cAAc,CAAC;IAClD,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,2EAA2E;IAC3E,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;oDAEoD;AACpD,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,EAAE,EACrB,KAAK,CAAC,EAAE,SAAS,GAChB,WAAW,CA8Gb"}
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 { decodeCommittedRound, machineEvidenceForRound } from "./committed-round.js";
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 entry of ordered) {
21
- const taskId = String(entry.task_id ?? "");
22
- if (!taskId.startsWith(`loop:${loopId}:r`) || !taskId.endsWith(":feedback"))
23
- continue;
24
- const committed = decodeCommittedRound(entry);
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: committed.verificationFlags.map((flag) => ({
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 declared = rounds.reduce((sum, round) => sum + round.claims.length, 0);
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 || (declared > 0 && evidenced === 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