@inerrata-corporation/errata 2.0.22-dev.4164 → 2.0.22-dev.4180

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/errata.mjs CHANGED
@@ -1168,6 +1168,11 @@ function versionlessPurl(purl) {
1168
1168
  const lastAt = purl.lastIndexOf("@");
1169
1169
  return lastAt > lastSlash ? purl.slice(0, lastAt) : purl;
1170
1170
  }
1171
+ function splitNameAtVersion(slug2) {
1172
+ const at = slug2.lastIndexOf("@");
1173
+ if (at <= 0) return { name: slug2.trim(), version: null };
1174
+ return { name: slug2.slice(0, at).trim(), version: slug2.slice(at + 1).trim() || null };
1175
+ }
1171
1176
  function parsePackageRef(ref) {
1172
1177
  let rest2 = ref.trim();
1173
1178
  if (rest2.startsWith("pkg:")) {
@@ -1177,9 +1182,7 @@ function parsePackageRef(ref) {
1177
1182
  const q = rest2.search(/[?#]/);
1178
1183
  if (q !== -1) rest2 = rest2.slice(0, q);
1179
1184
  }
1180
- const at = rest2.lastIndexOf("@");
1181
- const name2 = at > 0 ? rest2.slice(0, at) : rest2;
1182
- const version3 = at > 0 ? rest2.slice(at + 1) || null : null;
1185
+ const { name: name2, version: version3 } = splitNameAtVersion(rest2);
1183
1186
  const slug2 = (version3 ? `${name2}@${version3}` : name2).toLowerCase();
1184
1187
  return { name: name2, version: version3, slug: slug2 };
1185
1188
  }
@@ -17746,6 +17749,7 @@ __export(src_exports, {
17746
17749
  sha256Short: () => sha256Short,
17747
17750
  sidecarDropBucket: () => sidecarDropBucket,
17748
17751
  spineDeps: () => spineDeps,
17752
+ splitNameAtVersion: () => splitNameAtVersion,
17749
17753
  splitPnpmKey: () => splitPnpmKey,
17750
17754
  stripAnchorLabels: () => stripAnchorLabels,
17751
17755
  substituteSymbolSummaries: () => substituteSymbolSummaries,
@@ -18133,20 +18137,20 @@ function openDatabase(path3, opts) {
18133
18137
  } catch {
18134
18138
  }
18135
18139
  }
18136
- const wrapStmt = (stmt) => ({
18140
+ const wrapStmt = (stmt2) => ({
18137
18141
  run: (...params) => {
18138
- const r = stmt.run(...coerceParams(params));
18142
+ const r = stmt2.run(...coerceParams(params));
18139
18143
  return {
18140
18144
  changes: Number(r.changes),
18141
18145
  lastInsertRowid: Number(r.lastInsertRowid)
18142
18146
  };
18143
18147
  },
18144
18148
  get: (...params) => {
18145
- const r = stmt.get(...coerceParams(params));
18149
+ const r = stmt2.get(...coerceParams(params));
18146
18150
  return r;
18147
18151
  },
18148
18152
  all: (...params) => {
18149
- const r = stmt.all(...coerceParams(params));
18153
+ const r = stmt2.all(...coerceParams(params));
18150
18154
  return r;
18151
18155
  }
18152
18156
  });
@@ -19130,6 +19134,175 @@ var init_text_index = __esm({
19130
19134
  }
19131
19135
  });
19132
19136
 
19137
+ // ../../packages/local-graph/src/lineage-integrity.ts
19138
+ function stmt(db, sql) {
19139
+ let byDb = statements.get(db);
19140
+ if (!byDb) statements.set(db, byDb = /* @__PURE__ */ new Map());
19141
+ let s = byDb.get(sql);
19142
+ if (!s) byDb.set(sql, s = db.prepare(sql));
19143
+ return s;
19144
+ }
19145
+ function countStrandedEdges(db) {
19146
+ const rows = db.prepare(`SELECT e.type AS type, count(*) AS n FROM edges e WHERE ${STRANDED_EDGE_PREDICATE} GROUP BY e.type ORDER BY n DESC`).all();
19147
+ const byType = {};
19148
+ let total = 0;
19149
+ for (const r of rows) {
19150
+ byType[r.type] = Number(r.n);
19151
+ total += Number(r.n);
19152
+ }
19153
+ return { total, byType };
19154
+ }
19155
+ function countLiveHeadConflicts(db) {
19156
+ const row = db.prepare(
19157
+ `SELECT count(*) AS n FROM (
19158
+ SELECT COALESCE(logical_id, id) AS lid FROM nodes
19159
+ WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
19160
+ GROUP BY lid HAVING count(*) > 1)`
19161
+ ).get();
19162
+ return Number(row?.n ?? 0);
19163
+ }
19164
+ function closeIncidentEdges(db, id, t) {
19165
+ let closed = 0;
19166
+ for (const side of ["from_id", "to_id"]) {
19167
+ const r = stmt(db, `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE ${side} = ? AND valid_to IS NULL AND ${NOT_HISTORY}`).run(t, id);
19168
+ closed += Number(r.changes);
19169
+ }
19170
+ return closed;
19171
+ }
19172
+ function reopenCascadeClosedEdges(db, id, closedAt) {
19173
+ let reopened = 0;
19174
+ for (const [side, other] of [["from_id", "to_id"], ["to_id", "from_id"]]) {
19175
+ const r = stmt(
19176
+ db,
19177
+ `UPDATE edges SET valid_to = NULL
19178
+ WHERE ${side} = ? AND valid_to = ? AND ${NOT_HISTORY}
19179
+ AND EXISTS (SELECT 1 FROM nodes o WHERE o.id = edges.${other} AND o.valid_to IS NULL)`
19180
+ ).run(id, closedAt);
19181
+ reopened += Number(r.changes);
19182
+ }
19183
+ return reopened;
19184
+ }
19185
+ function collapseLiveHeads(db, logicalIds, t) {
19186
+ const headsOf = db.prepare(
19187
+ `SELECT id, version, valid_from FROM (
19188
+ SELECT id, version, valid_from, attrs_json FROM nodes WHERE logical_id = ?1 AND valid_to IS NULL
19189
+ UNION
19190
+ SELECT id, version, valid_from, attrs_json FROM nodes WHERE id = ?1 AND logical_id IS NULL AND valid_to IS NULL
19191
+ ) WHERE ${NOT_HISTORICAL_ROW}`
19192
+ );
19193
+ const continuesInto = db.prepare(
19194
+ `SELECT to_id FROM edges WHERE from_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
19195
+ );
19196
+ const rekeyHead = db.prepare(`UPDATE nodes SET logical_id = ?1 WHERE id = ?1`);
19197
+ const rekeyCopies = db.prepare(
19198
+ `UPDATE nodes SET logical_id = ?1 WHERE logical_id = ?2 AND substr(id, 1, length(?1) + 2) = ?1 || '@v'`
19199
+ );
19200
+ const lineageMembers = db.prepare(
19201
+ `SELECT id FROM nodes WHERE logical_id = ?1 UNION SELECT id FROM nodes WHERE id = ?1`
19202
+ );
19203
+ const closeContinues = db.prepare(
19204
+ `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE from_id = ? AND to_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
19205
+ );
19206
+ let split = 0;
19207
+ const queue = [...new Set(logicalIds)];
19208
+ for (let i2 = 0; i2 < queue.length; i2++) {
19209
+ const lid = queue[i2];
19210
+ const heads = headsOf.all(lid);
19211
+ if (heads.length < 2) continue;
19212
+ const members = new Set(lineageMembers.all(lid).map((r) => r.id));
19213
+ const splitOff = (h) => {
19214
+ for (const { to_id } of continuesInto.all(h.id)) {
19215
+ if (members.has(to_id)) closeContinues.run(t, h.id, to_id);
19216
+ }
19217
+ rekeyHead.run(h.id);
19218
+ rekeyCopies.run(h.id, lid);
19219
+ queue.push(h.id);
19220
+ split++;
19221
+ };
19222
+ const kept = new Set(heads.map((h) => h.id));
19223
+ for (const h of heads) {
19224
+ if (h.id === lid) continue;
19225
+ const refuted = continuesInto.all(h.id).some(
19226
+ (e) => e.to_id !== h.id && kept.has(e.to_id)
19227
+ );
19228
+ if (refuted && kept.size > 1) {
19229
+ splitOff(h);
19230
+ kept.delete(h.id);
19231
+ }
19232
+ }
19233
+ let remaining = heads.filter((h) => kept.has(h.id));
19234
+ if (remaining.length < 2) continue;
19235
+ remaining = [...remaining].sort(
19236
+ (a, b) => Number(b.id === lid) - Number(a.id === lid) || Number(b.version) - Number(a.version) || Number(b.valid_from) - Number(a.valid_from) || (a.id < b.id ? -1 : 1)
19237
+ );
19238
+ for (const h of remaining.slice(1)) splitOff(h);
19239
+ }
19240
+ return split;
19241
+ }
19242
+ function repairLineageIntegrity(db, now) {
19243
+ const historicalRowsClosed = Number(
19244
+ db.prepare(
19245
+ `UPDATE nodes SET valid_to = MAX(valid_from, COALESCE(
19246
+ (SELECT s.valid_from FROM edges s WHERE s.id = 'edge_superseded_' || nodes.id), ?))
19247
+ WHERE valid_to IS NULL AND json_type(attrs_json, '$.historical') = 'true'`
19248
+ ).run(now).changes
19249
+ );
19250
+ const conflicted = db.prepare(
19251
+ `SELECT COALESCE(logical_id, id) AS lid FROM nodes
19252
+ WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
19253
+ GROUP BY lid HAVING count(*) > 1`
19254
+ ).all().map((r) => r.lid);
19255
+ const headsSplit = collapseLiveHeads(db, conflicted, now);
19256
+ const strandedEdgesClosed = Number(
19257
+ db.prepare(
19258
+ `UPDATE edges SET valid_to = MAX(valid_from, COALESCE(
19259
+ (SELECT min(v) FROM (
19260
+ SELECT f.valid_to AS v FROM nodes f WHERE f.id = edges.from_id AND NOT ${PRESENT("f")}
19261
+ UNION ALL
19262
+ SELECT t.valid_to FROM nodes t WHERE t.id = edges.to_id AND NOT ${PRESENT("t")})), ?))
19263
+ WHERE id IN (SELECT e.id FROM edges e WHERE ${STRANDED_EDGE_PREDICATE})`
19264
+ ).run(now).changes
19265
+ );
19266
+ return { historicalRowsClosed, headsSplit, strandedEdgesClosed };
19267
+ }
19268
+ function installLineageIntegrityRepair(db, now = Date.now()) {
19269
+ if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(LINEAGE_INTEGRITY_KEY)) return null;
19270
+ return db.transaction(() => {
19271
+ const report = repairLineageIntegrity(db, now);
19272
+ db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, ?)").run(
19273
+ LINEAGE_INTEGRITY_KEY,
19274
+ JSON.stringify({ at: now, ...report })
19275
+ );
19276
+ return report;
19277
+ });
19278
+ }
19279
+ var statements, HISTORY_EDGE_TYPES, LINEAGE_INTEGRITY_KEY, HISTORY_SQL, NOT_HISTORY, NOT_HISTORICAL_ROW, RETAINS_EDGES, PRESENT, STRANDED_EDGE_PREDICATE;
19280
+ var init_lineage_integrity = __esm({
19281
+ "../../packages/local-graph/src/lineage-integrity.ts"() {
19282
+ "use strict";
19283
+ statements = /* @__PURE__ */ new WeakMap();
19284
+ HISTORY_EDGE_TYPES = [
19285
+ "SUPERSEDED_BY",
19286
+ "CONTINUES",
19287
+ "FORKED_FROM",
19288
+ "REGRESSION_OF",
19289
+ "SPLIT_INTO",
19290
+ "REVEALED_BY"
19291
+ ];
19292
+ LINEAGE_INTEGRITY_KEY = "lineage-integrity-v1";
19293
+ HISTORY_SQL = HISTORY_EDGE_TYPES.map((t) => `'${t}'`).join(",");
19294
+ NOT_HISTORY = `type NOT IN (${HISTORY_SQL})`;
19295
+ NOT_HISTORICAL_ROW = `json_type(attrs_json, '$.historical') IS NOT 'true'`;
19296
+ RETAINS_EDGES = (n) => `(${n}.label = 'Problem'
19297
+ AND json_type(${n}.attrs_json, '$.resolvedAt') IS NOT NULL
19298
+ AND COALESCE(json_extract(${n}.attrs_json, '$.resolvedAs'), '') NOT IN ('duplicate', 'false_positive'))`;
19299
+ PRESENT = (n) => `(${n}.valid_to IS NULL OR ${RETAINS_EDGES(n)})`;
19300
+ STRANDED_EDGE_PREDICATE = `e.valid_to IS NULL AND e.${NOT_HISTORY}
19301
+ AND (NOT EXISTS (SELECT 1 FROM nodes f WHERE f.id = e.from_id AND ${PRESENT("f")})
19302
+ OR NOT EXISTS (SELECT 1 FROM nodes t WHERE t.id = e.to_id AND ${PRESENT("t")}))`;
19303
+ }
19304
+ });
19305
+
19133
19306
  // ../../packages/math/src/constants.ts
19134
19307
  var SURPRISE_MOMENTUM, SURPRISE_LEARNING_RATE, SURPRISE_LEARNING_RATE_SCALING, TIER_HALFLIFE_MULTIPLIER, HIT_RATE_SCALING_ALPHA, BASE_HALF_LIFE_SEQ, MOMENTUM_GATE_FLOOR, SURPRISE_RECENCY_LAMBDA_SEQ, NEMORI_SOFT_WRITE_MIN_COSINE, NEMORI_SOFT_WRITE_MAX_COSINE, NEMORI_DEDUP_COSINE, NEMORI_SOFT_ALPHA_MIN, NEMORI_SOFT_ALPHA_MAX, NEMORI_HIGH_SURPRISE_THRESHOLD, NEMORI_PREDICTION_FLOOR, REVIEW_SURPRISE_THRESHOLD, REVIEW_HITS_THRESHOLD;
19135
19308
  var init_constants = __esm({
@@ -20215,7 +20388,7 @@ function graphStoreNeedsPreparation(path3) {
20215
20388
  for (const match2 of (SCHEMA_SQL + TEMPORAL_INDEX_SQL).matchAll(/CREATE INDEX IF NOT EXISTS (\w+)/g)) {
20216
20389
  if (!indexes.has(match2[1])) return true;
20217
20390
  }
20218
- return [SESSION_REPAIR_KEY, HISTORY_CACHE_FLOOR_KEY, EDIT_PLACEHOLDER_FLOOR_KEY, CLOUD_ATTR_COMPACTION_KEY, HISTORY_ATTR_SLIM_KEY, TEXT_INDEX_KEY].some((key) => !db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
20391
+ return [SESSION_REPAIR_KEY, HISTORY_CACHE_FLOOR_KEY, EDIT_PLACEHOLDER_FLOOR_KEY, CLOUD_ATTR_COMPACTION_KEY, HISTORY_ATTR_SLIM_KEY, TEXT_INDEX_KEY, LINEAGE_INTEGRITY_KEY].some((key) => !db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
20219
20392
  } catch {
20220
20393
  return true;
20221
20394
  } finally {
@@ -20294,6 +20467,7 @@ var init_store = __esm({
20294
20467
  init_cloud_attr_compaction();
20295
20468
  init_history_attr_slim();
20296
20469
  init_text_index();
20470
+ init_lineage_integrity();
20297
20471
  init_src3();
20298
20472
  init_node_select();
20299
20473
  PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
@@ -20519,6 +20693,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20519
20693
  this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
20520
20694
  });
20521
20695
  }
20696
+ installLineageIntegrityRepair(this.db);
20522
20697
  this.stmts = {
20523
20698
  insertNode: this.db.prepare(`
20524
20699
  INSERT INTO nodes (
@@ -20729,6 +20904,12 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20729
20904
  AND (t.id IS NULL OR t.valid_to IS NOT NULL)
20730
20905
  `).all();
20731
20906
  }
20907
+ strandedEdges() {
20908
+ return countStrandedEdges(this.db);
20909
+ }
20910
+ liveHeadConflicts() {
20911
+ return countLiveHeadConflicts(this.db);
20912
+ }
20732
20913
  fileIndexStates(ids) {
20733
20914
  if (ids.length === 0) return [];
20734
20915
  if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
@@ -20897,15 +21078,15 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20897
21078
  findNodeFieldsByLabel(label, attrKeys) {
20898
21079
  for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
20899
21080
  const cacheKey2 = attrKeys.join(",");
20900
- let stmt = this.fieldStmts.get(cacheKey2);
20901
- if (!stmt) {
21081
+ let stmt2 = this.fieldStmts.get(cacheKey2);
21082
+ if (!stmt2) {
20902
21083
  const cols = attrKeys.map((k, i2) => `, ${attrValueSql(`'$.${k}'`)} AS a${i2}, json_type(attrs_json, '$.${k}') AS t${i2}`).join("");
20903
- stmt = this.db.prepare(
21084
+ stmt2 = this.db.prepare(
20904
21085
  `SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
20905
21086
  );
20906
- this.fieldStmts.set(cacheKey2, stmt);
21087
+ this.fieldStmts.set(cacheKey2, stmt2);
20907
21088
  }
20908
- return stmt.all(label).map((r) => {
21089
+ return stmt2.all(label).map((r) => {
20909
21090
  const attrs = {};
20910
21091
  attrKeys.forEach((k, i2) => {
20911
21092
  const v = decodeAttr(r[`a${i2}`], r[`t${i2}`]);
@@ -21025,14 +21206,14 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21025
21206
  }
21026
21207
  nodeLabels(ids) {
21027
21208
  const width = NODE_LABELS_CHUNK;
21028
- const stmt = this.typedEdgeStmt(
21209
+ const stmt2 = this.typedEdgeStmt(
21029
21210
  `SELECT id, label FROM nodes WHERE id IN (${Array.from({ length: width }, () => "?").join(",")})`
21030
21211
  );
21031
21212
  const labels = /* @__PURE__ */ new Map();
21032
21213
  for (let i2 = 0; i2 < ids.length; i2 += width) {
21033
21214
  const chunk = ids.slice(i2, i2 + width);
21034
21215
  while (chunk.length < width) chunk.push(null);
21035
- for (const row of stmt.all(...chunk)) labels.set(row.id, row.label);
21216
+ for (const row of stmt2.all(...chunk)) labels.set(row.id, row.label);
21036
21217
  }
21037
21218
  return labels;
21038
21219
  }
@@ -21231,12 +21412,12 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21231
21412
  return scale;
21232
21413
  }
21233
21414
  typedEdgeStmt(sql) {
21234
- let stmt = this.typedEdgeStmts.get(sql);
21235
- if (!stmt) {
21236
- stmt = this.db.prepare(sql);
21237
- this.typedEdgeStmts.set(sql, stmt);
21415
+ let stmt2 = this.typedEdgeStmts.get(sql);
21416
+ if (!stmt2) {
21417
+ stmt2 = this.db.prepare(sql);
21418
+ this.typedEdgeStmts.set(sql, stmt2);
21238
21419
  }
21239
- return stmt;
21420
+ return stmt2;
21240
21421
  }
21241
21422
  outEdges(nodeId, types) {
21242
21423
  if (!types || types.length === 0) {
@@ -21437,22 +21618,28 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21437
21618
  }
21438
21619
  }
21439
21620
  reviveReobserved(relPaths, workspaceId2, since) {
21440
- let revived = 0;
21621
+ const revived = [];
21441
21622
  const CHUNK2 = 400;
21442
21623
  for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK2) {
21443
21624
  const slice = relPaths.slice(i2, i2 + CHUNK2);
21444
21625
  if (slice.length === 0) continue;
21445
- const r = this.db.prepare(
21446
- `UPDATE nodes SET valid_to = NULL
21626
+ const rows = this.db.prepare(
21627
+ `SELECT id, valid_to AS closedAt, COALESCE(logical_id, id) AS lid FROM nodes
21447
21628
  WHERE valid_to IS NOT NULL
21448
21629
  AND last_updated_at >= ?
21449
21630
  AND json_extract(attrs_json, '$.workspaceId') = ?
21450
- AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
21451
- ).run(since, workspaceId2, ...slice);
21452
- revived += Number(r.changes);
21453
- }
21454
- if (revived > 0) this.mutations++;
21455
- return revived;
21631
+ AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
21632
+ AND json_type(attrs_json, '$.historical') IS NOT 'true'`
21633
+ ).all(since, workspaceId2, ...slice);
21634
+ revived.push(...rows);
21635
+ }
21636
+ if (revived.length === 0) return 0;
21637
+ const reopen = this.db.prepare("UPDATE nodes SET valid_to = NULL WHERE id = ? AND valid_to IS NOT NULL");
21638
+ for (const r of revived) reopen.run(r.id);
21639
+ for (const r of revived) reopenCascadeClosedEdges(this.db, r.id, Number(r.closedAt));
21640
+ collapseLiveHeads(this.db, revived.map((r) => r.lid), since);
21641
+ this.mutations++;
21642
+ return revived.length;
21456
21643
  }
21457
21644
  incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
21458
21645
  if (edgeTypes.length === 0) return { count: 0, total: 0 };
@@ -21598,8 +21785,9 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21598
21785
  this.stmts.advanceLive.run({ live_id: liveId, t });
21599
21786
  return frozenId;
21600
21787
  }
21601
- closeNode(id, t) {
21602
- this.stmts.closeNode.run({ id, t });
21788
+ closeNode(id, t, opts = {}) {
21789
+ const r = this.stmts.closeNode.run({ id, t });
21790
+ if (Number(r.changes) > 0 && !opts.retainEdges) closeIncidentEdges(this.db, id, t);
21603
21791
  }
21604
21792
  closeEdge(id, t) {
21605
21793
  this.stmts.closeEdge.run({ id, t });
@@ -23575,7 +23763,7 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
23575
23763
  }
23576
23764
  function foldSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
23577
23765
  const report = { ...EMPTY_FOLD_REPORT };
23578
- const stmt = statement.trim();
23766
+ const stmt2 = statement.trim();
23579
23767
  if (!isAnchorableCodePath(relPath)) {
23580
23768
  report.noFile = true;
23581
23769
  return report;
@@ -23585,7 +23773,7 @@ function foldSameAnchorProblem(store, relPath, workspaceId2, statement, source,
23585
23773
  report.noFile = true;
23586
23774
  return report;
23587
23775
  }
23588
- const selfId = identityId({ kind: "DesignProblem", statement: stmt });
23776
+ const selfId = identityId({ kind: "DesignProblem", statement: stmt2 });
23589
23777
  let stmtVec = null;
23590
23778
  let best = null;
23591
23779
  let bestScout = null;
@@ -23599,11 +23787,11 @@ function foldSameAnchorProblem(store, relPath, workspaceId2, statement, source,
23599
23787
  continue;
23600
23788
  }
23601
23789
  report.candidates++;
23602
- const score2 = tokenJaccard(stmt, cand.description);
23790
+ const score2 = tokenJaccard(stmt2, cand.description);
23603
23791
  if (score2 > report.bestJaccard) report.bestJaccard = score2;
23604
23792
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
23605
23793
  if (!best) {
23606
- stmtVec ??= embed(stmt);
23794
+ stmtVec ??= embed(stmt2);
23607
23795
  const hc = cosine(stmtVec, embed(cand.description));
23608
23796
  if (hc > report.bestHashCos) report.bestHashCos = hc;
23609
23797
  if ((hc >= SAME_ANCHOR_SCOUT_MIN_COSINE || score2 >= SAME_ANCHOR_NOMINATE_JACCARD) && (!bestScout || hc > bestScout.hashCos)) {
@@ -23650,11 +23838,11 @@ function foldSameAnchorProblem(store, relPath, workspaceId2, statement, source,
23650
23838
  return report;
23651
23839
  }
23652
23840
  function findResolvedAnchorMatch(store, relPath, workspaceId2, statement, kind = "problem") {
23653
- const stmt = statement.trim();
23841
+ const stmt2 = statement.trim();
23654
23842
  if (!isAnchorableCodePath(relPath)) return null;
23655
23843
  const file2 = resolveFileNode(store, relPath, workspaceId2);
23656
23844
  if (!file2) return null;
23657
- const selfId = identityId({ kind: "DesignProblem", statement: stmt });
23845
+ const selfId = identityId({ kind: "DesignProblem", statement: stmt2 });
23658
23846
  let best = null;
23659
23847
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
23660
23848
  const cand = store.getNode(e.from);
@@ -23664,7 +23852,7 @@ function findResolvedAnchorMatch(store, relPath, workspaceId2, statement, kind =
23664
23852
  if (cand.id === selfId) continue;
23665
23853
  const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
23666
23854
  if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
23667
- const score2 = tokenJaccard(stmt, cand.description);
23855
+ const score2 = tokenJaccard(stmt2, cand.description);
23668
23856
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) {
23669
23857
  best = { id: cand.id, score: score2 };
23670
23858
  }
@@ -26846,8 +27034,10 @@ __export(src_exports2, {
26846
27034
  FOLD_SCOUT_AUTO_COSINE: () => FOLD_SCOUT_AUTO_COSINE,
26847
27035
  FOLD_SCOUT_MIN_COSINE: () => FOLD_SCOUT_MIN_COSINE,
26848
27036
  FOLD_SCOUT_MIN_JACCARD: () => FOLD_SCOUT_MIN_JACCARD,
27037
+ HISTORY_EDGE_TYPES: () => HISTORY_EDGE_TYPES,
26849
27038
  JOINT_COMMUNITY_EDGES: () => JOINT_COMMUNITY_EDGES,
26850
27039
  JOINT_COMMUNITY_LABELS: () => JOINT_COMMUNITY_LABELS,
27040
+ LINEAGE_INTEGRITY_KEY: () => LINEAGE_INTEGRITY_KEY,
26851
27041
  MAX_ANCHOR_FILES: () => MAX_ANCHOR_FILES,
26852
27042
  MECHANISMS: () => MECHANISMS,
26853
27043
  MIN_INTENT_TOKENS: () => MIN_INTENT_TOKENS,
@@ -26889,11 +27079,15 @@ __export(src_exports2, {
26889
27079
  clearRevisit: () => clearRevisit,
26890
27080
  clearSettledFixCandidates: () => clearSettledFixCandidates,
26891
27081
  closableByCaller: () => closableByCaller,
27082
+ closeIncidentEdges: () => closeIncidentEdges,
27083
+ collapseLiveHeads: () => collapseLiveHeads,
26892
27084
  communityInductionRequests: () => communityInductionRequests,
26893
27085
  communitySeeds: () => communitySeeds,
26894
27086
  compareSemver: () => compareSemver,
26895
27087
  confirmFixHeld: () => confirmFixHeld,
26896
27088
  corroborateClaim: () => corroborateClaim,
27089
+ countLiveHeadConflicts: () => countLiveHeadConflicts,
27090
+ countStrandedEdges: () => countStrandedEdges,
26897
27091
  creditAssignment: () => creditAssignment,
26898
27092
  crystallize: () => crystallize,
26899
27093
  deriveContextFromAnchors: () => deriveContextFromAnchors,
@@ -26924,6 +27118,7 @@ __export(src_exports2, {
26924
27118
  induceTriage: () => induceTriage,
26925
27119
  ingestDesignProblem: () => ingestDesignProblem,
26926
27120
  ingestWitnessedVulnerability: () => ingestWitnessedVulnerability,
27121
+ installLineageIntegrityRepair: () => installLineageIntegrityRepair,
26927
27122
  isConstraintProblem: () => isConstraintProblem,
26928
27123
  isFragmentFixNote: () => isFragmentFixNote,
26929
27124
  isPlaceholderStatement: () => isPlaceholderStatement,
@@ -26979,7 +27174,9 @@ __export(src_exports2, {
26979
27174
  reinforceSameAnchorProblem: () => reinforceSameAnchorProblem,
26980
27175
  relateClaims: () => relateClaims,
26981
27176
  reopenAutoClosedProblems: () => reopenAutoClosedProblems,
27177
+ reopenCascadeClosedEdges: () => reopenCascadeClosedEdges,
26982
27178
  repairLegacyAnchorsFromStatement: () => repairLegacyAnchorsFromStatement,
27179
+ repairLineageIntegrity: () => repairLineageIntegrity,
26983
27180
  resolveDesignProblemById: () => resolveDesignProblemById,
26984
27181
  resolveDesignProblemByStatement: () => resolveDesignProblemByStatement,
26985
27182
  resolveEnclosingSymbol: () => resolveEnclosingSymbol,
@@ -27029,6 +27226,7 @@ var init_src5 = __esm({
27029
27226
  init_tools();
27030
27227
  init_principle_sync();
27031
27228
  init_mechanism_liveness();
27229
+ init_lineage_integrity();
27032
27230
  init_wm_calibration();
27033
27231
  }
27034
27232
  });
@@ -33292,7 +33490,7 @@ function detectResolution(opts, ev) {
33292
33490
  function closeProblem(store, problemId, t) {
33293
33491
  const n = store.getNode(problemId);
33294
33492
  if (!n || (n.validTo ?? null) !== null) return false;
33295
- store.closeNode(problemId, t);
33493
+ store.closeNode(problemId, t, { retainEdges: true });
33296
33494
  store.updateNode(problemId, { attrs: { ...n.attrs, resolvedAt: t } });
33297
33495
  return true;
33298
33496
  }
@@ -37984,9 +38182,11 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
37984
38182
  signatureAfter: a.signature
37985
38183
  });
37986
38184
  }
38185
+ const repointable = /* @__PURE__ */ new Map();
37987
38186
  store.transaction(() => {
37988
38187
  for (const nid of changedIds) store.freezeNode(nid, t);
37989
38188
  for (const nid of removedIds) {
38189
+ repointable.set(nid, store.inEdges(nid, ["CALLS", "ACCESSES", "ANCHORED_AT"]));
37990
38190
  for (const e of store.outEdges(nid)) store.closeEdge(e.id, t);
37991
38191
  store.closeNode(nid, t);
37992
38192
  }
@@ -38075,8 +38275,8 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
38075
38275
  version: b.version + 1
38076
38276
  });
38077
38277
  store.mergeEdge(continuesEdge(m, t));
38078
- for (const e of store.inEdges(m.fromNid, ["CALLS", "ACCESSES", "ANCHORED_AT"])) {
38079
- if (e.validTo != null) continue;
38278
+ for (const e of repointable.get(m.fromNid) ?? []) {
38279
+ if (e.from === m.fromNid || !store.liveNodeIds([e.from]).has(e.from)) continue;
38080
38280
  const newId = edgeId(e.from, e.type, m.toNid);
38081
38281
  if (!store.getEdge(newId)) {
38082
38282
  store.mergeEdge({ ...e, id: newId, to: m.toNid, lastSeenAt: t });
@@ -44168,10 +44368,10 @@ function collectDocstrings(root, records, out2) {
44168
44368
  }
44169
44369
  }
44170
44370
  }
44171
- function docstringIn(stmt) {
44172
- if (!stmt) return null;
44173
- if (stmt.type !== "expression_statement") return null;
44174
- const expr = stmt.namedChild(0);
44371
+ function docstringIn(stmt2) {
44372
+ if (!stmt2) return null;
44373
+ if (stmt2.type !== "expression_statement") return null;
44374
+ const expr = stmt2.namedChild(0);
44175
44375
  if (!expr) return null;
44176
44376
  if (expr.type !== "string") return null;
44177
44377
  const text = expr.text;
@@ -47769,7 +47969,7 @@ var init_mcp = __esm({
47769
47969
  },
47770
47970
  {
47771
47971
  name: "errata.health",
47772
- description: "Graph-quality + backlog snapshot \u2014 the dashboard's health view, for an agent. Returns counts of: stranded symbols (a live edge points at a non-live node \u2014 invisible to name resolution), open Problems, open Problems with NO proposed fix, retracted false-positives + dissolutions (the agentic-misread rate \u2014 PLAN_RETRACTION \xA7D), ungrounded-but-'derived' Claims (belief-layer noise), and facts needing revisit. Use to triage the workspace's state or sanity-check the graph before trusting a transitive answer.",
47972
+ description: "Graph-quality + backlog snapshot \u2014 the dashboard's health view, for an agent. Returns counts of: stranded symbols (a live File DEFINES/CONTAINS edge points at a non-live node \u2014 invisible to name resolution), stranded edges (ANY live non-history edge with a closed endpoint, by type), lineages with more than one live head, open Problems, open Problems with NO proposed fix, retracted false-positives + dissolutions (the agentic-misread rate \u2014 PLAN_RETRACTION \xA7D), ungrounded-but-'derived' Claims (belief-layer noise), and facts needing revisit. Use to triage the workspace's state or sanity-check the graph before trusting a transitive answer.",
47773
47973
  inputSchema: { type: "object", properties: {} },
47774
47974
  handler: (_args, store) => {
47775
47975
  const unresolved2 = store.selectNodes({
@@ -47782,10 +47982,16 @@ var init_mcp = __esm({
47782
47982
  const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
47783
47983
  const retracted = (resolvedAs) => store.countNodes({ labels: ["Problem"], includeClosed: true, where: [{ attr: "resolvedAs", eq: resolvedAs }] });
47784
47984
  const ungroundedDerived = store.selectNodes({ labels: ["Claim"], attrs: ["groundedSupport"], where: [{ attr: "crystallized", eq: "derived" }] }).filter((c) => Number(c.attrs["groundedSupport"] ?? 0) === 0);
47985
+ const stranded = store.strandedEdges();
47785
47986
  return {
47786
47987
  nodes: store.nodeCount(),
47787
47988
  edges: store.edgeCount(),
47788
47989
  strandedSymbols: findStrandedSymbols(store).length,
47990
+ // strandedSymbols only sees File-rooted lineage; it read 0 while ~105k
47991
+ // live edges hung off closed nodes (2026-10-12). This counts all of them.
47992
+ strandedEdges: stranded.total,
47993
+ strandedEdgesByType: stranded.byType,
47994
+ liveHeadConflicts: store.liveHeadConflicts(),
47789
47995
  openProblems: open2.length,
47790
47996
  openProblemsWithoutFix: noFix.length,
47791
47997
  designConstraints: constraints.length,
@@ -48439,10 +48645,15 @@ function formatHotspotsMd(r) {
48439
48645
  }
48440
48646
  return lines.join("\n") + "\n";
48441
48647
  }
48648
+ function strandedTypes(byType) {
48649
+ const top = Object.entries(byType ?? {}).sort((a, b) => b[1] - a[1]).slice(0, 3);
48650
+ return top.length ? ` (${top.map(([t, n]) => `${t} ${n}`).join(", ")})` : "";
48651
+ }
48442
48652
  function formatHealthMd(r) {
48443
48653
  return `${head("health")}
48444
48654
  nodes ${r.nodes} \xB7 edges ${r.edges}
48445
48655
  stranded symbols: ${r.strandedSymbols}
48656
+ stranded edges: ${r.strandedEdges ?? 0}${strandedTypes(r.strandedEdgesByType)} \xB7 lineages with >1 live head: ${r.liveHeadConflicts ?? 0}
48446
48657
  open problems: ${r.openProblems} (${r.openProblemsWithoutFix} without a proposed fix)
48447
48658
  retracted: ${r.retractedFalsePositive} false-positive \xB7 ${r.retractedDissolution} dissolution
48448
48659
  ungrounded "derived" claims: ${r.ungroundedDerivedClaims}
@@ -81468,15 +81679,15 @@ function alreadyDone(configDir) {
81468
81679
  }
81469
81680
  }
81470
81681
  function replay(root) {
81471
- const statements = /* @__PURE__ */ new Set();
81682
+ const statements2 = /* @__PURE__ */ new Set();
81472
81683
  const citedByFix = /* @__PURE__ */ new Set();
81473
81684
  const dir = claudeProjectDir(root, homedir5());
81474
- if (!existsSync20(dir)) return { statements, citedByFix };
81685
+ if (!existsSync20(dir)) return { statements: statements2, citedByFix };
81475
81686
  let names;
81476
81687
  try {
81477
81688
  names = readdirSync8(dir).filter((n) => n.endsWith(".jsonl"));
81478
81689
  } catch {
81479
- return { statements, citedByFix };
81690
+ return { statements: statements2, citedByFix };
81480
81691
  }
81481
81692
  for (const f of names) {
81482
81693
  let lines;
@@ -81497,7 +81708,7 @@ function replay(root) {
81497
81708
  for (const b of rec.message.content) {
81498
81709
  if (b?.type !== "text" || typeof b.text !== "string") continue;
81499
81710
  for (const tag of parseInlineTags(b.text)) {
81500
- if (tag.kind === "constraint" && tag.statement) statements.add(tag.statement);
81711
+ if (tag.kind === "constraint" && tag.statement) statements2.add(tag.statement);
81501
81712
  if (tag.kind === "fix") {
81502
81713
  const t = tag;
81503
81714
  if (t.handle) citedByFix.add(t.handle);
@@ -81507,11 +81718,11 @@ function replay(root) {
81507
81718
  }
81508
81719
  }
81509
81720
  }
81510
- return { statements, citedByFix };
81721
+ return { statements: statements2, citedByFix };
81511
81722
  }
81512
81723
  function backfillConstraintKind(store, opts) {
81513
81724
  if (!opts.force && !opts.dryRun && alreadyDone(opts.configDir)) return EMPTY;
81514
- const { statements, citedByFix } = replay(opts.root);
81725
+ const { statements: statements2, citedByFix } = replay(opts.root);
81515
81726
  const report = {
81516
81727
  skipped: false,
81517
81728
  stamped: 0,
@@ -81525,7 +81736,7 @@ function backfillConstraintKind(store, opts) {
81525
81736
  const missing = [];
81526
81737
  const seen = /* @__PURE__ */ new Set();
81527
81738
  const work = [];
81528
- for (const statement of statements) {
81739
+ for (const statement of statements2) {
81529
81740
  const id = designProblemId(statement);
81530
81741
  if (seen.has(id)) continue;
81531
81742
  seen.add(id);
@@ -85832,7 +86043,7 @@ function createWatchBreaker(deps) {
85832
86043
  }
85833
86044
 
85834
86045
  // src/engine.ts
85835
- var DAEMON_VERSION = true ? "2.0.22-dev.4164" : "2.0.0-alpha.0";
86046
+ var DAEMON_VERSION = true ? "2.0.22-dev.4180" : "2.0.0-alpha.0";
85836
86047
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
85837
86048
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
85838
86049
  var GIT_OP_MUTE_MS = 4e3;