fix: harden daemon and large-index recovery paths (#1562)

* fix: harden indexing recovery and daemon liveness

* test: cover daemon and recovery review gaps

* test: pin that a failure marker never blocks a later successful parse (#1557 retry-discard guard)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Colby McHenry <me@colbymchenry.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Daniil
2026-08-20 12:53:49 -05:00
committed by GitHub
co-authored by Claude Fable 5 danusha2345 Colby McHenry
parent d8f2eeaddf
commit 81e1f4a92f
20 changed files with 680 additions and 83 deletions
+23
View File
@@ -145,6 +145,7 @@ export class DatabaseConnection {
// beginBulkNodeLoad and endBulkNodeLoad): the FTS triggers are missing and
// nodes_fts is stale. Rebuild + recreate so search stays in sync.
conn.healBulkNodeLoad();
conn.healBulkSecondaryIndexes();
// Self-heal a killed session's leftover oversized WAL (#1431) — one
// statSync when healthy, off-thread checkpoint+truncate when not.
@@ -363,6 +364,28 @@ export class DatabaseConnection {
this.endBulkNodeLoad();
}
/** Recreate every secondary index a killed bulk parse/ref/edge window may leave dropped. */
private healBulkSecondaryIndexes(): void {
const names = [...new Set<string>([
...DatabaseConnection.BULK_PARSE_INDEX_NAMES,
...DatabaseConnection.BULK_REF_INDEX_NAMES,
...DatabaseConnection.BULK_EDGE_INDEX_NAMES,
])];
const placeholders = names.map(() => '?').join(',');
const row = this.db
.prepare(`SELECT count(*) AS c FROM sqlite_master WHERE type = 'index' AND name IN (${placeholders})`)
.get(...names) as { c: number } | undefined;
if ((row?.c ?? 0) >= names.length) return;
const schemaPath = path.join(__dirname, 'schema.sql');
const schema = fs.readFileSync(schemaPath, 'utf-8');
for (const idx of names) {
const m = schema.match(new RegExp(`CREATE INDEX IF NOT EXISTS ${idx}\\b[^;]*;`));
if (!m) throw new Error(`schema.sql: index ${idx} not found for crash recovery`);
this.db.exec(m[0]);
}
}
/**
* Recreate the FTS sync triggers from schema.sql — extracted from the file
* rather than duplicated here so the DDL cannot drift from the schema.