@jmtrin/opencode-kevin 1.2.0 → 1.4.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.
- package/README.md +84 -22
- package/dist/plugin/index.d.ts +5 -4
- package/dist/plugin/index.js +127 -113
- package/dist/plugin/native.d.ts +2 -2
- package/dist/plugin/native.js +2 -2
- package/package.json +56 -68
- package/dist/migrations/001_initial.sql +0 -92
- package/dist/migrations/002_indexes.sql +0 -14
- package/dist/migrations/003_v02_signal.sql +0 -58
- package/dist/migrations/004_v03_knowledge.sql +0 -138
- package/dist/migrations/005_v04_signal.sql +0 -57
- package/dist/migrations/006_v05_glassbox.sql +0 -118
- package/dist/migrations/007_v06_pull.sql +0 -145
- package/dist/migrations/008_v07_truth.sql +0 -124
- package/dist/migrations/009_v08_team.sql +0 -100
- package/dist/migrations/010_v09_native.sql +0 -78
- package/dist/migrations/011_v10_proven.sql +0 -69
- package/dist/migrations/012_v11_drift.sql +0 -24
- package/dist/plugin/Archiver.d.ts +0 -42
- package/dist/plugin/Archiver.js +0 -83
- package/dist/plugin/ArtifactWriter.d.ts +0 -93
- package/dist/plugin/ArtifactWriter.js +0 -291
- package/dist/plugin/CausalChain.d.ts +0 -22
- package/dist/plugin/CausalChain.js +0 -197
- package/dist/plugin/ChatBridge.d.ts +0 -41
- package/dist/plugin/ChatBridge.js +0 -103
- package/dist/plugin/ConflictDetector.d.ts +0 -35
- package/dist/plugin/ConflictDetector.js +0 -260
- package/dist/plugin/ContextInjector.d.ts +0 -222
- package/dist/plugin/ContextInjector.js +0 -533
- package/dist/plugin/ConventionMiner.d.ts +0 -35
- package/dist/plugin/ConventionMiner.js +0 -242
- package/dist/plugin/Curator.d.ts +0 -116
- package/dist/plugin/Curator.js +0 -324
- package/dist/plugin/DashboardHtml.d.ts +0 -5
- package/dist/plugin/DashboardHtml.js +0 -180
- package/dist/plugin/Feedback.d.ts +0 -67
- package/dist/plugin/Feedback.js +0 -120
- package/dist/plugin/HookLiveness.d.ts +0 -88
- package/dist/plugin/HookLiveness.js +0 -322
- package/dist/plugin/InjectionLedger.d.ts +0 -98
- package/dist/plugin/InjectionLedger.js +0 -295
- package/dist/plugin/LessonFixer.d.ts +0 -44
- package/dist/plugin/LessonFixer.js +0 -45
- package/dist/plugin/Materializer.d.ts +0 -83
- package/dist/plugin/Materializer.js +0 -181
- package/dist/plugin/MemoryService.d.ts +0 -360
- package/dist/plugin/MemoryService.js +0 -1242
- package/dist/plugin/Migrate.d.ts +0 -17
- package/dist/plugin/Migrate.js +0 -271
- package/dist/plugin/PatternMiner.d.ts +0 -49
- package/dist/plugin/PatternMiner.js +0 -132
- package/dist/plugin/QualityGate.d.ts +0 -124
- package/dist/plugin/QualityGate.js +0 -119
- package/dist/plugin/Reflector.d.ts +0 -91
- package/dist/plugin/Reflector.js +0 -307
- package/dist/plugin/RepoIdentity.d.ts +0 -124
- package/dist/plugin/RepoIdentity.js +0 -300
- package/dist/plugin/RepoTruth.d.ts +0 -80
- package/dist/plugin/RepoTruth.js +0 -599
- package/dist/plugin/Retrospective.d.ts +0 -17
- package/dist/plugin/Retrospective.js +0 -216
- package/dist/plugin/SharedLayer.d.ts +0 -159
- package/dist/plugin/SharedLayer.js +0 -462
- package/dist/plugin/Store.d.ts +0 -29
- package/dist/plugin/Store.js +0 -50
- package/dist/plugin/ToolCallObserver.d.ts +0 -32
- package/dist/plugin/ToolCallObserver.js +0 -201
- package/dist/plugin/TuiActions.d.ts +0 -43
- package/dist/plugin/TuiActions.js +0 -181
- package/dist/plugin/TuiSnapshots.d.ts +0 -24
- package/dist/plugin/TuiSnapshots.js +0 -158
- package/dist/plugin/columns.d.ts +0 -11
- package/dist/plugin/columns.js +0 -54
- package/dist/plugin/confidence.d.ts +0 -8
- package/dist/plugin/confidence.js +0 -34
- package/dist/plugin/contract.d.ts +0 -54
- package/dist/plugin/contract.js +0 -386
- package/dist/plugin/diff.d.ts +0 -8
- package/dist/plugin/diff.js +0 -182
- package/dist/plugin/escape.d.ts +0 -39
- package/dist/plugin/escape.js +0 -60
- package/dist/plugin/fingerprint.d.ts +0 -27
- package/dist/plugin/fingerprint.js +0 -73
- package/dist/plugin/inferability.d.ts +0 -32
- package/dist/plugin/inferability.js +0 -88
- package/dist/plugin/kevin_approve.d.ts +0 -34
- package/dist/plugin/kevin_approve.js +0 -52
- package/dist/plugin/kevin_audit.d.ts +0 -229
- package/dist/plugin/kevin_audit.js +0 -631
- package/dist/plugin/kevin_bench.d.ts +0 -10
- package/dist/plugin/kevin_bench.js +0 -82
- package/dist/plugin/kevin_conflicts.d.ts +0 -9
- package/dist/plugin/kevin_conflicts.js +0 -50
- package/dist/plugin/kevin_contract.d.ts +0 -8
- package/dist/plugin/kevin_contract.js +0 -59
- package/dist/plugin/kevin_doctor.d.ts +0 -62
- package/dist/plugin/kevin_doctor.js +0 -196
- package/dist/plugin/kevin_facts.d.ts +0 -42
- package/dist/plugin/kevin_facts.js +0 -36
- package/dist/plugin/kevin_forget.d.ts +0 -33
- package/dist/plugin/kevin_forget.js +0 -260
- package/dist/plugin/kevin_native.d.ts +0 -29
- package/dist/plugin/kevin_native.js +0 -79
- package/dist/plugin/kevin_propose.d.ts +0 -23
- package/dist/plugin/kevin_propose.js +0 -14
- package/dist/plugin/kevin_publish.d.ts +0 -38
- package/dist/plugin/kevin_publish.js +0 -18
- package/dist/plugin/kevin_why.d.ts +0 -23
- package/dist/plugin/kevin_why.js +0 -111
- package/dist/plugin/memory-format.d.ts +0 -20
- package/dist/plugin/memory-format.js +0 -59
- package/dist/plugin/metrics.d.ts +0 -97
- package/dist/plugin/metrics.js +0 -288
- package/dist/plugin/okf-export.d.ts +0 -3
- package/dist/plugin/okf-export.js +0 -134
- package/dist/plugin/okf-import.d.ts +0 -76
- package/dist/plugin/okf-import.js +0 -271
- package/dist/plugin/okf.d.ts +0 -107
- package/dist/plugin/okf.js +0 -303
- package/dist/plugin/perf.d.ts +0 -35
- package/dist/plugin/perf.js +0 -174
- package/dist/plugin/query-tokenizer.d.ts +0 -13
- package/dist/plugin/query-tokenizer.js +0 -133
- package/dist/plugin/redact.d.ts +0 -2
- package/dist/plugin/redact.js +0 -18
- package/dist/plugin/replay-types.d.ts +0 -69
- package/dist/plugin/replay-types.js +0 -156
- package/dist/plugin/replay.d.ts +0 -36
- package/dist/plugin/replay.js +0 -202
- package/dist/plugin/sqlite-adapter.d.ts +0 -12
- package/dist/plugin/sqlite-adapter.js +0 -81
- package/dist/plugin/time-ms.d.ts +0 -1
- package/dist/plugin/time-ms.js +0 -16
- package/dist/plugin/tui-types.d.ts +0 -59
- package/dist/plugin/tui-types.js +0 -4
- package/dist/plugin/tui.d.ts +0 -18
- package/dist/plugin/tui.js +0 -198
- package/dist/plugin/uuid.d.ts +0 -1
- package/dist/plugin/uuid.js +0 -50
|
@@ -1,145 +0,0 @@
|
|
|
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
|
-
INSERT OR IGNORE INTO schema_version (version) VALUES ('007');
|
|
@@ -1,124 +0,0 @@
|
|
|
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');
|
|
@@ -1,100 +0,0 @@
|
|
|
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');
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
-- ============================================================
|
|
2
|
-
-- Kevin v0.9.0 "Native" — host surface and hook liveness
|
|
3
|
-
-- Migration 010. Additive only. No table rebuild.
|
|
4
|
-
-- ============================================================
|
|
5
|
-
|
|
6
|
-
-- 1. Hook liveness. Deliberately NOT project-scoped: a hook is a
|
|
7
|
-
-- property of the host binary, not of a checkout. One row per
|
|
8
|
-
-- hook name, updated in place. See D9-08.
|
|
9
|
-
CREATE TABLE IF NOT EXISTS hook_liveness (
|
|
10
|
-
hook TEXT PRIMARY KEY,
|
|
11
|
-
experimental INTEGER NOT NULL DEFAULT 0,
|
|
12
|
-
fire_count INTEGER NOT NULL DEFAULT 0,
|
|
13
|
-
error_count INTEGER NOT NULL DEFAULT 0,
|
|
14
|
-
expected_count INTEGER NOT NULL DEFAULT 0,
|
|
15
|
-
first_seen_at TEXT,
|
|
16
|
-
last_seen_at TEXT,
|
|
17
|
-
dead_since TEXT,
|
|
18
|
-
plugin_version TEXT
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
CREATE INDEX IF NOT EXISTS idx_hook_liveness_dead
|
|
22
|
-
ON hook_liveness(dead_since);
|
|
23
|
-
|
|
24
|
-
-- 2. Host probe history. Append-only. Off by default; exists so a
|
|
25
|
-
-- user chasing an intermittent fault can turn it on and get a
|
|
26
|
-
-- timeline instead of a single current value.
|
|
27
|
-
CREATE TABLE IF NOT EXISTS host_probes (
|
|
28
|
-
id TEXT PRIMARY KEY,
|
|
29
|
-
probed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
30
|
-
plugin_version TEXT,
|
|
31
|
-
flavour TEXT NOT NULL,
|
|
32
|
-
has_shell INTEGER NOT NULL DEFAULT 0,
|
|
33
|
-
v2_skill INTEGER NOT NULL DEFAULT 0,
|
|
34
|
-
v2_reference INTEGER NOT NULL DEFAULT 0,
|
|
35
|
-
notes TEXT
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
CREATE INDEX IF NOT EXISTS idx_host_probes_at
|
|
39
|
-
ON host_probes(probed_at);
|
|
40
|
-
|
|
41
|
-
-- 3. Native registration outcomes. One row per attach attempt, so
|
|
42
|
-
-- "registered but unverified" is a queryable state rather than a
|
|
43
|
-
-- log line.
|
|
44
|
-
CREATE TABLE IF NOT EXISTS native_registrations (
|
|
45
|
-
id TEXT PRIMARY KEY,
|
|
46
|
-
attached_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
47
|
-
surface TEXT NOT NULL CHECK (surface IN ('skill', 'reference')),
|
|
48
|
-
registered INTEGER NOT NULL DEFAULT 0,
|
|
49
|
-
verified INTEGER NOT NULL DEFAULT 0,
|
|
50
|
-
note TEXT
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
CREATE INDEX IF NOT EXISTS idx_native_registrations_surface
|
|
54
|
-
ON native_registrations(surface, attached_at);
|
|
55
|
-
|
|
56
|
-
-- 4. Metric seeds (39 -> 45).
|
|
57
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
|
|
58
|
-
('hook_fires_total', 0),
|
|
59
|
-
('hook_errors_total', 0),
|
|
60
|
-
('hooks_dead_total', 0),
|
|
61
|
-
('injections_suppressed_dead_hook', 0),
|
|
62
|
-
('native_registrations_total', 0),
|
|
63
|
-
('native_registration_failures', 0);
|
|
64
|
-
|
|
65
|
-
-- 5. Setting seeds (23 -> 27).
|
|
66
|
-
-- hook_liveness_enabled defaults ON: it is a read-only instrument
|
|
67
|
-
-- on the success path, and an instrument nobody switches on is an
|
|
68
|
-
-- instrument nobody has.
|
|
69
|
-
-- native_registration_enabled defaults OFF: it changes where a
|
|
70
|
-
-- curated skill comes from, and that is a change a user opts into.
|
|
71
|
-
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
|
|
72
|
-
('hook_liveness_enabled', '1'),
|
|
73
|
-
('native_registration_enabled', '0'),
|
|
74
|
-
('host_probe_history_enabled', '0'),
|
|
75
|
-
('dead_hook_report_threshold', '3');
|
|
76
|
-
|
|
77
|
-
-- 6. Version marker.
|
|
78
|
-
INSERT OR IGNORE INTO schema_version (version) VALUES ('010');
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
-- =============================================================
|
|
2
|
-
-- Kevin v1.0.0 "Proven" — migration 011
|
|
3
|
-
-- Adds: perf sampling, benchmark results.
|
|
4
|
-
-- No ALTER TABLE (second release running); no column is retyped.
|
|
5
|
-
-- =============================================================
|
|
6
|
-
|
|
7
|
-
-- 1. Per-scope latency aggregates, flushed at session.idle.
|
|
8
|
-
-- Machine-scoped: no project_id, for the same reason hook_liveness
|
|
9
|
-
-- has none (v0.9.0 D9-08) — latency is a property of the install.
|
|
10
|
-
CREATE TABLE IF NOT EXISTS perf_samples (
|
|
11
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
|
-
scope TEXT NOT NULL,
|
|
13
|
-
sample_count INTEGER NOT NULL,
|
|
14
|
-
p50_ms REAL NOT NULL,
|
|
15
|
-
p95_ms REAL NOT NULL,
|
|
16
|
-
max_ms REAL NOT NULL,
|
|
17
|
-
budget_p95_ms REAL NOT NULL,
|
|
18
|
-
within_budget INTEGER NOT NULL DEFAULT 1,
|
|
19
|
-
recorded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
CREATE INDEX IF NOT EXISTS idx_perf_samples_scope
|
|
23
|
-
ON perf_samples(scope, recorded_at DESC);
|
|
24
|
-
CREATE INDEX IF NOT EXISTS idx_perf_samples_breach
|
|
25
|
-
ON perf_samples(within_budget) WHERE within_budget = 0;
|
|
26
|
-
|
|
27
|
-
-- 2. Benchmark results. Append-only. One row per arm per run.
|
|
28
|
-
-- arm CHECK is closed by the plan; widening requires a table rebuild (intentional friction).
|
|
29
|
-
-- scope CHECK is intentionally absent: the scope union may gain members in a 1.x minor.
|
|
30
|
-
CREATE TABLE IF NOT EXISTS bench_runs (
|
|
31
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
-
corpus_digest TEXT NOT NULL,
|
|
33
|
-
contract_digest TEXT NOT NULL,
|
|
34
|
-
package_version TEXT NOT NULL,
|
|
35
|
-
runtime TEXT NOT NULL,
|
|
36
|
-
arm TEXT NOT NULL,
|
|
37
|
-
k INTEGER NOT NULL,
|
|
38
|
-
precision_at_k REAL NOT NULL,
|
|
39
|
-
recall_at_k REAL NOT NULL,
|
|
40
|
-
mrr REAL NOT NULL,
|
|
41
|
-
ran_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42
|
-
CHECK (arm IN ('none', 'recent-k', 'random-k', 'kevin'))
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
CREATE INDEX IF NOT EXISTS idx_bench_runs_corpus
|
|
46
|
-
ON bench_runs(corpus_digest, ran_at DESC);
|
|
47
|
-
CREATE INDEX IF NOT EXISTS idx_bench_runs_arm
|
|
48
|
-
ON bench_runs(arm, ran_at DESC);
|
|
49
|
-
|
|
50
|
-
-- 3. Metric seeds (45 -> 51).
|
|
51
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_samples_recorded', 0);
|
|
52
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_budget_breaches', 0);
|
|
53
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_fires_total', 0);
|
|
54
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_misses_total', 0);
|
|
55
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('contract_digest_changes', 0);
|
|
56
|
-
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('bench_runs_total', 0);
|
|
57
|
-
|
|
58
|
-
-- 4. Setting seeds (27 -> 31). All TEXT; '0'/'1' are strings.
|
|
59
|
-
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_enabled', '1');
|
|
60
|
-
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_ring_capacity', '512');
|
|
61
|
-
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_flush_on_idle', '1');
|
|
62
|
-
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('contract_report_enabled', '1');
|
|
63
|
-
|
|
64
|
-
-- 5. `dispose` becomes the seventh instrumented hook (v0.9.0 migration 010
|
|
65
|
-
-- seeded six rows; this is a data seed, not a schema change).
|
|
66
|
-
INSERT OR IGNORE INTO hook_liveness (hook) VALUES ('dispose');
|
|
67
|
-
|
|
68
|
-
-- 6. Schema version.
|
|
69
|
-
INSERT OR IGNORE INTO schema_version (version) VALUES ('011');
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
-- millisecond companions for the two tables whose ORDERING decides outcomes.
|
|
2
|
-
ALTER TABLE tool_calls ADD COLUMN ts_ms INTEGER;
|
|
3
|
-
ALTER TABLE kevin_injections ADD COLUMN injected_at_ms INTEGER;
|
|
4
|
-
|
|
5
|
-
-- conservative backfill: seconds -> ms (x1000). Rows keep their original
|
|
6
|
-
-- second-granularity value in the old column; nothing is rewritten there.
|
|
7
|
-
UPDATE tool_calls SET ts_ms =
|
|
8
|
-
CAST(strftime('%s', ts) AS INTEGER) * 1000
|
|
9
|
-
WHERE ts_ms IS NULL AND ts IS NOT NULL;
|
|
10
|
-
|
|
11
|
-
UPDATE kevin_injections SET injected_at_ms =
|
|
12
|
-
CAST(strftime('%s', injected_at) AS INTEGER) * 1000
|
|
13
|
-
WHERE injected_at_ms IS NULL AND injected_at IS NOT NULL;
|
|
14
|
-
|
|
15
|
-
CREATE INDEX idx_tool_calls_ts_ms ON tool_calls(ts_ms);
|
|
16
|
-
CREATE INDEX idx_injections_injected_ms ON kevin_injections(injected_at_ms);
|
|
17
|
-
|
|
18
|
-
-- metric seeds (C-05 additions carry since=1.1.0 in contract)
|
|
19
|
-
INSERT INTO kevin_metrics (key, value, updated_at) VALUES
|
|
20
|
-
('bench_regression_failures', 0, datetime('now')),
|
|
21
|
-
('forget_requests_total', 0, datetime('now')),
|
|
22
|
-
('forget_tombstones_published', 0, datetime('now'));
|
|
23
|
-
|
|
24
|
-
INSERT INTO schema_version (version) VALUES ('012');
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { MemoryService } from "./MemoryService.js";
|
|
2
|
-
import type { Store } from "./Store.js";
|
|
3
|
-
import type { Metrics } from "./metrics.js";
|
|
4
|
-
/**
|
|
5
|
-
* v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
|
|
6
|
-
*
|
|
7
|
-
* Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
|
|
8
|
-
* its fingerprint recurred enough times (penalizeRecurringReflectors,
|
|
9
|
-
* migration 003), but nothing ever retires those rows — they keep
|
|
10
|
-
* circulating through status filters and inflating counts. The Archiver
|
|
11
|
-
* runs on `session.idle` and retires stale memories whose last activity
|
|
12
|
-
* (`updated_at`, falling back to `last_verified_at`) is older than
|
|
13
|
-
* `archive_after_days` (kevin_settings, seeded '30' in migration 006).
|
|
14
|
-
*
|
|
15
|
-
* Rules:
|
|
16
|
-
* - Only `status = 'stale'` rows with `archived_at IS NULL`.
|
|
17
|
-
* - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
|
|
18
|
-
* (K4-025); archiving it would break kevin_why and the recurrence
|
|
19
|
-
* re-admission path (QualityGate rule 4).
|
|
20
|
-
* - Clock: the injection clock (K5-008) — tests freeze it; production
|
|
21
|
-
* defaults to `new Date()`. Never `Date.now()` inside.
|
|
22
|
-
* - `memories_archived` is bumped by the batch size, once per run.
|
|
23
|
-
* - Pre-006 DBs (no `archived_at`) degrade to a no-op.
|
|
24
|
-
*
|
|
25
|
-
* Archived rows remain queryable with `includeSuperseded: true`-style
|
|
26
|
-
* reads (status filter is exclusive, not destructive).
|
|
27
|
-
*/
|
|
28
|
-
export declare class Archiver {
|
|
29
|
-
private readonly store;
|
|
30
|
-
private readonly memoryService;
|
|
31
|
-
private readonly metrics;
|
|
32
|
-
private readonly now;
|
|
33
|
-
constructor(store: Store, memoryService: MemoryService, metrics?: Metrics | null, now?: () => Date);
|
|
34
|
-
/**
|
|
35
|
-
* Archives every eligible stale memory. Returns the number of rows
|
|
36
|
-
* retired. Idempotent: archived rows are excluded by `archived_at IS
|
|
37
|
-
* NULL`, so a second run archives nothing.
|
|
38
|
-
*/
|
|
39
|
-
run(): number;
|
|
40
|
-
private archiveAfterDays;
|
|
41
|
-
private hasArchivedColumn;
|
|
42
|
-
}
|
package/dist/plugin/Archiver.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { hasArchivedColumn } from "./columns.js";
|
|
2
|
-
/**
|
|
3
|
-
* v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
|
|
4
|
-
*
|
|
5
|
-
* Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
|
|
6
|
-
* its fingerprint recurred enough times (penalizeRecurringReflectors,
|
|
7
|
-
* migration 003), but nothing ever retires those rows — they keep
|
|
8
|
-
* circulating through status filters and inflating counts. The Archiver
|
|
9
|
-
* runs on `session.idle` and retires stale memories whose last activity
|
|
10
|
-
* (`updated_at`, falling back to `last_verified_at`) is older than
|
|
11
|
-
* `archive_after_days` (kevin_settings, seeded '30' in migration 006).
|
|
12
|
-
*
|
|
13
|
-
* Rules:
|
|
14
|
-
* - Only `status = 'stale'` rows with `archived_at IS NULL`.
|
|
15
|
-
* - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
|
|
16
|
-
* (K4-025); archiving it would break kevin_why and the recurrence
|
|
17
|
-
* re-admission path (QualityGate rule 4).
|
|
18
|
-
* - Clock: the injection clock (K5-008) — tests freeze it; production
|
|
19
|
-
* defaults to `new Date()`. Never `Date.now()` inside.
|
|
20
|
-
* - `memories_archived` is bumped by the batch size, once per run.
|
|
21
|
-
* - Pre-006 DBs (no `archived_at`) degrade to a no-op.
|
|
22
|
-
*
|
|
23
|
-
* Archived rows remain queryable with `includeSuperseded: true`-style
|
|
24
|
-
* reads (status filter is exclusive, not destructive).
|
|
25
|
-
*/
|
|
26
|
-
export class Archiver {
|
|
27
|
-
store;
|
|
28
|
-
memoryService;
|
|
29
|
-
metrics;
|
|
30
|
-
now;
|
|
31
|
-
constructor(store, memoryService, metrics, now = () => new Date()) {
|
|
32
|
-
this.store = store;
|
|
33
|
-
this.memoryService = memoryService;
|
|
34
|
-
this.metrics = metrics ?? null;
|
|
35
|
-
this.now = now;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Archives every eligible stale memory. Returns the number of rows
|
|
39
|
-
* retired. Idempotent: archived rows are excluded by `archived_at IS
|
|
40
|
-
* NULL`, so a second run archives nothing.
|
|
41
|
-
*/
|
|
42
|
-
run() {
|
|
43
|
-
if (!this.hasArchivedColumn())
|
|
44
|
-
return 0;
|
|
45
|
-
const days = this.archiveAfterDays();
|
|
46
|
-
if (days <= 0)
|
|
47
|
-
return 0;
|
|
48
|
-
const now = this.now();
|
|
49
|
-
const cutoff = formatSqliteUtc(new Date(now.getTime() - days * 86_400_000));
|
|
50
|
-
this.store
|
|
51
|
-
.prepare(`UPDATE memories
|
|
52
|
-
SET status = 'archived', archived_at = ?, updated_at = ?
|
|
53
|
-
WHERE status = 'stale'
|
|
54
|
-
AND type != 'pattern'
|
|
55
|
-
AND archived_at IS NULL
|
|
56
|
-
AND updated_at < ?`)
|
|
57
|
-
.run(formatSqliteUtc(now), formatSqliteUtc(now), cutoff);
|
|
58
|
-
const changed = this.store.prepare("SELECT changes() AS n").get();
|
|
59
|
-
const n = changed.n;
|
|
60
|
-
if (n > 0)
|
|
61
|
-
this.metrics?.incr("memories_archived", n);
|
|
62
|
-
return n;
|
|
63
|
-
}
|
|
64
|
-
archiveAfterDays() {
|
|
65
|
-
try {
|
|
66
|
-
const raw = this.memoryService.getSetting("archive_after_days", "30");
|
|
67
|
-
const n = Number(raw);
|
|
68
|
-
return Number.isFinite(n) ? n : 30;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return 30;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
hasArchivedColumn() {
|
|
75
|
-
return hasArchivedColumn(this.store);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/** 'YYYY-MM-DD HH:MM:SS' UTC — the format SQLite `datetime('now')` uses,
|
|
79
|
-
* so lexicographic comparisons against `updated_at` are valid. */
|
|
80
|
-
function formatSqliteUtc(d) {
|
|
81
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
82
|
-
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
|
|
83
|
-
}
|