pi-mega-compact 0.4.2 → 0.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
5
5
  "type": "module",
6
6
  "license": "BSD-2-Clause",
@@ -174,6 +174,38 @@ test("checkAllIntegrity covers every session", () => {
174
174
  assert.ok(reports.every((r: { ok: boolean }) => r.ok));
175
175
  });
176
176
 
177
+ // --- schema migration (v0.4.2: original_token_estimate) -------------------
178
+
179
+ test("Sprint 10 migration: pre-0.4.2 db gains original_token_estimate and repoStats works", () => {
180
+ const dir = join(baseTmp, `run-${counter++}`);
181
+ // Simulate a v0.4.1-era store: openStore() builds the full current schema,
182
+ // then drop the original_token_estimate column that 0.4.2 added. CREATE TABLE
183
+ // IF NOT EXISTS is a no-op on an existing table, which is exactly why the
184
+ // shipped v0.4.2 crashed at runtime on old repos ("no such column").
185
+ const db = openStore(dir);
186
+ db.exec("ALTER TABLE context_chunks DROP COLUMN original_token_estimate;");
187
+ closeStore(dir);
188
+
189
+ // Re-open through the real code path — must ALTER the column in, not crash.
190
+ const vs = new VectorStore({ dedupSim: 0.9, stateDir: dir });
191
+ vs.add({
192
+ sessionId: "sess_mig",
193
+ summary: "legacy region",
194
+ regionText: "some region text to compact",
195
+ tokenEstimate: 100,
196
+ originalTokenEstimate: 500,
197
+ timestamp: 1,
198
+ });
199
+ const repo = vs.repoStats();
200
+ // No "no such column" crash = migration succeeded; totals reflect the new col.
201
+ assert.equal(repo.checkpointCount, 1);
202
+ assert.equal(repo.originalTokens, 500, "originalTokens read from migrated column");
203
+ // Re-open a second time to prove idempotency (column already exists).
204
+ closeStore(dir);
205
+ const vs2 = new VectorStore({ dedupSim: 0.9, stateDir: dir });
206
+ assert.equal(vs2.repoStats().originalTokens, 500, "stable across re-open");
207
+ });
208
+
177
209
  // --- cleanup ---------------------------------------------------------------
178
210
 
179
211
  test("Sprint 10 cleanup", () => {
@@ -190,6 +190,12 @@ function initSchema(db: Database.Database): void {
190
190
  tokenize='trigram'
191
191
  );
192
192
  `);
193
+ // Idempotent column migrations. `CREATE TABLE IF NOT EXISTS` is a no-op on a
194
+ // pre-existing table, so new columns added to context_chunks after a store was
195
+ // first created (e.g. original_token_estimate in v0.4.2) must be ALTERed in for
196
+ // databases created by an older version — otherwise repoStats()/upsert crash
197
+ // with "no such column" and the extension fails to load. Additive only.
198
+ ensureColumn(db, "context_chunks", "original_token_estimate", "INTEGER");
193
199
  const v = db.prepare("SELECT value FROM meta WHERE key='schema_version'").get() as
194
200
  | { value: string }
195
201
  | undefined;
@@ -198,6 +204,19 @@ function initSchema(db: Database.Database): void {
198
204
  }
199
205
  }
200
206
 
207
+ /**
208
+ * Add `column` (with `decl`, e.g. "INTEGER") to `table` if it does not already
209
+ * exist. Idempotent: checks PRAGMA table_info first, so it is safe to run on
210
+ * every open. Table/column/decl are code-controlled constants (never user
211
+ * input), so the unavoidable identifier interpolation here does not violate
212
+ * PREVENT-002 (no external data reaches this SQL).
213
+ */
214
+ function ensureColumn(db: Database.Database, table: string, column: string, decl: string): void {
215
+ const cols = db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
216
+ if (cols.some((c) => c.name === column)) return;
217
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${decl}`);
218
+ }
219
+
201
220
  /** Read a string-valued meta key (or undefined). Used for cumulative counters. */
202
221
  export function getMeta(key: string, stateDir: string = getStateDir()): string | undefined {
203
222
  const db = openStore(stateDir);