@jmtrin/opencode-kevin 0.6.0 → 0.8.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 (83) hide show
  1. package/README.md +771 -580
  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 +144 -144
  8. package/dist/migrations/008_v07_truth.sql +124 -0
  9. package/dist/migrations/009_v08_team.sql +100 -0
  10. package/dist/plugin/ArtifactWriter.d.ts +28 -0
  11. package/dist/plugin/ArtifactWriter.js +35 -2
  12. package/dist/plugin/ArtifactWriter.js.map +1 -1
  13. package/dist/plugin/ConflictDetector.d.ts +35 -0
  14. package/dist/plugin/ConflictDetector.js +283 -0
  15. package/dist/plugin/ConflictDetector.js.map +1 -0
  16. package/dist/plugin/ContextInjector.d.ts +9 -0
  17. package/dist/plugin/ContextInjector.js +14 -3
  18. package/dist/plugin/ContextInjector.js.map +1 -1
  19. package/dist/plugin/ConventionMiner.d.ts +35 -0
  20. package/dist/plugin/ConventionMiner.js +243 -0
  21. package/dist/plugin/ConventionMiner.js.map +1 -0
  22. package/dist/plugin/Curator.d.ts +22 -2
  23. package/dist/plugin/Curator.js +101 -28
  24. package/dist/plugin/Curator.js.map +1 -1
  25. package/dist/plugin/InjectionLedger.d.ts +6 -0
  26. package/dist/plugin/InjectionLedger.js +6 -0
  27. package/dist/plugin/InjectionLedger.js.map +1 -1
  28. package/dist/plugin/Materializer.d.ts +4 -5
  29. package/dist/plugin/Materializer.js +9 -6
  30. package/dist/plugin/Materializer.js.map +1 -1
  31. package/dist/plugin/MemoryService.d.ts +49 -2
  32. package/dist/plugin/MemoryService.js +306 -8
  33. package/dist/plugin/MemoryService.js.map +1 -1
  34. package/dist/plugin/Migrate.js +61 -2
  35. package/dist/plugin/Migrate.js.map +1 -1
  36. package/dist/plugin/Reflector.js +13 -0
  37. package/dist/plugin/Reflector.js.map +1 -1
  38. package/dist/plugin/RepoIdentity.d.ts +113 -0
  39. package/dist/plugin/RepoIdentity.js +266 -0
  40. package/dist/plugin/RepoIdentity.js.map +1 -0
  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.js +17 -0
  45. package/dist/plugin/Retrospective.js.map +1 -1
  46. package/dist/plugin/SharedLayer.d.ts +159 -0
  47. package/dist/plugin/SharedLayer.js +463 -0
  48. package/dist/plugin/SharedLayer.js.map +1 -0
  49. package/dist/plugin/index.d.ts +36 -2
  50. package/dist/plugin/index.js +591 -10
  51. package/dist/plugin/index.js.map +1 -1
  52. package/dist/plugin/kevin_approve.js +7 -4
  53. package/dist/plugin/kevin_approve.js.map +1 -1
  54. package/dist/plugin/kevin_audit.d.ts +59 -1
  55. package/dist/plugin/kevin_audit.js +190 -3
  56. package/dist/plugin/kevin_audit.js.map +1 -1
  57. package/dist/plugin/kevin_conflicts.d.ts +9 -0
  58. package/dist/plugin/kevin_conflicts.js +51 -0
  59. package/dist/plugin/kevin_conflicts.js.map +1 -0
  60. package/dist/plugin/kevin_facts.d.ts +42 -0
  61. package/dist/plugin/kevin_facts.js +37 -0
  62. package/dist/plugin/kevin_facts.js.map +1 -0
  63. package/dist/plugin/metrics.d.ts +3 -3
  64. package/dist/plugin/metrics.js +21 -2
  65. package/dist/plugin/metrics.js.map +1 -1
  66. package/dist/plugin/okf-export.d.ts +2 -2
  67. package/dist/plugin/okf-export.js +15 -7
  68. package/dist/plugin/okf-export.js.map +1 -1
  69. package/dist/plugin/okf.d.ts +107 -0
  70. package/dist/plugin/okf.js +304 -0
  71. package/dist/plugin/okf.js.map +1 -0
  72. package/dist/plugin/replay-types.d.ts +29 -287
  73. package/dist/plugin/replay-types.js +145 -53
  74. package/dist/plugin/replay-types.js.map +1 -1
  75. package/migrations/001_initial.sql +91 -91
  76. package/migrations/003_v02_signal.sql +57 -57
  77. package/migrations/004_v03_knowledge.sql +138 -138
  78. package/migrations/005_v04_signal.sql +57 -57
  79. package/migrations/006_v05_glassbox.sql +118 -118
  80. package/migrations/007_v06_pull.sql +144 -144
  81. package/migrations/008_v07_truth.sql +124 -0
  82. package/migrations/009_v08_team.sql +100 -0
  83. package/package.json +4 -4
@@ -1,145 +1,145 @@
1
- -- ============================================================================
2
- -- 007_v06_pull.sql — v0.6.0 "Pull"
3
- --
4
- -- Distribution: curated artifacts instead of a per-prompt token tax.
5
- --
6
- -- Section 1: curation_proposals — the persisted human decision record.
7
- -- Section 2: artifact_writes — the disk audit trail, including refusals.
8
- -- Section 3: memories curation + inferability columns.
9
- -- Section 4: metric seeds.
10
- -- Section 5: setting seeds.
11
- -- Section 6: conditional push-budget demotion.
12
- -- Section 7: schema_version.
13
- --
14
- -- Additive only. Every CHECK constraint introduced here is on a NEW table;
15
- -- no existing constraint is widened, so unlike migration 006 there is no
16
- -- table rebuild in this file.
17
- -- ============================================================================
18
-
19
- -- ---------------------------------------------------------------------------
20
- -- 1. curation_proposals — one row per proposed artifact change.
21
- --
22
- -- This table is the "explicit human decision between generation and
23
- -- application" of Principle 22. `kevin_propose` writes 'pending' rows and
24
- -- nothing else; `kevin_approve` moves them to 'approved' → 'applied' or to
25
- -- 'rejected'. Rows are never deleted: rejection history is the evidence
26
- -- base for roadmap kill criterion K4 ("proposals rejected more often than
27
- -- approved"), which is uncheckable if rejections are discarded.
28
- --
29
- -- memory_id has no REFERENCES clause. Store sets PRAGMA foreign_keys = ON,
30
- -- and a hard FK would block deleting a memory that a historical proposal
31
- -- once mentioned — the same reasoning as superseded_by in migration 006.
32
- -- ---------------------------------------------------------------------------
33
- CREATE TABLE IF NOT EXISTS curation_proposals (
34
- id TEXT PRIMARY KEY,
35
- project_id TEXT NOT NULL,
36
- memory_id TEXT NOT NULL,
37
- kind TEXT NOT NULL CHECK (kind IN ('agents_md','skill','reference')),
38
- target_path TEXT NOT NULL,
39
- proposed_text TEXT NOT NULL,
40
- diff TEXT NOT NULL,
41
- status TEXT NOT NULL DEFAULT 'pending'
42
- CHECK (status IN ('pending','approved','rejected','applied','superseded')),
43
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
44
- decided_at TEXT,
45
- applied_at TEXT
46
- );
47
- CREATE INDEX IF NOT EXISTS idx_proposals_status ON curation_proposals(status);
48
- CREATE INDEX IF NOT EXISTS idx_proposals_memory ON curation_proposals(memory_id);
49
- CREATE INDEX IF NOT EXISTS idx_proposals_project ON curation_proposals(project_id);
50
-
51
- -- ---------------------------------------------------------------------------
52
- -- 2. artifact_writes — the append-only audit trail for every disk operation.
53
- --
54
- -- A row is written for EVERY ArtifactWriter.apply() call, including
55
- -- outcome='noop' and outcome='refused'. A refusal that leaves no trace is
56
- -- indistinguishable from a write that never happened, and the whole point
57
- -- of §5.1 rule 3 is that a refusal is a reportable event.
58
- --
59
- -- hash_before / hash_after are SHA-256 of the full file contents, not of
60
- -- the marker block. That is what makes rule 4 (bytes outside the markers
61
- -- are byte-identical) auditable after the fact rather than only at test
62
- -- time.
63
- -- ---------------------------------------------------------------------------
64
- CREATE TABLE IF NOT EXISTS artifact_writes (
65
- id TEXT PRIMARY KEY,
66
- proposal_id TEXT,
67
- project_id TEXT NOT NULL,
68
- path TEXT NOT NULL,
69
- bytes_before INTEGER,
70
- bytes_after INTEGER,
71
- hash_before TEXT,
72
- hash_after TEXT,
73
- outcome TEXT NOT NULL CHECK (outcome IN ('written','noop','refused')),
74
- reason TEXT,
75
- wrote_at TEXT NOT NULL DEFAULT (datetime('now'))
76
- );
77
- CREATE INDEX IF NOT EXISTS idx_artifact_writes_path ON artifact_writes(path);
78
-
79
- -- ---------------------------------------------------------------------------
80
- -- 3. memories: curation state and inferability.
81
- --
82
- -- curated / curated_at record that a memory has already been published,
83
- -- so the Curator does not re-propose it on every session idle.
84
- --
85
- -- inferable is deliberately NULLABLE with no default. Three states are
86
- -- needed, not two: 1 = inferable (a self-describing diagnostic the model
87
- -- resolves for free), 0 = non-inferable (project truth), NULL = unknown
88
- -- (not yet classified, or classified as 'unknown'). The Curator predicate
89
- -- is `inferable != 1`, so NULL rows stay eligible — an unclassified memory
90
- -- must not be silently withheld from curation.
91
- -- ---------------------------------------------------------------------------
92
- ALTER TABLE memories ADD COLUMN curated INTEGER NOT NULL DEFAULT 0;
93
- ALTER TABLE memories ADD COLUMN curated_at TEXT;
94
- ALTER TABLE memories ADD COLUMN inferable INTEGER;
95
-
96
- CREATE INDEX IF NOT EXISTS idx_memories_curated ON memories(curated);
97
- CREATE INDEX IF NOT EXISTS idx_memories_inferable ON memories(inferable);
98
-
99
- -- ---------------------------------------------------------------------------
100
- -- 4. Metric seeds. Order matches the additions to METRIC_KEYS in metrics.ts.
101
- -- injections_blocked_confidence is the sixth member of the v0.5 blocked
102
- -- family and MUST be counted like the other five (Principle 16).
103
- -- ---------------------------------------------------------------------------
104
- INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
105
- ('proposals_created', 0),
106
- ('proposals_approved', 0),
107
- ('proposals_rejected', 0),
108
- ('artifact_writes_total', 0),
109
- ('artifact_writes_noop', 0),
110
- ('injections_blocked_confidence', 0);
111
-
112
- -- ---------------------------------------------------------------------------
113
- -- 5. Setting seeds. Values are TEXT, always. Read them with an explicit
114
- -- string comparison or an explicit Number() parse — never `=== 1`.
115
- -- (That exact mistake kept cross_project_enabled unreachable for the
116
- -- whole of v0.3.0.)
117
- --
118
- -- skill_emission_enabled and reference_emission_enabled default to '0':
119
- -- the pull channels ship OFF and are opted into, because they depend on a
120
- -- v2 domain Kevin does not pin.
121
- -- ---------------------------------------------------------------------------
122
- INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
123
- ('curation_enabled', '1'),
124
- ('agents_md_path', 'AGENTS.md'),
125
- ('skill_emission_enabled', '0'),
126
- ('reference_emission_enabled', '0'),
127
- ('injection_confidence_floor', '0.6');
128
-
129
- -- ---------------------------------------------------------------------------
130
- -- 6. Push-budget demotion.
131
- --
132
- -- Lower the default push budget only where the user has not overridden it.
133
- -- A user who deliberately set 1200 (or 1500, or 200) keeps their value;
134
- -- only an installation still sitting on the v0.5 default of '900' is
135
- -- moved to '400'. An unconditional UPDATE here would silently discard a
136
- -- deliberate configuration choice, which is a worse defect than the token
137
- -- cost it would save.
138
- -- ---------------------------------------------------------------------------
139
- UPDATE kevin_settings SET value = '400'
140
- WHERE key = 'pre_prompt_budget_tokens' AND value = '900';
141
-
142
- -- ---------------------------------------------------------------------------
143
- -- 7. Version marker.
144
- -- ---------------------------------------------------------------------------
1
+ -- ============================================================================
2
+ -- 007_v06_pull.sql — v0.6.0 "Pull"
3
+ --
4
+ -- Distribution: curated artifacts instead of a per-prompt token tax.
5
+ --
6
+ -- Section 1: curation_proposals — the persisted human decision record.
7
+ -- Section 2: artifact_writes — the disk audit trail, including refusals.
8
+ -- Section 3: memories curation + inferability columns.
9
+ -- Section 4: metric seeds.
10
+ -- Section 5: setting seeds.
11
+ -- Section 6: conditional push-budget demotion.
12
+ -- Section 7: schema_version.
13
+ --
14
+ -- Additive only. Every CHECK constraint introduced here is on a NEW table;
15
+ -- no existing constraint is widened, so unlike migration 006 there is no
16
+ -- table rebuild in this file.
17
+ -- ============================================================================
18
+
19
+ -- ---------------------------------------------------------------------------
20
+ -- 1. curation_proposals — one row per proposed artifact change.
21
+ --
22
+ -- This table is the "explicit human decision between generation and
23
+ -- application" of Principle 22. `kevin_propose` writes 'pending' rows and
24
+ -- nothing else; `kevin_approve` moves them to 'approved' → 'applied' or to
25
+ -- 'rejected'. Rows are never deleted: rejection history is the evidence
26
+ -- base for roadmap kill criterion K4 ("proposals rejected more often than
27
+ -- approved"), which is uncheckable if rejections are discarded.
28
+ --
29
+ -- memory_id has no REFERENCES clause. Store sets PRAGMA foreign_keys = ON,
30
+ -- and a hard FK would block deleting a memory that a historical proposal
31
+ -- once mentioned — the same reasoning as superseded_by in migration 006.
32
+ -- ---------------------------------------------------------------------------
33
+ CREATE TABLE IF NOT EXISTS curation_proposals (
34
+ id TEXT PRIMARY KEY,
35
+ project_id TEXT NOT NULL,
36
+ memory_id TEXT NOT NULL,
37
+ kind TEXT NOT NULL CHECK (kind IN ('agents_md','skill','reference')),
38
+ target_path TEXT NOT NULL,
39
+ proposed_text TEXT NOT NULL,
40
+ diff TEXT NOT NULL,
41
+ status TEXT NOT NULL DEFAULT 'pending'
42
+ CHECK (status IN ('pending','approved','rejected','applied','superseded')),
43
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
44
+ decided_at TEXT,
45
+ applied_at TEXT
46
+ );
47
+ CREATE INDEX IF NOT EXISTS idx_proposals_status ON curation_proposals(status);
48
+ CREATE INDEX IF NOT EXISTS idx_proposals_memory ON curation_proposals(memory_id);
49
+ CREATE INDEX IF NOT EXISTS idx_proposals_project ON curation_proposals(project_id);
50
+
51
+ -- ---------------------------------------------------------------------------
52
+ -- 2. artifact_writes — the append-only audit trail for every disk operation.
53
+ --
54
+ -- A row is written for EVERY ArtifactWriter.apply() call, including
55
+ -- outcome='noop' and outcome='refused'. A refusal that leaves no trace is
56
+ -- indistinguishable from a write that never happened, and the whole point
57
+ -- of §5.1 rule 3 is that a refusal is a reportable event.
58
+ --
59
+ -- hash_before / hash_after are SHA-256 of the full file contents, not of
60
+ -- the marker block. That is what makes rule 4 (bytes outside the markers
61
+ -- are byte-identical) auditable after the fact rather than only at test
62
+ -- time.
63
+ -- ---------------------------------------------------------------------------
64
+ CREATE TABLE IF NOT EXISTS artifact_writes (
65
+ id TEXT PRIMARY KEY,
66
+ proposal_id TEXT,
67
+ project_id TEXT NOT NULL,
68
+ path TEXT NOT NULL,
69
+ bytes_before INTEGER,
70
+ bytes_after INTEGER,
71
+ hash_before TEXT,
72
+ hash_after TEXT,
73
+ outcome TEXT NOT NULL CHECK (outcome IN ('written','noop','refused')),
74
+ reason TEXT,
75
+ wrote_at TEXT NOT NULL DEFAULT (datetime('now'))
76
+ );
77
+ CREATE INDEX IF NOT EXISTS idx_artifact_writes_path ON artifact_writes(path);
78
+
79
+ -- ---------------------------------------------------------------------------
80
+ -- 3. memories: curation state and inferability.
81
+ --
82
+ -- curated / curated_at record that a memory has already been published,
83
+ -- so the Curator does not re-propose it on every session idle.
84
+ --
85
+ -- inferable is deliberately NULLABLE with no default. Three states are
86
+ -- needed, not two: 1 = inferable (a self-describing diagnostic the model
87
+ -- resolves for free), 0 = non-inferable (project truth), NULL = unknown
88
+ -- (not yet classified, or classified as 'unknown'). The Curator predicate
89
+ -- is `inferable != 1`, so NULL rows stay eligible — an unclassified memory
90
+ -- must not be silently withheld from curation.
91
+ -- ---------------------------------------------------------------------------
92
+ ALTER TABLE memories ADD COLUMN curated INTEGER NOT NULL DEFAULT 0;
93
+ ALTER TABLE memories ADD COLUMN curated_at TEXT;
94
+ ALTER TABLE memories ADD COLUMN inferable INTEGER;
95
+
96
+ CREATE INDEX IF NOT EXISTS idx_memories_curated ON memories(curated);
97
+ CREATE INDEX IF NOT EXISTS idx_memories_inferable ON memories(inferable);
98
+
99
+ -- ---------------------------------------------------------------------------
100
+ -- 4. Metric seeds. Order matches the additions to METRIC_KEYS in metrics.ts.
101
+ -- injections_blocked_confidence is the sixth member of the v0.5 blocked
102
+ -- family and MUST be counted like the other five (Principle 16).
103
+ -- ---------------------------------------------------------------------------
104
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
105
+ ('proposals_created', 0),
106
+ ('proposals_approved', 0),
107
+ ('proposals_rejected', 0),
108
+ ('artifact_writes_total', 0),
109
+ ('artifact_writes_noop', 0),
110
+ ('injections_blocked_confidence', 0);
111
+
112
+ -- ---------------------------------------------------------------------------
113
+ -- 5. Setting seeds. Values are TEXT, always. Read them with an explicit
114
+ -- string comparison or an explicit Number() parse — never `=== 1`.
115
+ -- (That exact mistake kept cross_project_enabled unreachable for the
116
+ -- whole of v0.3.0.)
117
+ --
118
+ -- skill_emission_enabled and reference_emission_enabled default to '0':
119
+ -- the pull channels ship OFF and are opted into, because they depend on a
120
+ -- v2 domain Kevin does not pin.
121
+ -- ---------------------------------------------------------------------------
122
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
123
+ ('curation_enabled', '1'),
124
+ ('agents_md_path', 'AGENTS.md'),
125
+ ('skill_emission_enabled', '0'),
126
+ ('reference_emission_enabled', '0'),
127
+ ('injection_confidence_floor', '0.6');
128
+
129
+ -- ---------------------------------------------------------------------------
130
+ -- 6. Push-budget demotion.
131
+ --
132
+ -- Lower the default push budget only where the user has not overridden it.
133
+ -- A user who deliberately set 1200 (or 1500, or 200) keeps their value;
134
+ -- only an installation still sitting on the v0.5 default of '900' is
135
+ -- moved to '400'. An unconditional UPDATE here would silently discard a
136
+ -- deliberate configuration choice, which is a worse defect than the token
137
+ -- cost it would save.
138
+ -- ---------------------------------------------------------------------------
139
+ UPDATE kevin_settings SET value = '400'
140
+ WHERE key = 'pre_prompt_budget_tokens' AND value = '900';
141
+
142
+ -- ---------------------------------------------------------------------------
143
+ -- 7. Version marker.
144
+ -- ---------------------------------------------------------------------------
145
145
  INSERT OR IGNORE INTO schema_version (version) VALUES ('007');
@@ -0,0 +1,124 @@
1
+ -- ============================================================
2
+ -- Kevin 0.7.0 — Migration 008: Project Truth (additive)
3
+ -- ============================================================
4
+ -- Backward-compatible, additive only. Two new tables, two new
5
+ -- nullable/defaulted columns on `memories`, one index, five
6
+ -- metric seeds, four setting seeds. NO table rebuild: nothing
7
+ -- here widens a CHECK constraint on an existing table.
8
+ --
9
+ -- Section 1: repo_facts — the ground-truth store.
10
+ -- Section 2: memory_conflicts — surfaced, never auto-resolved.
11
+ -- Section 3: memories truth columns.
12
+ -- Section 4: metric seeds.
13
+ -- Section 5: setting seeds.
14
+ -- Section 6: schema_version.
15
+ -- ============================================================
16
+
17
+ -- ------------------------------------------------------------
18
+ -- 1. repo_facts — facts extracted from package.json and
19
+ -- tsconfig.json. Both are JSON, parsed with JSON.parse; this
20
+ -- release adds NO parser and NO runtime dependency.
21
+ --
22
+ -- The UNIQUE index INCLUDES project_id and that is load-
23
+ -- bearing (D7-02). Kevin's DB is global
24
+ -- (~/.opencode-kevin/kevin.db, projectId =
25
+ -- fingerprint(process.cwd())). Without project_id, project A's
26
+ -- packageManager=npm would overwrite project B's
27
+ -- packageManager=pnpm and contradictions() would then flag A's
28
+ -- memories against B's repository.
29
+ --
30
+ -- source_mtime lets scan() skip re-parsing an unchanged file.
31
+ -- ------------------------------------------------------------
32
+ CREATE TABLE IF NOT EXISTS repo_facts (
33
+ id TEXT PRIMARY KEY,
34
+ project_id TEXT NOT NULL,
35
+ file TEXT NOT NULL,
36
+ key_path TEXT NOT NULL,
37
+ value TEXT NOT NULL,
38
+ fingerprint TEXT NOT NULL,
39
+ source_mtime TEXT,
40
+ scanned_at TEXT NOT NULL DEFAULT (datetime('now'))
41
+ );
42
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_repo_facts ON repo_facts(project_id, file, key_path);
43
+ CREATE INDEX IF NOT EXISTS idx_repo_facts_fp ON repo_facts(fingerprint);
44
+
45
+ -- ------------------------------------------------------------
46
+ -- 2. memory_conflicts — detection only. status moves
47
+ -- open → acknowledged → resolved, and ONLY the kevin_conflicts
48
+ -- tool may move it to 'resolved' (D7-06). No session.idle path
49
+ -- writes 'resolved'.
50
+ --
51
+ -- memory_a / memory_b / fact_id carry NO REFERENCES clause on
52
+ -- purpose: Store sets PRAGMA foreign_keys = ON, and a hard FK
53
+ -- would block deleting a memory that participates in a
54
+ -- conflict.
55
+ -- ------------------------------------------------------------
56
+ CREATE TABLE IF NOT EXISTS memory_conflicts (
57
+ id TEXT PRIMARY KEY,
58
+ project_id TEXT NOT NULL,
59
+ memory_a TEXT NOT NULL,
60
+ memory_b TEXT,
61
+ fact_id TEXT,
62
+ kind TEXT NOT NULL CHECK (kind IN ('repo_truth','decision_pair','temporal')),
63
+ detail TEXT,
64
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','acknowledged','resolved')),
65
+ detected_at TEXT NOT NULL DEFAULT (datetime('now')),
66
+ resolved_at TEXT
67
+ );
68
+
69
+ CREATE INDEX IF NOT EXISTS idx_conflicts_status ON memory_conflicts(status);
70
+ CREATE INDEX IF NOT EXISTS idx_conflicts_memory ON memory_conflicts(memory_a);
71
+
72
+ -- ------------------------------------------------------------
73
+ -- 3. memories: the de-ranking column and its timestamp.
74
+ --
75
+ -- truth_penalty is clamped by application code to [0, 0.5] and
76
+ -- multiplies rankScore as (1 - truth_penalty), AFTER the
77
+ -- existing BM25 × origin_boost × recency_decay chain. At 0.0
78
+ -- the v0.6.0 ranking is reproduced exactly (D7-04).
79
+ --
80
+ -- There is deliberately no status transition here. Contra-
81
+ -- diction de-ranks; it never deletes (Principle 24, D7-03).
82
+ -- ------------------------------------------------------------
83
+ ALTER TABLE memories ADD COLUMN truth_penalty REAL NOT NULL DEFAULT 0.0;
84
+ ALTER TABLE memories ADD COLUMN contradicted_at TEXT;
85
+
86
+ CREATE INDEX IF NOT EXISTS idx_memories_truth_penalty ON memories(truth_penalty);
87
+
88
+ -- ------------------------------------------------------------
89
+ -- 4. Metric seeds. Order matches the additions to METRIC_KEYS
90
+ -- in metrics.ts (28 → 33).
91
+ -- ------------------------------------------------------------
92
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
93
+ ('repo_facts_scanned', 0),
94
+ ('memories_contradicted', 0),
95
+ ('conventions_mined', 0),
96
+ ('conflicts_detected', 0),
97
+ ('error_lessons_suppressed', 0);
98
+
99
+ -- ------------------------------------------------------------
100
+ -- 5. Setting seeds. Values are TEXT, always. Read them with an
101
+ -- explicit string comparison or an explicit Number() parse —
102
+ -- never `=== 1`, and never for truthiness.
103
+ --
104
+ -- error_lesson_mode is a TEXT ENUM ('all' | 'triage_only').
105
+ -- `if (mode)` is true for BOTH values and would put every
106
+ -- installation into triage mode on upgrade. Compare with
107
+ -- === "triage_only" (D7-12). Defaults to 'all', preserving
108
+ -- v0.6.0 behaviour exactly.
109
+ --
110
+ -- The three feature flags default to '0' (off): a release that
111
+ -- changes nothing until asked must not silently start scanning
112
+ -- the repository or mining conventions on upgrade. They are
113
+ -- opted into explicitly. (Task K7-001 / plan §5 & §12.)
114
+ -- ------------------------------------------------------------
115
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
116
+ ('repo_truth_enabled', '0'),
117
+ ('convention_mining_enabled', '0'),
118
+ ('conflict_detection_enabled', '0'),
119
+ ('error_lesson_mode', 'all');
120
+
121
+ -- ------------------------------------------------------------
122
+ -- 6. Version marker.
123
+ -- ------------------------------------------------------------
124
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('008');
@@ -0,0 +1,100 @@
1
+ -- ============================================================
2
+ -- Kevin 0.8.0 - Migration 009: Team (additive)
3
+ -- ============================================================
4
+ -- Backward-compatible, additive only. All new columns are
5
+ -- nullable or carry a NOT NULL DEFAULT so legacy rows keep
6
+ -- working without a destructive rebuild.
7
+ --
8
+ -- Scope note: this migration introduces `repo_id`, a SECOND
9
+ -- scoping dimension. `project_id` is retained on every table,
10
+ -- unchanged, as local-path provenance (D8-02). Nothing that
11
+ -- reads `project_id` today stops working.
12
+ -- ============================================================
13
+
14
+ -- 1. shared_entries: the local projection of the committed OKF file.
15
+ -- One row per (repo_id, entry_id). Rewritten by SharedLayer.import(),
16
+ -- never edited by hand, never the source of truth - the file is.
17
+ -- No REFERENCES to memories: an entry may arrive from a teammate
18
+ -- before any local memory corresponds to it (D8-12).
19
+ CREATE TABLE IF NOT EXISTS shared_entries (
20
+ id TEXT PRIMARY KEY,
21
+ repo_id TEXT NOT NULL,
22
+ entry_id TEXT NOT NULL,
23
+ type TEXT NOT NULL CHECK (type IN ('decision', 'rule', 'pattern', 'solution')),
24
+ statement TEXT NOT NULL,
25
+ scope TEXT,
26
+ confidence REAL NOT NULL DEFAULT 0.0,
27
+ evidence INTEGER NOT NULL DEFAULT 0,
28
+ origin TEXT NOT NULL DEFAULT 'shared',
29
+ author_hash TEXT,
30
+ op TEXT NOT NULL CHECK (op IN ('assert', 'tombstone')) DEFAULT 'assert',
31
+ supersedes TEXT,
32
+ created_at TEXT NOT NULL,
33
+ imported_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+
36
+ -- 1b. Identity is (repo_id, entry_id). The UNIQUE index is what makes
37
+ -- import() an idempotent upsert instead of an append.
38
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_entries
39
+ ON shared_entries(repo_id, entry_id);
40
+ CREATE INDEX IF NOT EXISTS idx_shared_entries_op
41
+ ON shared_entries(op);
42
+ CREATE INDEX IF NOT EXISTS idx_shared_entries_type
43
+ ON shared_entries(type);
44
+
45
+ -- 2. okf_imports: append-only audit of every read of the shared file,
46
+ -- including no-op reads and refusals. `file_hash` drives the skip
47
+ -- path in SharedLayer.import() (D8-14).
48
+ CREATE TABLE IF NOT EXISTS okf_imports (
49
+ id TEXT PRIMARY KEY,
50
+ repo_id TEXT NOT NULL,
51
+ path TEXT NOT NULL,
52
+ file_hash TEXT,
53
+ entries_parsed INTEGER NOT NULL DEFAULT 0,
54
+ entries_folded INTEGER NOT NULL DEFAULT 0,
55
+ entries_rejected INTEGER NOT NULL DEFAULT 0,
56
+ skipped INTEGER NOT NULL DEFAULT 0,
57
+ imported_at TEXT NOT NULL DEFAULT (datetime('now'))
58
+ );
59
+
60
+ CREATE INDEX IF NOT EXISTS idx_okf_imports_repo
61
+ ON okf_imports(repo_id, imported_at);
62
+
63
+ -- 3. memories: the second scoping dimension and the layer marker.
64
+ -- repo_id is NULLABLE and back-filled by the post-apply hook, not by
65
+ -- a DEFAULT: the value depends on the row's existing project_id and
66
+ -- SQLite cannot express that in a column default.
67
+ -- layer carries NO CHECK constraint - widening it later would force
68
+ -- the migration-004 rebuild path (D8-07). Enforced in TypeScript.
69
+ ALTER TABLE memories ADD COLUMN repo_id TEXT;
70
+ ALTER TABLE memories ADD COLUMN layer TEXT NOT NULL DEFAULT 'local';
71
+ ALTER TABLE memories ADD COLUMN shared_entry_id TEXT;
72
+
73
+ CREATE INDEX IF NOT EXISTS idx_memories_repo_id
74
+ ON memories(repo_id, status);
75
+ CREATE INDEX IF NOT EXISTS idx_memories_layer
76
+ ON memories(layer);
77
+
78
+ -- 4. kevin_metrics: seed the six v0.8 counters (33 -> 39).
79
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
80
+ ('shared_entries_total', 0),
81
+ ('shared_entries_imported', 0),
82
+ ('shared_entries_exported', 0),
83
+ ('okf_merge_folds', 0),
84
+ ('rekey_events', 0),
85
+ ('injections_from_shared', 0);
86
+
87
+ -- 5. kevin_settings: seed the five v0.8 flags (18 -> 23).
88
+ -- shared_layer_enabled defaults OFF: this release must be opted into,
89
+ -- because its first side effect is a new file in the user's repository.
90
+ -- shared_confidence_floor (0.7) is deliberately STRICTER than
91
+ -- injection_confidence_floor (0.6) - see 5.7.
92
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
93
+ ('shared_layer_enabled', '0'),
94
+ ('okf_path', '.kevin/knowledge.okf'),
95
+ ('share_requires_approval', '1'),
96
+ ('author_identity_mode', 'hashed'),
97
+ ('shared_confidence_floor', '0.7');
98
+
99
+ -- 6. Seed version 009.
100
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('009');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmtrin/opencode-kevin",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Kevin — Observa y aprende: capa de aprendizaje para OpenCode",
5
5
  "type": "module",
6
6
  "main": "dist/plugin/index.js",
@@ -22,11 +22,11 @@
22
22
  "lint": "biome check .",
23
23
  "format": "biome format --write .",
24
24
  "verify": "node --import tsx scripts/verify-install.ts",
25
- "replay": "node --import tsx scripts/replay.ts"
25
+ "replay": "node --import tsx scripts/replay.ts",
26
+ "measure:mix": "node --import tsx scripts/measure-mix.ts"
26
27
  },
27
28
  "dependencies": {
28
- "@opencode-ai/plugin": "^1.17.6",
29
- "zod": "^3.23.8"
29
+ "@opencode-ai/plugin": "^1.17.6"
30
30
  },
31
31
  "optionalDependencies": {
32
32
  "better-sqlite3": "^12.11.0"