@jmtrin/opencode-kevin 0.5.0 → 0.7.0

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 (90) hide show
  1. package/README.md +268 -122
  2. package/dist/migrations/001_initial.sql +91 -91
  3. package/dist/migrations/003_v02_signal.sql +57 -57
  4. package/dist/migrations/004_v03_knowledge.sql +138 -138
  5. package/dist/migrations/005_v04_signal.sql +57 -57
  6. package/dist/migrations/006_v05_glassbox.sql +118 -118
  7. package/dist/migrations/007_v06_pull.sql +145 -0
  8. package/dist/migrations/008_v07_truth.sql +124 -0
  9. package/dist/plugin/Archiver.js +4 -3
  10. package/dist/plugin/Archiver.js.map +1 -1
  11. package/dist/plugin/ArtifactWriter.d.ts +50 -0
  12. package/dist/plugin/ArtifactWriter.js +240 -0
  13. package/dist/plugin/ArtifactWriter.js.map +1 -0
  14. package/dist/plugin/ConflictDetector.d.ts +35 -0
  15. package/dist/plugin/ConflictDetector.js +283 -0
  16. package/dist/plugin/ConflictDetector.js.map +1 -0
  17. package/dist/plugin/ContextInjector.d.ts +15 -0
  18. package/dist/plugin/ContextInjector.js +46 -11
  19. package/dist/plugin/ContextInjector.js.map +1 -1
  20. package/dist/plugin/ConventionMiner.d.ts +35 -0
  21. package/dist/plugin/ConventionMiner.js +243 -0
  22. package/dist/plugin/ConventionMiner.js.map +1 -0
  23. package/dist/plugin/Curator.d.ts +96 -0
  24. package/dist/plugin/Curator.js +283 -0
  25. package/dist/plugin/Curator.js.map +1 -0
  26. package/dist/plugin/Feedback.js +4 -3
  27. package/dist/plugin/Feedback.js.map +1 -1
  28. package/dist/plugin/Materializer.d.ts +59 -0
  29. package/dist/plugin/Materializer.js +237 -0
  30. package/dist/plugin/Materializer.js.map +1 -0
  31. package/dist/plugin/MemoryService.d.ts +37 -0
  32. package/dist/plugin/MemoryService.js +241 -20
  33. package/dist/plugin/MemoryService.js.map +1 -1
  34. package/dist/plugin/Migrate.js +51 -2
  35. package/dist/plugin/Migrate.js.map +1 -1
  36. package/dist/plugin/QualityGate.d.ts +15 -1
  37. package/dist/plugin/QualityGate.js +12 -0
  38. package/dist/plugin/QualityGate.js.map +1 -1
  39. package/dist/plugin/Reflector.js +13 -0
  40. package/dist/plugin/Reflector.js.map +1 -1
  41. package/dist/plugin/RepoTruth.d.ts +80 -0
  42. package/dist/plugin/RepoTruth.js +600 -0
  43. package/dist/plugin/RepoTruth.js.map +1 -0
  44. package/dist/plugin/Retrospective.d.ts +1 -0
  45. package/dist/plugin/Retrospective.js +20 -1
  46. package/dist/plugin/Retrospective.js.map +1 -1
  47. package/dist/plugin/capabilities.d.ts +15 -0
  48. package/dist/plugin/capabilities.js +42 -0
  49. package/dist/plugin/capabilities.js.map +1 -0
  50. package/dist/plugin/diff.d.ts +8 -0
  51. package/dist/plugin/diff.js +183 -0
  52. package/dist/plugin/diff.js.map +1 -0
  53. package/dist/plugin/index.d.ts +8 -1
  54. package/dist/plugin/index.js +354 -2
  55. package/dist/plugin/index.js.map +1 -1
  56. package/dist/plugin/inferability.d.ts +32 -0
  57. package/dist/plugin/inferability.js +89 -0
  58. package/dist/plugin/inferability.js.map +1 -0
  59. package/dist/plugin/kevin_approve.d.ts +34 -0
  60. package/dist/plugin/kevin_approve.js +50 -0
  61. package/dist/plugin/kevin_approve.js.map +1 -0
  62. package/dist/plugin/kevin_audit.d.ts +87 -1
  63. package/dist/plugin/kevin_audit.js +190 -3
  64. package/dist/plugin/kevin_audit.js.map +1 -1
  65. package/dist/plugin/kevin_conflicts.d.ts +9 -0
  66. package/dist/plugin/kevin_conflicts.js +51 -0
  67. package/dist/plugin/kevin_conflicts.js.map +1 -0
  68. package/dist/plugin/kevin_facts.d.ts +42 -0
  69. package/dist/plugin/kevin_facts.js +37 -0
  70. package/dist/plugin/kevin_facts.js.map +1 -0
  71. package/dist/plugin/kevin_propose.d.ts +23 -0
  72. package/dist/plugin/kevin_propose.js +15 -0
  73. package/dist/plugin/kevin_propose.js.map +1 -0
  74. package/dist/plugin/kevin_publish.d.ts +38 -0
  75. package/dist/plugin/kevin_publish.js +19 -0
  76. package/dist/plugin/kevin_publish.js.map +1 -0
  77. package/dist/plugin/metrics.d.ts +12 -1
  78. package/dist/plugin/metrics.js +49 -0
  79. package/dist/plugin/metrics.js.map +1 -1
  80. package/dist/plugin/replay-types.d.ts +8 -8
  81. package/dist/plugin/replay.js +10 -0
  82. package/dist/plugin/replay.js.map +1 -1
  83. package/migrations/001_initial.sql +91 -91
  84. package/migrations/003_v02_signal.sql +57 -57
  85. package/migrations/004_v03_knowledge.sql +138 -138
  86. package/migrations/005_v04_signal.sql +57 -57
  87. package/migrations/006_v05_glassbox.sql +118 -118
  88. package/migrations/007_v06_pull.sql +145 -0
  89. package/migrations/008_v07_truth.sql +124 -0
  90. package/package.json +3 -2
package/README.md CHANGED
@@ -6,14 +6,30 @@ Kevin is an [OpenCode](https://opencode.ai) plugin that **observes** every agent
6
6
 
7
7
  - **Local-first**: SQLite + FTS5, no external services, no network calls.
8
8
  - **Global memory**: a single `~/.opencode-kevin/kevin.db` shared across all your projects (WAL mode → safe for concurrent sessions). No per-project folders.
9
- - **Knowledge + Causality (v0.3.0)**: causal failure→fix chains, `kevin_why` explanations, OKF export/import, a supersede model, and human-in-the-loop AGENTS.md suggestions.
10
- - **Signal over Noise (v0.4.0)**: a quality gate that stores weak lessons without injecting them, an injection ledger with honest `precision_rate`, two-sided confidence, and a fixed compacting hook.
11
- - **Glass Box (v0.5.0)**: honest measurement replaces estimates — three-way injection settlement (`effective` / `ineffective` / `inconclusive`), human feedback that actually moves confidence, a strict dry-run `kevin_trace`, a read-only `kevin_audit`, memory lifecycle completion (`superseded_by`, `ignored`, `archived`), and a hermetic replay harness. See [How Kevin measures itself](#how-kevin-measures-itself).
12
- - **Audited**: the v0.4.0 bug catalog (`docs/Kevin_v0.4.0_Bugs.md`) is fully closed 16/16 bugs fixed and regression-tested (evidence in `kevin_query`/`kevin_get`, OKF round-trip fidelity, causal refresh guard, redaction precision, cross-session isolation).
9
+ - **Knowledge + Causality**: causal failure→fix chains, `kevin_why` explanations, OKF export/import, a supersede model, and human-in-the-loop AGENTS.md suggestions.
10
+ - **Signal over Noise**: a quality gate that stores weak lessons without injecting them, an injection ledger with honest `precision_rate`, and two-sided confidence.
11
+ - **Glass Box**: honest measurement replaces estimates — three-way injection settlement (`effective` / `ineffective` / `inconclusive`), human feedback that actually moves confidence, a strict dry-run `kevin_trace`, a read-only `kevin_audit`, and a hermetic replay harness.
12
+ - **Pull**: knowledge earns its way into files the model actually reads `kevin_propose` generates a reviewable diff, a human approves, and **only then** does Kevin write, inside a frozen marker block, preserving your file's CRLF/BOM/formatting byte-for-byte outside it. Plus three distribution channels (AGENTS.md, skills, references) and a push budget gated by a confidence floor.
13
+ - **Audited**: the v0.4.0 bug catalog (`docs/Kevin_v0.4.0_Bugs.md`) is fully closed — 16/16 bugs fixed and regression-tested.
13
14
  - **Standalone**: works without any other plugin. With the ecosystem, it learns more richly.
14
15
 
15
16
  ---
16
17
 
18
+ ## Contents
19
+
20
+ - [Installation](#installation)
21
+ - [How Kevin works](#how-kevin-works)
22
+ - [Tools](#tools)
23
+ - [How Kevin measures itself](#how-kevin-measures-itself)
24
+ - [Curation & Pull](#curation--pull)
25
+ - [Replay harness](#replay-harness)
26
+ - [Hooks](#hooks)
27
+ - [Configuration](#configuration)
28
+ - [Development](#development)
29
+ - [License](#license)
30
+
31
+ ---
32
+
17
33
  ## Installation
18
34
 
19
35
  ### 1. Declare the plugin
@@ -34,7 +50,9 @@ For a **single project**, put the same `plugin` array in `./opencode.json` or `.
34
50
 
35
51
  ### 2. Restart OpenCode
36
52
 
37
- Config is loaded once at startup and is **not hot-reloaded** — quit and reopen OpenCode after editing. On start, OpenCode resolves the npm spec, caches the plugin in `~/.cache/opencode/packages/@jmtrin/opencode-kevin/`, and exposes thirteen tools: `kevin_save`, `kevin_query`, `kevin_get`, `kevin_recall`, `kevin_status`, `kevin_retrospective`, `kevin_why`, `kevin_export`, `kevin_import`, `kevin_config`, `kevin_feedback`, `kevin_trace`, `kevin_audit` (v0.5.0 adds the last three).
53
+ Config is loaded once at startup and is **not hot-reloaded** — quit and reopen OpenCode after editing. On start, Kevin exposes 18 tools, including `kevin_facts` and `kevin_conflicts`.
54
+
55
+ Contradictions de-rank memories and surface conflicts. They never delete, stale, archive, or auto-resolve a memory.
38
56
 
39
57
  ### 3. Where data lives
40
58
 
@@ -85,60 +103,48 @@ Use `:memory:` for `dbPath` in tests.
85
103
 
86
104
  ---
87
105
 
88
- ## Cycle: Observe → Learn → Share
106
+ ## How Kevin works
107
+
108
+ Every tool call is observed; every failure becomes a lesson; every lesson is either pushed into the next prompt, written into an artifact a human approved, or retired when it stops earning its place.
89
109
 
90
110
  ```
91
111
  Tool call (success or failure)
92
112
 
93
113
 
94
- ┌─────────────────┐
95
- OBSERVE ToolCallObserver records every call
96
- ToolCallObserver│ (tool, args redacted, success, duration, error_type,
97
- │ id = callID) + stripPrivate + opt-in dedup (v0.2.0)
98
- └────────┬────────┘
99
- │ on failure │ on success
100
- ▼ ▼
101
- ┌─────────────────┐ ┌─────────────────────────┐
102
- LEARN CAUSAL CHAIN (v0.3.0)
103
- │ Reflector│ CausalChain.onSuccess │
104
- │ heuristic lesson │ links the fix to the
105
- │ per-error-code │ failure within 10 tool │
106
- rule table │ │ calls (error_fingerprint)
107
- │ (v0.2.0 v2), │ └───────────┬─────────────┘
108
- per-fingerprint
109
- throttle BEFORE │ session.idle
110
- LLM enrich
111
- (opt-in v0.3.0),│ ┌─────────────────────────┐
112
- stamps │CausalChain.onSessionIdle│
113
- │ error_fingerprint│ │ promotes recurring errors│
114
- └────────┬────────┘ causal patterns (idempotent,
115
- │ │ cumulative evidence) │
116
- ▼ └───────────┬───────────────┘
117
- ┌─────────────────┐
118
- │ SHARE │◄──────────────┘
119
- │ ContextInjector │ injects relevant lessons pre-prompt
120
- │ │ (900 tokens, configurable via pre_prompt_budget_tokens) + on compacting (2000)
121
- │ │ + <protect> + id: line (v0.2.0)
122
- │ │ + origin-aware rank (v0.2.0)
123
- │ │ + <kevin-suggestion> after negative
124
- │ │ feedback half (HITL, v0.3.0)
125
- └────────┬────────┘
126
- │ session.idle
127
-
128
- ┌─────────────────┐
129
- │ RETROSPECTIVE │ generates ~/.opencode-kevin/retrospectives/<session>.md
130
- │ │ with origin labels, FP recap, metrics snapshot (v0.2.0)
131
- │ │ + boostPositiveReflectors (v0.2.0)
132
- │ │ + penalizeRecurringReflectors (v0.3.0)
133
- │ │ + PatternMiner.mine (opt-in, v0.2.0)
134
- └─────────────────┘
135
- ```
114
+ ┌─────────────────────────┐ OBSERVE
115
+ ToolCallObserver │ records every call (tool, redacted args,
116
+ └───────────┬─────────────┘ success, duration, error type, dedup)
117
+
118
+ failure │ success
119
+ ┌──────────▼───────────┐ ┌─────────────────────────┐
120
+ │ Reflector │ │ CausalChain │
121
+ heuristic lesson │ │ links the fix to the │
122
+ per error code │ │ failure within 10 calls
123
+ (throttled per└────────────┬────────────┘
124
+ fingerprint)
125
+ └──────────┬───────────┘ session.idle
126
+
127
+ ▼ ┌─────────────────────────┐
128
+ ┌───────────────────────┐ promotes recurring
129
+ ContextInjector │◄─┤ errors causal
130
+ SHARE: injects │ patterns (cumulative │
131
+ <kevin-context> │ │ evidence)
132
+ ≤400 tokens/prompt└─────────────────────────┘
133
+ └───────────┬───────────┘
134
+ session.idle
135
+
136
+ ┌─────────────────────────┐ RETROSPECTIVE: <session>.md with
137
+ Retrospective │ lessons, metrics snapshot, causal
138
+ └─────────────────────────┘ promotion, pattern mining (opt-in)
139
+ ```
140
+
141
+ At `session.idle` Kevin also settles injection outcomes, retires stale memories, and — when curation is enabled — drafts pull proposals for your review (see [Curation & Pull](#curation--pull)).
136
142
 
137
143
  ---
138
144
 
139
145
  ## Tools
140
146
 
141
- Kevin exposes 13 tools callable by the agent:
147
+ Kevin exposes 16 tools callable by the agent.
142
148
 
143
149
  ### `kevin_save`
144
150
 
@@ -149,25 +155,23 @@ kevin_save({ type: "decision", content: "We use vitest for tests", scope: "proje
149
155
  // → { "id": "0195a3b2-..." }
150
156
  ```
151
157
 
152
- `type`: `error` | `pattern` | `decision` | `context` | `rule` | `solution` (v0.3.0). `scope`: `project` (persists) | `session` (TTL 24h).
158
+ `type`: `error` | `pattern` | `decision` | `context` | `rule` | `solution`. `scope`: `project` (persists) | `session` (TTL 24h).
153
159
 
154
- Saving a `decision` or `rule` with the same `fingerprint` as an existing active row supersedes the old one (v0.3.0 — `status='superseded'`, hidden from default queries).
160
+ Saving a `decision` or `rule` with the same `fingerprint` as an existing active row supersedes the old one (`status='superseded'`, hidden from default queries).
155
161
 
156
162
  ### `kevin_query`
157
163
 
158
- Searches memories by text (FTS5 + bm25). Returns a **slim** payload by default (v0.2.0). Pass `full: true` for the v0.1.x full content body, or `evidence: true` (v0.3.0) to include `confidence`, `evidence_count`, `last_verified_at` in the slim payload.
164
+ Searches memories by text (FTS5 + bm25). Returns a **slim** payload by default; pass `full: true` for the complete content, or `evidence: true` to include `confidence`, `evidence_count` and `last_verified_at`.
159
165
 
160
166
  ```
161
167
  kevin_query({ query: "typecheck", type: "error", limit: 5 })
162
- // → [{ "id": "...", "type": "error", "scope": "project", "score": -0.87, "snippet": "When bash fails with typecheck:..." }, ...]
163
-
164
- kevin_query({ query: "typecheck", type: "error", limit: 5, full: true })
165
- // → [{ "id": "...", "type": "error", "content": "...", "scope": "project" }, ...]
168
+ // → [{ "id": "...", "type": "error", "scope": "project", "score": -0.87,
169
+ // "snippet": "When bash fails with typecheck:..." }, ...]
166
170
  ```
167
171
 
168
172
  ### `kevin_get`
169
173
 
170
- Fetches a **single full memory** by id (v0.2.0 — progressive disclosure). Use when `kevin_query` returns a slim snippet and you need the complete content.
174
+ Fetches a **single full memory** by id (progressive disclosure) use it when `kevin_query` returned a slim snippet and you need the complete content.
171
175
 
172
176
  ```
173
177
  kevin_get({ id: "0195a3b2-..." })
@@ -180,7 +184,7 @@ kevin_get({ id: "0195a3b2-..." })
180
184
 
181
185
  ### `kevin_recall`
182
186
 
183
- Retrieves relevant memories (greedy fill by relevance). Without `query`, returns all memories in scope. Pass `includeSuperseded: true` to include superseded rows (v0.3.0).
187
+ Retrieves relevant memories (greedy fill by relevance). Without `query`, returns all memories in scope. Pass `includeSuperseded: true` to include superseded rows.
184
188
 
185
189
  ```
186
190
  kevin_recall({ query: "auth", limit: 3 })
@@ -189,28 +193,33 @@ kevin_recall({ query: "auth", limit: 3 })
189
193
 
190
194
  ### `kevin_status`
191
195
 
192
- Global counts and metrics. v0.2.0 adds `memories_reflector`, `memories_agent`, `memories_pattern` and a `metrics` object; v0.3.0 adds `memories_causal` and 3 more seeded counters (`patterns_causal`, `causal_links`, `memories_superseded`); v0.4.0 adds the precision block: `injections_total`, `injections_effective`, `injections_ineffective`, `precision_rate`, `patterns_promoted_new`, and per-origin `recurrence_by_origin`. v0.5.0 adds `injections_inconclusive`, `coverage_rate`, `blocked`, `memories_ignored`, `memories_archived` and `feedback { positive, negative }`.
196
+ Global counts and metrics: memory census, the precision block, the six blocked-gate counters, feedback totals, and the v0.6 block (`schema_version`, `curation_enabled`, emission states, `proposals_pending` omitted on pre-007 databases).
193
197
 
194
198
  ```
195
199
  kevin_status({})
196
- // → { "memories": 42, "memories_reflector": 12, "memories_agent": 30, "memories_pattern": 0,
197
- // "memories_causal": 1, "tool_calls": 318, "retrospectives": 7,
200
+ // → { "memories": 42, "memories_reflector": 12, "memories_agent": 30,
201
+ // "memories_pattern": 0, "memories_causal": 1, "tool_calls": 318,
202
+ // "retrospectives": 7, "tool_count": 16,
198
203
  // "metrics": { "tokens_injected_pre_prompt": 51, "tokens_injected_compacting": 0,
199
204
  // "reflections_throttled": 3, "duplicate_suppressions": 2,
200
205
  // "tool_calls_deduped": 0, "patterns_mined": 0,
201
206
  // "patterns_causal": 1, "causal_links": 2, "memories_superseded": 0,
202
- // "injections_inconclusive": 9, "injections_blocked_seen": 1, ... },
207
+ // "injections_inconclusive": 9, ... },
203
208
  // "injections_total": 14, "injections_effective": 2, "injections_ineffective": 3,
204
209
  // "injections_inconclusive": 9, "precision_rate": 0.40, "coverage_rate": 0.36,
205
- // "blocked": { "seen": 1, "weak": 0, "recurrence": 2, "stale": 0, "ignored": 1 },
210
+ // "blocked": { "seen": 1, "weak": 0, "recurrence": 2, "stale": 0,
211
+ // "ignored": 1, "confidence": 2 },
206
212
  // "memories_ignored": 1, "memories_archived": 4,
207
213
  // "feedback": { "positive": 2, "negative": 1 },
208
- // "patterns_promoted_new": 2, "recurrence_by_origin": { "reflector": 3, "causal": 1 } }
214
+ // "patterns_promoted_new": 2, "recurrence_by_origin": { "reflector": 3, "causal": 1 },
215
+ // "v06": { "schema_version": "007", "curation_enabled": "1",
216
+ // "skill_emission": "off", "reference_emission": "off",
217
+ // "proposals_pending": 2 } }
209
218
  ```
210
219
 
211
220
  ### `kevin_retrospective`
212
221
 
213
- Generates a retrospective for a session (uses current session if `session_id` is omitted).
222
+ Generates a retrospective for a session (uses the current session if `session_id` is omitted).
214
223
 
215
224
  ```
216
225
  kevin_retrospective({ session_id: "sess-abc" })
@@ -218,7 +227,7 @@ kevin_retrospective({ session_id: "sess-abc" })
218
227
  // or → { "message": "No failures in session sess-abc." }
219
228
  ```
220
229
 
221
- ### `kevin_why` (v0.3.0)
230
+ ### `kevin_why`
222
231
 
223
232
  Explains *why* a failure keeps happening: looks up causal patterns for the query and builds a failure → fix trace from memories + tool_calls, including related TypeScript error-code rules.
224
233
 
@@ -230,29 +239,31 @@ kevin_why({ query: "TS2304 cannot find name" })
230
239
  // "related_rules": [ { "code": "TS2304", "suggestion": "import or typo" } ] }
231
240
  ```
232
241
 
233
- ### `kevin_export` (v0.3.0)
242
+ ### `kevin_export`
234
243
 
235
- Exports knowledge for sharing: `decision`/`rule`/`pattern` memories (active only, no raw errors) as YAML-frontmatter blocks (`format: "okf"`) or markdown (`format: "markdown"`). Includes `id`, `type`, `confidence` (two-sided v0.4.0 formula), `evidence_count`, `recurrence_count`, `last_verified_at`, `fingerprint`. Timestamps are treated as UTC — a re-import reproduces the exact source values.
244
+ Exports knowledge for sharing: `decision`/`rule`/`pattern` memories (active only, no raw errors) as YAML-frontmatter blocks (`format: "okf"`) or markdown (`format: "markdown"`). Includes `id`, `type`, `confidence`, `evidence_count`, `recurrence_count`, `last_verified_at`, `fingerprint`. Timestamps are treated as UTC — a re-import reproduces the exact source values.
236
245
 
237
- ### `kevin_import` (v0.3.0)
246
+ ### `kevin_import`
238
247
 
239
248
  Ingests an exported bundle. Each entry becomes a `context` memory with `origin='imported'`; a fingerprint collision with an existing `decision`/`rule` supersedes the old row. Returns `{ imported, superseded }`.
240
249
 
241
- ### `kevin_config` (v0.4.0)
250
+ ### `kevin_config`
242
251
 
243
252
  Reads/writes `kevin_settings` without SQL. `action: "list"` returns every setting; `action: "set"` upserts a value (default `"1"` when omitted) and rejects unknown keys unless `strict: false`.
244
253
 
245
254
  ```
246
255
  kevin_config({ action: "list" })
247
- // → { "quality_gate_enabled": "1", "lesson_snippet_injection": "1", "llm_reflection_enabled": "0", ... }
256
+ // → { "quality_gate_enabled": "1", "lesson_snippet_injection": "1",
257
+ // "llm_reflection_enabled": "0", "pre_prompt_budget_tokens": "400",
258
+ // "injection_confidence_floor": "0.6", ... }
248
259
 
249
260
  kevin_config({ action: "set", key: "quality_gate_enabled", value: "0" })
250
- // → { "ok": true }
261
+ // → { "ok": true, "key": "quality_gate_enabled", "value": "0" }
251
262
  ```
252
263
 
253
- Known keys: `quality_gate_enabled`, `lesson_snippet_injection`, `llm_reflection_enabled`, `cross_project_enabled`, `patternminer_enabled`, `tool_calls_dedup_enabled` (v0.4.0), `deterministic_retrieval`, `pre_prompt_budget_tokens`, `archive_after_days` (v0.5.0).
264
+ All settings and their defaults are listed in [Configuration](#configuration).
254
265
 
255
- ### `kevin_feedback` (v0.5.0)
266
+ ### `kevin_feedback`
256
267
 
257
268
  Rates an injected memory and makes the rating count. `verdict` is `useful` | `wrong` | `outdated` | `ignore`. The first three are stored in `memory_feedback` and move `kevin_why`'s confidence (`+0.05` / `-0.1` per count); **`ignore` is a hard action** — the memory is stamped `ignored = 1` and excluded from retrieval, queries and injection.
258
269
 
@@ -261,41 +272,79 @@ kevin_feedback({ memory_id: "0195a3b2-...", verdict: "wrong", note: "the fix was
261
272
  // → { "ok": true, "verdict": "wrong" }
262
273
  ```
263
274
 
264
- ### `kevin_trace` (v0.5.0)
275
+ ### `kevin_trace`
265
276
 
266
- Strict dry-run: predicts exactly which memories `onSystemTransform` WOULD inject for a query (optionally `session_id`, `tag` and `cap`), with **zero side effects** — no counters, no ledger rows, no seen-set writes, no relevance bumps. Rejected items carry their `GateReason` (`seen_this_session` | `weak` | `recurrence` | `stale` | `ignored`).
277
+ Strict dry-run: predicts exactly which memories `onSystemTransform` WOULD inject for a query (optionally `session_id`, `tag` and `cap`), with **zero side effects** — no counters, no ledger rows, no seen-set writes, no relevance bumps. Rejected items carry their `GateReason` (`seen_this_session` | `weak` | `recurrence` | `stale` | `ignored` | `confidence`).
267
278
 
268
279
  ```
269
280
  kevin_trace({ query: "tsc error" })
270
- // → { "query": "tsc error", "tag": "context", "cap": 900, "would_inject": true,
281
+ // → { "query": "tsc error", "tag": "context", "cap": 400, "would_inject": true,
271
282
  // "total_tokens": 82,
272
283
  // "admitted": [ { "id": "...", "type": "error", "decision": "admitted", "tokens": 62 } ],
273
- // "blocked": [ { "id": "...", "type": "error", "decision": "blocked", "reason": "seen_this_session", "tokens": 20 } ] }
284
+ // "blocked": [ { "id": "...", "type": "error", "decision": "blocked",
285
+ // "reason": "confidence", "tokens": 20 } ] }
274
286
  ```
275
287
 
276
- ### `kevin_audit` (v0.5.0)
288
+ ### `kevin_audit`
277
289
 
278
- Read-only report of the whole system state: memories by `status`/`origin`/`type`, injection outcomes with `precision_rate`/`coverage_rate`, the five `blocked` counters, feedback by verdict, tokens injected. `verbose: true` adds the settings block. No writes, no LLM; on a pre-006 database it returns what it can with `"partial": true`.
290
+ Read-only report of the whole system state: memories by `status`/`origin`/`type`, injection outcomes with `precision_rate`/`coverage_rate`, the six `blocked` counters, feedback by verdict, tokens injected, the push-vs-pull `channels` comparison and the `curation` scoreboard. `verbose: true` adds the settings block. No writes, no LLM; on pre-007 databases it omits the v0.6 blocks and reports `"partial": true`.
279
291
 
280
292
  ```
281
293
  kevin_audit({})
282
294
  // → { "memories": { "total": 42, "by_status": { "active": 37, "stale": 1, "archived": 4 },
283
295
  // "by_origin": { "reflector": 12, "agent": 30 }, "by_type": { "error": 20, ... },
284
- // "ignored": 1, "archived": 4, "with_feedback": 3, "superseded_with_target": 2 },
296
+ // "ignored": 1, "with_feedback": 3 },
285
297
  // "injections": { "total": 14, "effective": 2, "ineffective": 3, "inconclusive": 9,
286
298
  // "unmeasured": 0, "precision_rate": 0.40, "coverage_rate": 0.36 },
287
- // "blocked": { "seen": 1, "weak": 0, "recurrence": 2, "stale": 0, "ignored": 1 },
299
+ // "blocked": { "seen": 1, "weak": 0, "recurrence": 2, "stale": 0,
300
+ // "ignored": 1, "confidence": 2 },
288
301
  // "feedback": { "positive": 2, "negative": 1, "by_verdict": { "useful": 2, "wrong": 1 } },
289
- // "tokens": { "pre_prompt": 51, "compacting": 0 }, "partial": false }
302
+ // "tokens": { "pre_prompt": 51, "compacting": 0 }, "partial": false,
303
+ // "channels": { "push": { "tokens_pre_prompt": 51, "injections_total": 14,
304
+ // "precision_rate": 0.40, "coverage_rate": 0.36,
305
+ // "budget_tokens": 400 },
306
+ // "pull": { "proposals_created": 6, "proposals_approved": 1,
307
+ // "proposals_rejected": 2, "artifact_writes_total": 2,
308
+ // "artifact_writes_noop": 1, "references_registered": 0,
309
+ // "skills_registered": 0,
310
+ // "skill_emission": "off", "reference_emission": "off" } },
311
+ // "curation": { "eligible": 5, "curated": 1, "inferable": 3, "non_inferable": 2,
312
+ // "unknown": 1, "proposals_by_status": { "pending": 2, "applied": 1, ... } } }
290
313
  ```
291
314
 
292
- ---
315
+ ### `kevin_propose`
316
+
317
+ Creates curation proposals as `pending` rows with unified diffs — **a strict dry run**. Reads the eligible memories (`inferable != 1`), renders what would go into the artifact, and returns the minimal diff. No disk write, no `curated` marks, no side effects. Only `kevin_approve` may write.
293
318
 
294
- ## Precision (v0.4.0)
319
+ ```
320
+ kevin_propose({ kind: "agents_md" }) // kind: "agents_md" | "skill" | "reference"
321
+ // → { "proposals": [ { "id": "...", "kind": "agents_md", "targetPath": "AGENTS.md",
322
+ // "memoryIds": ["mem-1"], "status": "pending",
323
+ // "createdAt": "2026-08-14 10:00:00",
324
+ // "diff": "--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ ..." } ] }
325
+ ```
295
326
 
296
- Weak lessons — errors the reflector cannot dispatch to a deterministic rule — are **stored but never injected** while `quality_gate_enabled = '1'` (default). Injection now goes through a ledger: every pre-prompt/compacting injection is recorded and settled as effective or ineffective at session idle, so `kevin_status` reports the honest picture (`injections_total`, `injections_effective/ineffective`, `precision_rate`, `patterns_promoted_new`) instead of raw "lessons shared" counts. Recurrences demote lessons (`recurrence_count` → `stale`) and lower confidence. Debug mode: `kevin_config({ action: "set", key: "quality_gate_enabled", value: "0" })` re-injects weak lessons with a `(low confidence)` marker.
327
+ ### `kevin_approve`
297
328
 
298
- ## How Kevin measures itself (v0.5.0)
329
+ The **only** code path that writes a file. `approve` applies the proposal's diff atomically (temp file + rename, CRLF/BOM preserved), records an `artifact_writes` audit row, marks the proposal `applied` and its memories `curated`. `reject` records the human decision and touches nothing. Refusals and noops are audited, never silent.
330
+
331
+ ```
332
+ kevin_approve({ proposal_id: "...", decision: "approve" }) // or "reject"
333
+ // → { "proposalId": "...", "status": "applied", "outcome": "written", "curated": 1 }
334
+ // ("outcome": "noop" when the artifact already matches, "refused" when the
335
+ // marker block is malformed; a rejected proposal returns
336
+ // { "proposalId": "...", "status": "rejected" })
337
+ ```
338
+
339
+ ### `kevin_publish`
340
+
341
+ Regenerates the pull-channel bundles under `~/.opencode-kevin/` — `skills/project-knowledge.md` and `refs/<topic>.md` — reporting per-bundle outcome and the emission state (`on` / `off` / `unavailable`). Registration with the host happens at plugin startup; this tool only materializes and reports.
342
+
343
+ ---
344
+
345
+ ## How Kevin measures itself
346
+
347
+ ### Injection outcomes
299
348
 
300
349
  Every injection is settled at `session.idle` into one of **four outcomes**:
301
350
 
@@ -306,13 +355,77 @@ Every injection is settled at `session.idle` into one of **four outcomes**:
306
355
  | `inconclusive` | Neither — the error did not recur, but no fix was seen either | no |
307
356
  | `unmeasured` | Session went idle before settlement could run | no |
308
357
 
309
- - **`precision_rate`** = `effective / (effective + ineffective)`. The v0.4.0 definition counted "did not recur" as success measuring absence of recurrence, not effect. **Your precision rate will look lower on v0.5.0. That is the honest number.** Existing `effective` rows were remapped to `inconclusive` by migration 006.
358
+ - **`precision_rate`** = `effective / (effective + ineffective)`. Measuring *effect*, not absence of recurrence: a lesson that was injected and never contradicted counts as `inconclusive`, not success. **Your precision rate will look lower than before v0.5.0. That is the honest number.**
310
359
  - **`coverage_rate`** = `(effective + ineffective) / total` — the share of injections that were actually measured. Reported alongside precision so a low measurable fraction stays visible instead of hiding behind a large total.
311
- - **`blocked`** counts every gate rejection by reason (`seen_this_session`, `weak`, `recurrence`, `stale`, `ignored`) — a rejection you did not count did not happen.
360
+ - **`blocked`** counts every gate rejection by reason `seen_this_session`, `weak`, `recurrence`, `stale`, `ignored`, `confidence` — a rejection you did not count did not happen.
361
+
362
+ ### The quality gate
363
+
364
+ Weak lessons — errors the reflector cannot dispatch to a deterministic rule — are **stored but never injected** while `quality_gate_enabled = '1'` (default). Recurrences demote lessons (`recurrence_count` → `stale`) and lower confidence. Debug mode: `kevin_config({ action: "set", key: "quality_gate_enabled", value: "0" })` re-injects weak lessons with a `(low confidence)` marker.
365
+
366
+ ### Seeing the whole picture
367
+
368
+ `kevin_trace` shows you the plan *before* it happens (dry run, zero side effects); `kevin_audit` reads the whole state after; `kevin_feedback` lets a human correct it — and the correction moves the confidence number `kevin_why` reports.
369
+
370
+ ---
371
+
372
+ ## Curation & Pull
312
373
 
313
- `kevin_trace` shows you the plan *before* it happens (dry run, zero side effects); `kevin_audit` reads the whole picture after; `kevin_feedback` lets a human correct it — and the correction moves the confidence number `kevin_why` reports.
374
+ ### The marker contract
314
375
 
315
- ## Replay harness (v0.5.0)
376
+ Kevin never edits your files directly. Every artifact write happens inside a frozen marker block, delimited verbatim by:
377
+
378
+ ```
379
+ <!-- kevin:begin — curated by opencode-kevin, safe to edit -->
380
+ <!-- kevin:end -->
381
+ ```
382
+
383
+ These exact strings are **frozen for the v0.x line** — README, tests and the v1.0.0 migration plan all depend on their byte sequences. What Kevin guarantees:
384
+
385
+ - **Only the block between the markers may change.** Bytes outside them are byte-identical after every write — including line endings (a CRLF file stays CRLF everywhere, even inside the generated block), a leading UTF-8 BOM, and the file's final newline.
386
+ - **Malformed markers are refused, never repaired.** If the file contains a `begin` without an `end` (or vice versa), Kevin refuses the write with an explicit reason and the file is untouched. Repairing would mean guessing at user intent; refusing means the state stays visible and auditable.
387
+ - **Idempotent**: applying an unchanged plan is a counted `noop` — no temp file, no write, no mtime churn.
388
+
389
+ ### The propose → review → approve flow
390
+
391
+ ```
392
+ eligible memories (inferable != 1)
393
+
394
+
395
+ kevin_propose({ kind }) ── creates pending rows + unified diffs.
396
+ │ NO disk write, NO curated marks.
397
+
398
+ HUMAN REVIEWS THE DIFF ── this is the entire safety model:
399
+ │ a memory earns its way into a file
400
+ ▼ only after a human said yes.
401
+ kevin_approve({ proposal_id, decision })
402
+
403
+ ├── "approve" ── ArtifactWriter.apply() (the ONLY write path)
404
+ │ atomic temp+rename, audit row in artifact_writes,
405
+ │ memory marked curated, proposal marked applied
406
+ └── "reject" ── recorded, nothing touches disk
407
+ ```
408
+
409
+ Rejection history is never deleted: it is the evidence base for the roadmap's kill criterion "proposals rejected more often than approved".
410
+
411
+ ### Three distribution channels
412
+
413
+ | Channel | Artifact | Cost when unused |
414
+ |---|---|---|
415
+ | **Push** | per-prompt `<kevin-context>` injection | charges on every prompt — now capped at 400 tokens by default |
416
+ | **Pull — AGENTS.md** | marker block in the project's `AGENTS.md` | zero |
417
+ | **Pull — skills** | `~/.opencode-kevin/skills/project-knowledge.md` (`skill_emission_enabled`) | zero |
418
+ | **Pull — references** | `~/.opencode-kevin/refs/<topic>.md` (`reference_emission_enabled`) | zero |
419
+
420
+ `kevin_audit`'s `channels` block compares push vs pull on the same axes, and reports each emission channel as `"on"`, `"off"` (setting `'0'` on a capable host) or `"unavailable"` (host without the v2 domain).
421
+
422
+ ### The confidence floor gate
423
+
424
+ `injection_confidence_floor` (default `'0.6'`) rejects memories whose computed confidence is below the floor, counted as `injections_blocked_confidence` — the sixth gate rejection reason, measured exactly like the first five. Single-observation memories (base confidence 0.5, no confirmed evidence) stop being pushed by default; `kevin_config({ action: "set", key: "injection_confidence_floor", value: "0" })` restores v0.5 behaviour exactly.
425
+
426
+ ---
427
+
428
+ ## Replay harness
316
429
 
317
430
  `npm run replay` runs every transcript in `tests/replay/fixtures/` through the plugin against an in-memory database with a frozen clock and prints one table row per transcript (memories created, injection outcomes, `precision_rate`, `coverage_rate`, tokens). Record your own session as a JSON array of typed events (`session.created`, `chat.message`, `tool.before`, `tool.after`, `system.transform`, `compacting`, `session.idle`) with ISO-8601 `at` timestamps, drop it into `tests/replay/fixtures/`, and re-run. The `at` timestamps are the only source of time during replay.
318
431
 
@@ -325,15 +438,15 @@ Kevin subscribes to 6 OpenCode hooks:
325
438
  | Hook | What Kevin does |
326
439
  |---|---|
327
440
  | `tool.execute.before` | Records tool call start (callID + redacted args) |
328
- | `tool.execute.after` | Records result (id = callID); on failure → Reflector.invoke async (throttled); on success → CausalChain.onSuccess links the fix (v0.3.0) |
329
- | `experimental.chat.system.transform` | Injects relevant lessons in `<kevin-context>` (900 tokens by default, configurable) + optional `<kevin-suggestion>` (v0.3.0) |
441
+ | `tool.execute.after` | Records result (id = callID); on failure → Reflector.invoke async (throttled); on success → CausalChain links the fix |
442
+ | `experimental.chat.system.transform` | Injects relevant lessons in `<kevin-context>` (400 tokens by default, configurable) + optional `<kevin-suggestion>` |
330
443
  | `experimental.session.compacting` | Re-injects lessons in `<kevin-memory>` after compacting (2000 tokens) + optional `<kevin-suggestion>` |
331
- | `event` (`session.created`) | Captures current `sessionID` |
332
- | `event` (`session.idle`) | Generates retrospective.md; boosts positive lessons (v0.2.0); penalizes recurring failures (v0.3.0); promotes causal patterns + mines patterns (opt-in); flushes metrics |
444
+ | `event` (`session.created`) | Captures current `sessionID` (skill/reference emissions register at plugin startup, not per session) |
445
+ | `event` (`session.idle`) | Settles injection outcomes; generates the retrospective; boosts positive lessons; penalizes recurring failures; promotes causal patterns and mines patterns (opt-in); drafts curation proposals (`curation_enabled`); flushes metrics |
333
446
 
334
- **Redaction**: absolute paths (`C:\Users\...`, `/home/...`) → `<path>` and secrets (`API_KEY=`, `Bearer`, `token`) → `<redacted>` before persisting anything. v0.2.0 adds `<private>…</private>` block redaction: sweeps tool call args and output before persistence, replaces with `<private: redacted N chars>`.
447
+ **Redaction**: absolute paths (`C:\Users\...`, `/home/...`) → `<path>` and secrets (`API_KEY=`, `Bearer`, `token`) → `<redacted>` before persisting anything. `<private>…</private>` blocks are swept from tool call args and output before persistence and replaced with `<private: redacted N chars>`.
335
448
 
336
- **Throttle**: Reflector generates at most 1 lesson per minute per unique fingerprint (v0.2.0: per-fingerprint, not global). Configurable via `throttleMs`.
449
+ **Throttle**: Reflector generates at most 1 lesson per minute per unique fingerprint (per-fingerprint, not global). Configurable via `throttleMs`.
337
450
 
338
451
  **Truncation**: content > 4KB keeps the lesson searchable; only the additional context is truncated (`metadata.truncated = true`).
339
452
 
@@ -341,6 +454,8 @@ Kevin subscribes to 6 OpenCode hooks:
341
454
 
342
455
  ## Configuration
343
456
 
457
+ ### Plugin options
458
+
344
459
  Kevin accepts options via the plugin's tuple form (see Installation → Advanced). Programmatic defaults:
345
460
 
346
461
  ```ts
@@ -355,17 +470,26 @@ KevinPlugin(input, {
355
470
  });
356
471
  ```
357
472
 
358
- ### Settings (v0.5.0)
473
+ ### Settings
359
474
 
360
- Read/write via `kevin_config({ action: "list" | "set", ... })`. v0.5.0 adds:
475
+ Read/write via `kevin_config({ action: "list" | "set", ... })`. All values are TEXT; booleans compare against `"1"`.
361
476
 
362
477
  | Setting | Default | Effect |
363
478
  |---|---|---|
479
+ | `quality_gate_enabled` | `"1"` | Weak lessons are stored but never injected while enabled |
480
+ | `lesson_snippet_injection` | `"1"` | Injects the rescued errorType snippet with each lesson |
481
+ | `llm_reflection_enabled` | `"0"` | Opt-in LLM enrichment of reflector lessons |
482
+ | `cross_project_enabled` | `"0"` | `kevin_query` includes imported cross-project memories |
483
+ | `patternminer_enabled` | `"0"` | Opt-in deterministic 2-gram/3-gram pattern miner at `session.idle` |
484
+ | `tool_calls_dedup_enabled` | `"0"` | Opt-in dedup of repeated tool calls |
364
485
  | `deterministic_retrieval` | `"0"` | Freezes Kevin's internal clock (recency factor 1.0, no relevance bumps) — for hermetic tests and the replay harness |
365
- | `pre_prompt_budget_tokens` | `"900"` | Pre-prompt injection cap, clamped to `[100, 4000]` (was a hard-coded 1500) |
486
+ | `pre_prompt_budget_tokens` | `"400"` | Pre-prompt injection cap, clamped to `[0, 4000]`; `0` turns push off |
366
487
  | `archive_after_days` | `"30"` | Age at which stale non-pattern memories are retired to `archived` on `session.idle` |
367
-
368
- All settings are stored as TEXT in `kevin_settings`; values are compared against `"1"` for booleans.
488
+ | `curation_enabled` | `"1"` | Generates curation proposals at `session.idle` |
489
+ | `agents_md_path` | `"AGENTS.md"` | Where the AGENTS.md channel writes (project-relative) |
490
+ | `skill_emission_enabled` | `"0"` | Registers the curated skill with the host at startup (v2 hosts only) |
491
+ | `reference_emission_enabled` | `"0"` | Registers `@kevin/<topic>` references at startup (v2 hosts only) |
492
+ | `injection_confidence_floor` | `"0.6"` | Push gate: memories below this confidence are counted and rejected |
369
493
 
370
494
  ---
371
495
 
@@ -379,7 +503,7 @@ npm run typecheck # tsc --noEmit (strict)
379
503
  npm run lint # biome check .
380
504
  npm test # vitest run (unit + integration + e2e + replay)
381
505
  npm run verify # post-install verification
382
- npm run replay # replay report over tests/replay/fixtures (v0.5.0)
506
+ npm run replay # replay report over tests/replay/fixtures
383
507
  ```
384
508
 
385
509
  ### Publishing (maintainer)
@@ -395,42 +519,64 @@ npm publish --access public
395
519
 
396
520
  ```
397
521
  plugin/
398
- index.ts # Entry point: KevinPlugin
399
- Store.ts # Wrapper SQLite (node:sqlite / bun:sqlite / better-sqlite3 fallback)
522
+ index.ts # Entry point: KevinPlugin (wires hooks, tools, emissions)
523
+ Store.ts # SQLite wrapper (node:sqlite / bun:sqlite / better-sqlite3 fallback)
524
+ sqlite-adapter.ts # Runtime-agnostic SQLite adapter behind Store
400
525
  Migrate.ts # Idempotent migrations + post-apply hooks
401
526
  MemoryService.ts # save/query/getRelevant (FTS5 + bm25 + origin-aware rank + supersede)
402
527
  ToolCallObserver.ts # onBefore/onAfter + redact + inferErrorType + dedup (opt-in)
403
- Reflector.ts # Heuristic lessons + per-fingerprint throttle + lesson v2 + LLM enrich
404
- CausalChain.ts # v0.3.0 — links fixes to failures + promotes causal patterns
528
+ Reflector.ts # Heuristic lessons + per-fingerprint throttle + LLM enrich (opt-in)
405
529
  ContextInjector.ts # deriveQuery + pre-prompt/compacting injection + <kevin-suggestion>
406
- QualityGate.ts # v0.4.0 — weak-lesson gate (stored, not injected by default)
407
- InjectionLedger.ts # v0.4.0 — injection ledger + settle → precision_rate
408
530
  Retrospective.ts # Generates retrospective.md + FP recap + metrics snapshot
409
- LessonFixer.ts # v0.4.0 deterministic fix_args capture + promotion enrichment
410
- confidence.ts # v0.4.0 two-sided computeConfidence (evidence + recurrence)
411
- fingerprint.ts # FNV-1a 64-bit (in-house, no node:crypto)
412
- metrics.ts # In-memory counters + debounced flush to kevin_metrics
531
+ Feedback.ts # kevin_feedback: verdicts, confidence terms, ignored stamp
532
+ Archiver.ts # Retires stale non-pattern memories past archive_after_days
533
+ CausalChain.ts # Links fixes to failures + promotes causal patterns
534
+ QualityGate.ts # Weak-lesson gate (stored, not injected by default)
535
+ InjectionLedger.ts # Injection ledger + settle → precision_rate
536
+ LessonFixer.ts # Deterministic fix_args capture + promotion enrichment
413
537
  PatternMiner.ts # Opt-in deterministic 2-gram/3-gram miner
414
- kevin_why.ts # v0.3.0 kevin_why tool: failure→fix traces + related rules
415
- okf-export.ts # v0.3.0 kevin_export: OKF/markdown export
416
- okf-import.ts # v0.3.0 kevin_import: bundle parser + import
417
- query-tokenizer.ts # v0.4.0 FTS5 tokenizer for query sanitization
538
+ Curator.ts # Curation candidates + propose/approve lifecycle
539
+ ArtifactWriter.ts # The SINGLE write path (markers, atomic, noop, audit rows)
540
+ Materializer.ts # Pull-channel topic bundles (skills, refs)
541
+ inferability.ts # Deterministic inferable/non-inferable/unknown classifier
542
+ capabilities.ts # v2 domain probe (skills / references)
543
+ diff.ts # Minimal unified diff for proposal review
544
+ replay.ts # Hermetic replay driver over recorded transcripts
545
+ replay-types.ts # Transcript/result types for the replay harness
546
+ kevin_propose.ts # kevin_propose tool (strict dry run)
547
+ kevin_approve.ts # kevin_approve tool (only writer call site)
548
+ kevin_publish.ts # kevin_publish tool (bundle regeneration)
549
+ kevin_audit.ts # Read-only audit + channels/curation blocks
550
+ kevin_why.ts # kevin_why tool: failure→fix traces + related rules
551
+ okf-export.ts # kevin_export: OKF/markdown export
552
+ okf-import.ts # kevin_import: bundle parser + import
553
+ confidence.ts # Two-sided computeConfidence (evidence + recurrence + feedback)
554
+ query-tokenizer.ts # FTS5 tokenizer for query sanitization
418
555
  memory-format.ts # escapeInjectedText, formatMemories, <protect> + id: line wrappers
419
556
  redact.ts # redactPaths + stripPrivate
557
+ fingerprint.ts # FNV-1a 64-bit (in-house, no node:crypto)
558
+ metrics.ts # In-memory counters + debounced flush to kevin_metrics
420
559
  uuid.ts # UUIDv7
421
560
  migrations/
422
561
  001_initial.sql # schema: memories, tool_calls, retrospectives
423
562
  002_indexes.sql # FTS5 + indexes
424
- 003_v02_signal.sql # v0.2.0 Signal Quality: fingerprint, origin, metrics, dedup indexes
425
- 004_v03_knowledge.sql # v0.3.0 Knowledge + Causality: evidence/status/supersede, error_fingerprint
426
- 005_v04_signal.sql # v0.4.0 Signal over Noise: recurrence_count, fix_args, last_injected_at
427
- tests/{unit,integration,e2e}/
563
+ 003_v02_signal.sql # fingerprint, origin, metrics, dedup indexes
564
+ 004_v03_knowledge.sql # evidence/status/supersede, error_fingerprint
565
+ 005_v04_signal.sql # recurrence_count, fix_args, last_injected_at
566
+ 006_v05_glassbox.sql # ignored/archived/superseded_by, feedback, metrics
567
+ 007_v06_pull.sql # curation_proposals, artifact_writes, curated/inferable
568
+ tests/
569
+ unit/ # component tests
570
+ integration/ # tool-level tests through real components
571
+ e2e/ # closed-loop tests through the host hooks
572
+ replay/ # transcript fixtures + replay harness tests
428
573
  scripts/
429
574
  copy-migrations.mjs # build step: copies *.sql to dist/migrations
430
575
  verify-install.ts # npm run verify
431
576
  ```
432
577
 
433
578
  ---
579
+
434
580
  ## License
435
581
 
436
- MIT
582
+ MIT