@inerrata-corporation/errata 2.0.2-dev.545 → 2.0.2-dev.554

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.
@@ -16580,6 +16580,16 @@ var SqliteGraphStore = class {
16580
16580
  if (revived > 0) this.mutations++;
16581
16581
  return revived;
16582
16582
  }
16583
+ recentEdgeAttrCoverage(marker, attr, limit) {
16584
+ const r = this.db.prepare(
16585
+ `SELECT COUNT(*) AS total,
16586
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
16587
+ FROM (SELECT attrs_json FROM edges
16588
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
16589
+ ORDER BY created_at DESC LIMIT ?)`
16590
+ ).get(attr, `%${marker}%`, limit);
16591
+ return { count: Number(r.count), total: Number(r.total) };
16592
+ }
16583
16593
  liveNodeIds(ids) {
16584
16594
  const live = /* @__PURE__ */ new Set();
16585
16595
  const CHUNK = 900;
package/errata.mjs CHANGED
@@ -17255,6 +17255,16 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
17255
17255
  if (revived > 0) this.mutations++;
17256
17256
  return revived;
17257
17257
  }
17258
+ recentEdgeAttrCoverage(marker, attr, limit) {
17259
+ const r = this.db.prepare(
17260
+ `SELECT COUNT(*) AS total,
17261
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
17262
+ FROM (SELECT attrs_json FROM edges
17263
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
17264
+ ORDER BY created_at DESC LIMIT ?)`
17265
+ ).get(attr, `%${marker}%`, limit);
17266
+ return { count: Number(r.count), total: Number(r.total) };
17267
+ }
17258
17268
  liveNodeIds(ids) {
17259
17269
  const live = /* @__PURE__ */ new Set();
17260
17270
  const CHUNK = 900;
@@ -21850,14 +21860,24 @@ var init_principle_sync = __esm({
21850
21860
 
21851
21861
  // ../../packages/local-graph/src/mechanism-liveness.ts
21852
21862
  function evaluateFed(store, d) {
21863
+ if (d.fedEdges) {
21864
+ const { count: count2, total } = store.recentEdgeAttrCoverage(
21865
+ d.fedEdges.marker,
21866
+ d.fedEdges.attr,
21867
+ d.fedEdges.recent
21868
+ );
21869
+ return { count: count2, total, fraction: total === 0 ? 0 : count2 / total };
21870
+ }
21871
+ if (!d.fed) return { count: 0, total: 0, fraction: 0 };
21853
21872
  const column = COLUMN_INPUTS[d.fed.attr];
21854
- const has = (n) => column ? column(n) : n.attrs[d.fed.attr] !== void 0;
21855
- const nodes = d.fed.labels.flatMap((label) => store.findNodesByLabel(label));
21873
+ const fed = d.fed;
21874
+ const has = (n) => column ? column(n) : n.attrs[fed.attr] !== void 0;
21875
+ const nodes = fed.labels.flatMap((label) => store.findNodesByLabel(label));
21856
21876
  let window2 = nodes;
21857
- if (d.fed.recent !== void 0) {
21858
- window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, d.fed.recent);
21877
+ if (fed.recent !== void 0) {
21878
+ window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, fed.recent);
21859
21879
  }
21860
- if (d.fed.sinceFirstStamp) {
21880
+ if (fed.sinceFirstStamp) {
21861
21881
  let anchor = Infinity;
21862
21882
  for (const n of window2) if (has(n) && n.createdAt < anchor) anchor = n.createdAt;
21863
21883
  window2 = anchor === Infinity ? [] : window2.filter((n) => n.createdAt >= anchor);
@@ -21877,7 +21897,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
21877
21897
  const stalled = backlog !== void 0 && backlog > 0 && effectCount === 0 && (inv?.runs ?? 0) >= STALL_MIN_RUNS && span >= STALL_MIN_SPAN_MS && (backlogWas === void 0 || backlog >= backlogWas);
21878
21898
  let verdict;
21879
21899
  if (!inv || inv.runs === 0) verdict = "never-invoked";
21880
- else if (fed.fraction < d.fed.minFraction) verdict = "starved";
21900
+ else if (fed.fraction < (d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0)) verdict = "starved";
21881
21901
  else if (stalled) verdict = "stalled";
21882
21902
  else if (effectCount === 0) verdict = "no-effect";
21883
21903
  else verdict = "ok";
@@ -21888,7 +21908,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
21888
21908
  fedCount: fed.count,
21889
21909
  fedTotal: fed.total,
21890
21910
  fedFraction: fed.fraction,
21891
- minFraction: d.fed.minFraction,
21911
+ minFraction: d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0,
21892
21912
  ...inv ? { lastInvokedMs: inv.lastTs } : {},
21893
21913
  runs: inv?.runs ?? 0,
21894
21914
  effectCount,
@@ -22004,6 +22024,20 @@ var init_mechanism_liveness = __esm({
22004
22024
  fed: { labels: ["File"], attr: "relPath", minFraction: 0.9 },
22005
22025
  effectCounter: "reaped"
22006
22026
  },
22027
+ {
22028
+ id: "prior-corroboration",
22029
+ what: "grounds a cited prior by overlap with what the session actually touched",
22030
+ // CAPTURE mints the edges and stamps the verdict; the fed metric is the
22031
+ // corroborated fraction of the newest priorTag edges. Measured 2026-08-08:
22032
+ // 1 of 139 recent despite 84 having a live grounding path — reading STARVED
22033
+ // is the honest state until the touched-set plumbing is fixed
22034
+ // (VS-corroboration-rate); an alarm on a genuinely broken channel is what
22035
+ // alarms are for.
22036
+ pass: "capture",
22037
+ fedEdges: { marker: '"priorTag":true', attr: "corroborated", minFraction: 0.02, recent: 100 },
22038
+ effectCounter: "corroboratedEdges",
22039
+ note: "expected STARVED until VS-corroboration-rate lands \u2014 the channel is genuinely starved"
22040
+ },
22007
22041
  {
22008
22042
  id: "liveness-watch",
22009
22043
  what: "the daemon-side schedule of this very verdict \u2014 alarms when a mechanism stops working",
@@ -29268,12 +29302,12 @@ function loadSymbolSummaryCache(configDir) {
29268
29302
  }
29269
29303
  return { version: 1, entries: {} };
29270
29304
  }
29271
- function saveSymbolSummaryCache(configDir, cache) {
29272
- writeFileSync7(symbolSummariesPath(configDir), JSON.stringify(cache, null, 2), "utf8");
29305
+ function saveSymbolSummaryCache(configDir, cache2) {
29306
+ writeFileSync7(symbolSummariesPath(configDir), JSON.stringify(cache2, null, 2), "utf8");
29273
29307
  }
29274
- function summariesByBodyHash(cache) {
29308
+ function summariesByBodyHash(cache2) {
29275
29309
  const m = /* @__PURE__ */ new Map();
29276
- for (const [bodyHash, e] of Object.entries(cache.entries)) {
29310
+ for (const [bodyHash, e] of Object.entries(cache2.entries)) {
29277
29311
  if (!e.rejected && typeof e.summary === "string" && e.summary.trim()) m.set(bodyHash, e.summary);
29278
29312
  }
29279
29313
  return m;
@@ -29355,7 +29389,7 @@ function readSnippet(workspaceRoot, attrs) {
29355
29389
  async function runSymbolSummarySweep(store, workspaceRoot, configDir, summarizer, opts = {}) {
29356
29390
  const maxSymbols = opts.maxSymbols ?? DEFAULT_MAX_SYMBOLS_PER_SWEEP;
29357
29391
  const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);
29358
- const cache = loadSymbolSummaryCache(configDir);
29392
+ const cache2 = loadSymbolSummaryCache(configDir);
29359
29393
  const candidates = [];
29360
29394
  const seen = /* @__PURE__ */ new Set();
29361
29395
  const docs = buildDocIndex(store);
@@ -29363,7 +29397,7 @@ async function runSymbolSummarySweep(store, workspaceRoot, configDir, summarizer
29363
29397
  for (const n of store.findNodesByLabel(label)) {
29364
29398
  const bodyHash = n.attrs["bodyHash"];
29365
29399
  if (typeof bodyHash !== "string" || !bodyHash || seen.has(bodyHash)) continue;
29366
- if (cache.entries[bodyHash]) continue;
29400
+ if (cache2.entries[bodyHash]) continue;
29367
29401
  const name2 = n.description;
29368
29402
  if (!name2 || !isDistinctiveIdentifier(name2)) continue;
29369
29403
  seen.add(bodyHash);
@@ -29395,14 +29429,14 @@ async function runSymbolSummarySweep(store, workspaceRoot, configDir, summarizer
29395
29429
  const phrase = phrases[j];
29396
29430
  if (phrase == null) continue;
29397
29431
  if (summaryRejectionReason(phrase, isKnown) === null) {
29398
- cache.entries[sym.bodyHash] = { summary: phrase, model: SUMMARY_MODEL, createdAt: now };
29432
+ cache2.entries[sym.bodyHash] = { summary: phrase, model: SUMMARY_MODEL, createdAt: now };
29399
29433
  generated++;
29400
29434
  } else {
29401
- cache.entries[sym.bodyHash] = { rejected: true, model: SUMMARY_MODEL, createdAt: now };
29435
+ cache2.entries[sym.bodyHash] = { rejected: true, model: SUMMARY_MODEL, createdAt: now };
29402
29436
  rejected++;
29403
29437
  }
29404
29438
  }
29405
- saveSymbolSummaryCache(configDir, cache);
29439
+ saveSymbolSummaryCache(configDir, cache2);
29406
29440
  }
29407
29441
  return { generated, rejected, remaining };
29408
29442
  }
@@ -48843,8 +48877,8 @@ var init_report_render = __esm({
48843
48877
 
48844
48878
  // src/cli.ts
48845
48879
  init_src6();
48846
- import { closeSync as closeSync2, existsSync as existsSync28, openSync as openSync2, readFileSync as readFileSync27, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
48847
- import { join as join31 } from "node:path";
48880
+ import { closeSync as closeSync2, existsSync as existsSync29, openSync as openSync2, readFileSync as readFileSync28, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
48881
+ import { join as join32 } from "node:path";
48848
48882
  import { spawn as spawn3 } from "node:child_process";
48849
48883
 
48850
48884
  // src/daemon.ts
@@ -49071,14 +49105,14 @@ var Response2 = class _Response {
49071
49105
  }
49072
49106
  }
49073
49107
  get headers() {
49074
- const cache = this[cacheKey];
49075
- if (cache) {
49076
- if (!(cache[2] instanceof Headers)) {
49077
- cache[2] = new Headers(
49078
- cache[2] || { "content-type": "text/plain; charset=UTF-8" }
49108
+ const cache2 = this[cacheKey];
49109
+ if (cache2) {
49110
+ if (!(cache2[2] instanceof Headers)) {
49111
+ cache2[2] = new Headers(
49112
+ cache2[2] || { "content-type": "text/plain; charset=UTF-8" }
49079
49113
  );
49080
49114
  }
49081
- return cache[2];
49115
+ return cache2[2];
49082
49116
  }
49083
49117
  return this[getResponseCache]().headers;
49084
49118
  }
@@ -51964,13 +51998,13 @@ function mintPriorEdge(store, source, target, sentence, touched, ts) {
51964
51998
  } catch {
51965
51999
  }
51966
52000
  }
51967
- return true;
52001
+ return { corroborated };
51968
52002
  }
51969
52003
  function harvestInlineTags(store, text, opts) {
51970
52004
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
51971
52005
  const mintPriors = opts.mintPriors ?? true;
51972
52006
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
51973
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], causalLinks: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
52007
+ const plan = { priorEdges: 0, corroboratedEdges: 0, problems: [], fixes: [], triages: [], causalLinks: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
51974
52008
  const tags = parseInlineTags(text);
51975
52009
  const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
51976
52010
  const bindSymptom = (seq, threadId) => {
@@ -52163,11 +52197,23 @@ function harvestInlineTags(store, text, opts) {
52163
52197
  plan.corroborations.push({ nodeId: targetId, witnessKey });
52164
52198
  }
52165
52199
  }
52166
- if (mintPriors && source && target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
52200
+ if (mintPriors && source && target) {
52201
+ const m = mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts);
52202
+ if (m) {
52203
+ plan.priorEdges++;
52204
+ if (m.corroborated) plan.corroboratedEdges++;
52205
+ }
52206
+ }
52167
52207
  } else if (mintPriors && source) {
52168
52208
  const targetId = resolveHandle(store, tag.handle, opts.handleMap);
52169
52209
  const target = targetId ? store.getNode(targetId) : null;
52170
- if (target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
52210
+ if (target) {
52211
+ const m = mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts);
52212
+ if (m) {
52213
+ plan.priorEdges++;
52214
+ if (m.corroborated) plan.corroboratedEdges++;
52215
+ }
52216
+ }
52171
52217
  }
52172
52218
  }
52173
52219
  return plan;
@@ -54328,7 +54374,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
54328
54374
  }
54329
54375
 
54330
54376
  // src/engine.ts
54331
- var DAEMON_VERSION = true ? "2.0.2-dev.545" : "2.0.0-alpha.0";
54377
+ var DAEMON_VERSION = true ? "2.0.2-dev.554" : "2.0.0-alpha.0";
54332
54378
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
54333
54379
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
54334
54380
  var GIT_OP_MUTE_MS = 4e3;
@@ -55001,6 +55047,9 @@ function createWorkspaceEngine(opts) {
55001
55047
  let abstracted = 0;
55002
55048
  let triaged = 0;
55003
55049
  let priorEdges = 0;
55050
+ let corroboratedEdges = 0;
55051
+ let touchedFileTurns = 0;
55052
+ let touchedToolTurns = 0;
55004
55053
  let linked = 0;
55005
55054
  const t = Date.now();
55006
55055
  let processedTurns = 0;
@@ -55120,6 +55169,9 @@ function createWorkspaceEngine(opts) {
55120
55169
  ...touched.size > 0 ? { sessionTouchedIds: touched } : {}
55121
55170
  });
55122
55171
  priorEdges += plan.priorEdges;
55172
+ corroboratedEdges += plan.corroboratedEdges;
55173
+ if (touchedFileId) touchedFileTurns++;
55174
+ if ((toolRuns.get(sessionId)?.size ?? 0) > 0) touchedToolTurns++;
55123
55175
  const wf = workingFiles.get(sessionId);
55124
55176
  let inScopeProblemId;
55125
55177
  const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
@@ -55529,6 +55581,9 @@ function createWorkspaceEngine(opts) {
55529
55581
  resolved,
55530
55582
  triaged,
55531
55583
  priorEdges,
55584
+ corroboratedEdges,
55585
+ touchedFileTurns,
55586
+ touchedToolTurns,
55532
55587
  seqAdvanced: store.currentIngestSeq() - seqAtStart
55533
55588
  });
55534
55589
  }
@@ -56142,7 +56197,7 @@ function pidAlive(pid) {
56142
56197
  // src/multi.ts
56143
56198
  init_dist();
56144
56199
  init_src5();
56145
- import { readFileSync as readFileSync26, unlinkSync as unlinkSync3, writeFileSync as writeFileSync22 } from "node:fs";
56200
+ import { readFileSync as readFileSync27, unlinkSync as unlinkSync3, writeFileSync as writeFileSync22 } from "node:fs";
56146
56201
 
56147
56202
  // src/principle-sync.ts
56148
56203
  init_src5();
@@ -56380,6 +56435,114 @@ function noveltyAgainst(node2, neighbors, opts = {}) {
56380
56435
  };
56381
56436
  }
56382
56437
 
56438
+ // src/public-symbols.ts
56439
+ import { readFileSync as readFileSync25, readdirSync as readdirSync10, existsSync as existsSync26 } from "node:fs";
56440
+ import { join as join30 } from "node:path";
56441
+ var HEAD_BYTES = 8 * 1024;
56442
+ var INDEX_TTL_MS = 10 * 60 * 1e3;
56443
+ var MIN_SYMBOL_LEN = 4;
56444
+ var NAMED_IMPORT_RE = /import\s+(?:type\s+)?(.+?)\s+from\s+['"]([^'"]+)['"]/g;
56445
+ var REQUIRE_RE = /(?:const|let|var)\s+(.+?)\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)/g;
56446
+ function isBareExternal(spec, internalNames) {
56447
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("node:")) return false;
56448
+ const pkg = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
56449
+ return !internalNames.has(pkg);
56450
+ }
56451
+ function bindingNames(clause) {
56452
+ const names = [];
56453
+ const braced = /\{([^}]*)\}/.exec(clause);
56454
+ if (braced) {
56455
+ for (const part of braced[1].split(",")) {
56456
+ const local = part.split(/\s+as\s+/).pop()?.trim();
56457
+ if (local && /^[A-Za-z_$][\w$]*$/.test(local)) names.push(local);
56458
+ }
56459
+ }
56460
+ const ns = /\*\s+as\s+([A-Za-z_$][\w$]*)/.exec(clause);
56461
+ if (ns) names.push(ns[1]);
56462
+ const head2 = clause.replace(/\{[^}]*\}/, "").replace(/\*\s+as\s+[A-Za-z_$][\w$]*/, "");
56463
+ const def = /(?:^|,)\s*([A-Za-z_$][\w$]*)\s*(?:,|$)/.exec(head2.trim());
56464
+ if (def) names.push(def[1]);
56465
+ return names;
56466
+ }
56467
+ function internalPackageNames(workspaceRoot) {
56468
+ const names = /* @__PURE__ */ new Set();
56469
+ const tryRead = (dir) => {
56470
+ const pj = join30(dir, "package.json");
56471
+ if (!existsSync26(pj)) return;
56472
+ try {
56473
+ const name2 = JSON.parse(readFileSync25(pj, "utf8")).name;
56474
+ if (typeof name2 === "string" && name2.length > 0) names.add(name2);
56475
+ } catch {
56476
+ }
56477
+ };
56478
+ tryRead(workspaceRoot);
56479
+ for (const group of ["packages", "apps"]) {
56480
+ const groupDir = join30(workspaceRoot, group);
56481
+ if (!existsSync26(groupDir)) continue;
56482
+ try {
56483
+ for (const entry of readdirSync10(groupDir)) tryRead(join30(groupDir, entry));
56484
+ } catch {
56485
+ }
56486
+ }
56487
+ return names;
56488
+ }
56489
+ var cache = /* @__PURE__ */ new Map();
56490
+ function publicSymbolIndex(store, workspaceRoot, deps = {}) {
56491
+ const now = deps.nowMs ?? Date.now();
56492
+ const cached2 = cache.get(workspaceRoot);
56493
+ if (cached2 && now - cached2.builtAt < INDEX_TTL_MS) return cached2;
56494
+ const readHead = deps.readHead ?? ((absPath) => {
56495
+ try {
56496
+ return readFileSync25(absPath, "utf8").slice(0, HEAD_BYTES);
56497
+ } catch {
56498
+ return "";
56499
+ }
56500
+ });
56501
+ const internal = internalPackageNames(workspaceRoot);
56502
+ const bareModules = /* @__PURE__ */ new Set();
56503
+ for (const mod of store.findNodesByLabel("Module")) {
56504
+ if (isBareExternal(mod.description, internal)) bareModules.add(mod.id);
56505
+ }
56506
+ const files = /* @__PURE__ */ new Set();
56507
+ for (const modId of bareModules) {
56508
+ for (const e of store.inEdges(modId, ["IMPORTS"])) {
56509
+ const f = store.getNode(e.from);
56510
+ const rel = f?.attrs["relPath"];
56511
+ if (f && f.validTo == null && typeof rel === "string") files.add(rel);
56512
+ }
56513
+ }
56514
+ const symbols = /* @__PURE__ */ new Set();
56515
+ for (const rel of files) {
56516
+ const head2 = readHead(join30(workspaceRoot, rel));
56517
+ if (!head2) continue;
56518
+ for (const re of [NAMED_IMPORT_RE, REQUIRE_RE]) {
56519
+ re.lastIndex = 0;
56520
+ for (const m of head2.matchAll(re)) {
56521
+ if (!isBareExternal(m[2], internal)) continue;
56522
+ for (const name2 of bindingNames(m[1])) {
56523
+ if (name2.length >= MIN_SYMBOL_LEN) symbols.add(name2);
56524
+ }
56525
+ }
56526
+ }
56527
+ }
56528
+ const index = { symbols, builtAt: now };
56529
+ cache.set(workspaceRoot, index);
56530
+ return index;
56531
+ }
56532
+ var PRESERVED_SYMBOLS_CAP = 8;
56533
+ function preservedSymbolsFor(description, index) {
56534
+ const out2 = [];
56535
+ const seen = /* @__PURE__ */ new Set();
56536
+ for (const m of description.matchAll(/[A-Za-z_$][\w$]{3,}/g)) {
56537
+ const tok = m[0];
56538
+ if (seen.has(tok) || !index.symbols.has(tok)) continue;
56539
+ seen.add(tok);
56540
+ out2.push(tok);
56541
+ if (out2.length >= PRESERVED_SYMBOLS_CAP) break;
56542
+ }
56543
+ return out2;
56544
+ }
56545
+
56383
56546
  // src/instance-ingest.ts
56384
56547
  var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
56385
56548
  var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
@@ -56449,17 +56612,22 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
56449
56612
  const key = sessionOriginKey(n.attrs["sources"]?.[0]);
56450
56613
  return key ? { originSession: key } : {};
56451
56614
  };
56452
- const shareable = (n) => ({
56453
- ...n,
56454
- description: generalize(n.description, { level }).text,
56455
- embedding: [],
56456
- ...originSessionOf(n),
56457
- attrs: {
56458
- scope: stripCodebaseScope(n.attrs["scope"]),
56459
- ...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
56460
- ...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {}
56461
- }
56462
- });
56615
+ const symbolIndex = opts.workspaceRoot ? publicSymbolIndex(store, opts.workspaceRoot) : null;
56616
+ const shareable = (n) => {
56617
+ const preserved = symbolIndex ? preservedSymbolsFor(n.description, symbolIndex) : [];
56618
+ return {
56619
+ ...n,
56620
+ description: generalize(n.description, { level }).text,
56621
+ embedding: [],
56622
+ ...originSessionOf(n),
56623
+ attrs: {
56624
+ scope: stripCodebaseScope(n.attrs["scope"]),
56625
+ ...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
56626
+ ...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {},
56627
+ ...preserved.length > 0 ? { preservedSymbols: preserved } : {}
56628
+ }
56629
+ };
56630
+ };
56463
56631
  const nodes = [];
56464
56632
  const seen = /* @__PURE__ */ new Set();
56465
56633
  const shippedById = /* @__PURE__ */ new Map();
@@ -56772,7 +56940,7 @@ var ConsolidateWorker = class {
56772
56940
  init_paths();
56773
56941
 
56774
56942
  // src/lock.ts
56775
- import { existsSync as existsSync26, readFileSync as readFileSync25 } from "node:fs";
56943
+ import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
56776
56944
  function isProcessAlive(pid) {
56777
56945
  if (!pid || pid <= 0) return false;
56778
56946
  try {
@@ -56783,9 +56951,9 @@ function isProcessAlive(pid) {
56783
56951
  }
56784
56952
  }
56785
56953
  function readDaemonLock(lockPath) {
56786
- if (!existsSync26(lockPath)) return null;
56954
+ if (!existsSync27(lockPath)) return null;
56787
56955
  try {
56788
- const lock = JSON.parse(readFileSync25(lockPath, "utf8"));
56956
+ const lock = JSON.parse(readFileSync26(lockPath, "utf8"));
56789
56957
  return typeof lock.pid === "number" ? lock : null;
56790
56958
  } catch {
56791
56959
  return null;
@@ -57069,12 +57237,12 @@ async function reanchorProject(opts) {
57069
57237
  }
57070
57238
 
57071
57239
  // src/adopt.ts
57072
- import { existsSync as existsSync27 } from "node:fs";
57073
- import { dirname as dirname10, join as join30 } from "node:path";
57240
+ import { existsSync as existsSync28 } from "node:fs";
57241
+ import { dirname as dirname10, join as join31 } from "node:path";
57074
57242
  function findGitRoot(absPath) {
57075
57243
  let dir = absPath;
57076
57244
  for (let depth = 0; depth < 64; depth++) {
57077
- if (existsSync27(join30(dir, ".git"))) return dir;
57245
+ if (existsSync28(join31(dir, ".git"))) return dir;
57078
57246
  const parent = dirname10(dir);
57079
57247
  if (parent === dir) return null;
57080
57248
  dir = parent;
@@ -57923,6 +58091,7 @@ async function startMultiDaemon(opts = {}) {
57923
58091
  }
57924
58092
  const instances = buildInstanceIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
57925
58093
  includePackages: cfg2.consent.contributePackages,
58094
+ workspaceRoot: r.root,
57926
58095
  ...lexicon ? { lexicon } : {},
57927
58096
  ...project ? { project } : {}
57928
58097
  });
@@ -58069,7 +58238,7 @@ async function startMultiDaemon(opts = {}) {
58069
58238
  },
58070
58239
  async stop() {
58071
58240
  try {
58072
- const cur = readFileSync26(lockPath, "utf8");
58241
+ const cur = readFileSync27(lockPath, "utf8");
58073
58242
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
58074
58243
  } catch {
58075
58244
  }
@@ -59092,21 +59261,21 @@ async function cmdInit() {
59092
59261
  if (!skipHooks) {
59093
59262
  console.log("");
59094
59263
  console.log("installing harness hooks...");
59095
- const { existsSync: existsSync29 } = await import("node:fs");
59096
- const { join: join32 } = await import("node:path");
59264
+ const { existsSync: existsSync30 } = await import("node:fs");
59265
+ const { join: join33 } = await import("node:path");
59097
59266
  try {
59098
59267
  await installClaudeHooks(port);
59099
59268
  } catch (err2) {
59100
59269
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
59101
59270
  }
59102
- if (existsSync29(join32(ROOT, ".cursor"))) {
59271
+ if (existsSync30(join33(ROOT, ".cursor"))) {
59103
59272
  try {
59104
59273
  await installCursorMcpConfig();
59105
59274
  } catch (err2) {
59106
59275
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
59107
59276
  }
59108
59277
  }
59109
- if (existsSync29(join32(ROOT, ".codex"))) {
59278
+ if (existsSync30(join33(ROOT, ".codex"))) {
59110
59279
  try {
59111
59280
  await installCodexHooks(port);
59112
59281
  } catch (err2) {
@@ -59263,9 +59432,9 @@ async function cmdStatus() {
59263
59432
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
59264
59433
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
59265
59434
  }
59266
- console.log(` graph db: ${existsSync28(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
59267
- console.log(` event log: ${existsSync28(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
59268
- if (existsSync28(paths.castalia)) {
59435
+ console.log(` graph db: ${existsSync29(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
59436
+ console.log(` event log: ${existsSync29(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
59437
+ if (existsSync29(paths.castalia)) {
59269
59438
  try {
59270
59439
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
59271
59440
  const store = openGraphStore2({ path: paths.castalia });
@@ -59946,11 +60115,11 @@ function cmdInstallationProfile(args2) {
59946
60115
  }
59947
60116
  async function cmdReview() {
59948
60117
  const paths = workspacePaths(ROOT);
59949
- if (!existsSync28(paths.reviewQueue)) {
60118
+ if (!existsSync29(paths.reviewQueue)) {
59950
60119
  console.log("(review queue empty)");
59951
60120
  return;
59952
60121
  }
59953
- const queue = JSON.parse(readFileSync27(paths.reviewQueue, "utf8"));
60122
+ const queue = JSON.parse(readFileSync28(paths.reviewQueue, "utf8"));
59954
60123
  if (queue.length === 0) {
59955
60124
  console.log("(review queue empty)");
59956
60125
  return;
@@ -60621,7 +60790,7 @@ async function gatherRepo(store, ws) {
60621
60790
  };
60622
60791
  }
60623
60792
  async function gatherReportData(generatedAt) {
60624
- const { existsSync: existsSync29 } = await import("node:fs");
60793
+ const { existsSync: existsSync30 } = await import("node:fs");
60625
60794
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
60626
60795
  const cfg = loadConfig();
60627
60796
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -60629,7 +60798,7 @@ async function gatherReportData(generatedAt) {
60629
60798
  for (const ws of listWorkspaces()) {
60630
60799
  if (ws.missing) continue;
60631
60800
  const dbPath = workspacePaths(ws.path).castalia;
60632
- if (!existsSync29(dbPath)) continue;
60801
+ if (!existsSync30(dbPath)) continue;
60633
60802
  let store = null;
60634
60803
  try {
60635
60804
  store = openGraphStore2({ path: dbPath });
@@ -60673,8 +60842,8 @@ async function cmdReport(args2) {
60673
60842
  const outDir = workspacePaths(ROOT).configDir;
60674
60843
  mkdirSync8(outDir, { recursive: true });
60675
60844
  const files = renderReport2(data, { includeFutureVerbs });
60676
- for (const f of files) writeFileSync23(join31(outDir, f.name), f.html, "utf8");
60677
- const indexPath = join31(outDir, "report.html");
60845
+ for (const f of files) writeFileSync23(join32(outDir, f.name), f.html, "utf8");
60846
+ const indexPath = join32(outDir, "report.html");
60678
60847
  console.log(`report \u2192 ${indexPath}`);
60679
60848
  console.log(
60680
60849
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -60792,15 +60961,15 @@ function hookRelayCommand(port, path2) {
60792
60961
  return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
60793
60962
  }
60794
60963
  async function installClaudeHooks(port) {
60795
- const { mkdirSync: mkdirSync8, existsSync: existsSync29, readFileSync: readFileSync28, writeFileSync: writeFileSync23 } = await import("node:fs");
60796
- const { join: join32 } = await import("node:path");
60797
- const dir = join32(ROOT, ".claude");
60798
- if (!existsSync29(dir)) mkdirSync8(dir, { recursive: true });
60799
- const file2 = join32(dir, "settings.json");
60964
+ const { mkdirSync: mkdirSync8, existsSync: existsSync30, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
60965
+ const { join: join33 } = await import("node:path");
60966
+ const dir = join33(ROOT, ".claude");
60967
+ if (!existsSync30(dir)) mkdirSync8(dir, { recursive: true });
60968
+ const file2 = join33(dir, "settings.json");
60800
60969
  let settings = {};
60801
- if (existsSync29(file2)) {
60970
+ if (existsSync30(file2)) {
60802
60971
  try {
60803
- settings = JSON.parse(readFileSync28(file2, "utf8"));
60972
+ settings = JSON.parse(readFileSync29(file2, "utf8"));
60804
60973
  } catch {
60805
60974
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
60806
60975
  process.exit(2);
@@ -60849,7 +61018,7 @@ async function installClaudeHooks(port) {
60849
61018
  writeFileSync23(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
60850
61019
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
60851
61020
  await installClaudeMcpConfig();
60852
- const claudeMd = join32(ROOT, "CLAUDE.md");
61021
+ const claudeMd = join33(ROOT, "CLAUDE.md");
60853
61022
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
60854
61023
  if (recall.kind === "collision") {
60855
61024
  console.warn(
@@ -60861,15 +61030,15 @@ async function installClaudeHooks(port) {
60861
61030
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
60862
61031
  }
60863
61032
  async function installClaudeMcpConfig() {
60864
- const { mkdirSync: mkdirSync8, existsSync: existsSync29, readFileSync: readFileSync28, writeFileSync: writeFileSync23 } = await import("node:fs");
60865
- const { join: join32, dirname: dirname11 } = await import("node:path");
60866
- const file2 = join32(ROOT, ".mcp.json");
61033
+ const { mkdirSync: mkdirSync8, existsSync: existsSync30, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
61034
+ const { join: join33, dirname: dirname11 } = await import("node:path");
61035
+ const file2 = join33(ROOT, ".mcp.json");
60867
61036
  const dir = dirname11(file2);
60868
- if (!existsSync29(dir)) mkdirSync8(dir, { recursive: true });
61037
+ if (!existsSync30(dir)) mkdirSync8(dir, { recursive: true });
60869
61038
  let cfg = {};
60870
- if (existsSync29(file2)) {
61039
+ if (existsSync30(file2)) {
60871
61040
  try {
60872
- cfg = JSON.parse(readFileSync28(file2, "utf8"));
61041
+ cfg = JSON.parse(readFileSync29(file2, "utf8"));
60873
61042
  } catch {
60874
61043
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
60875
61044
  process.exit(2);
@@ -60883,15 +61052,15 @@ async function installClaudeMcpConfig() {
60883
61052
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
60884
61053
  }
60885
61054
  async function installCursorMcpConfig() {
60886
- const { mkdirSync: mkdirSync8, existsSync: existsSync29, readFileSync: readFileSync28, writeFileSync: writeFileSync23 } = await import("node:fs");
60887
- const { join: join32 } = await import("node:path");
60888
- const dir = join32(ROOT, ".cursor");
60889
- if (!existsSync29(dir)) mkdirSync8(dir, { recursive: true });
60890
- const file2 = join32(dir, "mcp.json");
61055
+ const { mkdirSync: mkdirSync8, existsSync: existsSync30, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
61056
+ const { join: join33 } = await import("node:path");
61057
+ const dir = join33(ROOT, ".cursor");
61058
+ if (!existsSync30(dir)) mkdirSync8(dir, { recursive: true });
61059
+ const file2 = join33(dir, "mcp.json");
60891
61060
  let cfg = {};
60892
- if (existsSync29(file2)) {
61061
+ if (existsSync30(file2)) {
60893
61062
  try {
60894
- cfg = JSON.parse(readFileSync28(file2, "utf8"));
61063
+ cfg = JSON.parse(readFileSync29(file2, "utf8"));
60895
61064
  } catch {
60896
61065
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
60897
61066
  process.exit(2);
@@ -60907,16 +61076,16 @@ async function installCursorMcpConfig() {
60907
61076
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
60908
61077
  }
60909
61078
  async function installCodexHooks(port) {
60910
- const { mkdirSync: mkdirSync8, existsSync: existsSync29, readFileSync: readFileSync28, writeFileSync: writeFileSync23 } = await import("node:fs");
60911
- const { join: join32 } = await import("node:path");
60912
- const dir = join32(ROOT, ".codex");
60913
- if (!existsSync29(dir)) mkdirSync8(dir, { recursive: true });
60914
- const file2 = join32(dir, "config.toml");
61079
+ const { mkdirSync: mkdirSync8, existsSync: existsSync30, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
61080
+ const { join: join33 } = await import("node:path");
61081
+ const dir = join33(ROOT, ".codex");
61082
+ if (!existsSync30(dir)) mkdirSync8(dir, { recursive: true });
61083
+ const file2 = join33(dir, "config.toml");
60915
61084
  const BEGIN = `# >>> errata hooks (errata-managed)`;
60916
61085
  const END = `# <<< errata hooks`;
60917
61086
  let existing = "";
60918
- if (existsSync29(file2)) {
60919
- existing = readFileSync28(file2, "utf8");
61087
+ if (existsSync30(file2)) {
61088
+ existing = readFileSync29(file2, "utf8");
60920
61089
  const beginIdx = existing.indexOf(BEGIN);
60921
61090
  const endIdx = existing.indexOf(END);
60922
61091
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -61281,7 +61450,7 @@ async function cmdDash(args2) {
61281
61450
  await yieldToLoop2();
61282
61451
  try {
61283
61452
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
61284
- const res = bleedRules(join31(r.root, ".claude", "rules"), items);
61453
+ const res = bleedRules(join32(r.root, ".claude", "rules"), items);
61285
61454
  if (res.written || res.pruned) {
61286
61455
  console.log(
61287
61456
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.545",
3
+ "version": "2.0.2-dev.554",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -24921,6 +24921,16 @@ var SqliteGraphStore = class {
24921
24921
  if (revived > 0) this.mutations++;
24922
24922
  return revived;
24923
24923
  }
24924
+ recentEdgeAttrCoverage(marker, attr, limit) {
24925
+ const r = this.db.prepare(
24926
+ `SELECT COUNT(*) AS total,
24927
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
24928
+ FROM (SELECT attrs_json FROM edges
24929
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
24930
+ ORDER BY created_at DESC LIMIT ?)`
24931
+ ).get(attr, `%${marker}%`, limit);
24932
+ return { count: Number(r.count), total: Number(r.total) };
24933
+ }
24924
24934
  liveNodeIds(ids) {
24925
24935
  const live = /* @__PURE__ */ new Set();
24926
24936
  const CHUNK = 900;