@inerrata-corporation/errata 2.0.24-dev.5105 → 2.0.24-dev.5107

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
@@ -20275,6 +20275,44 @@ var init_community_floor = __esm({
20275
20275
  });
20276
20276
 
20277
20277
  // ../../packages/math/src/spectral.ts
20278
+ function largestConnectedComponent(n, adj) {
20279
+ const seen = new Uint8Array(n);
20280
+ let best = [];
20281
+ for (let start2 = 0; start2 < n; start2++) {
20282
+ if (seen[start2]) continue;
20283
+ const comp = [];
20284
+ const queue = [start2];
20285
+ seen[start2] = 1;
20286
+ while (queue.length) {
20287
+ const v = queue.shift();
20288
+ comp.push(v);
20289
+ for (const { j } of adj[v]) {
20290
+ if (!seen[j]) {
20291
+ seen[j] = 1;
20292
+ queue.push(j);
20293
+ }
20294
+ }
20295
+ }
20296
+ if (comp.length > best.length) best = comp;
20297
+ }
20298
+ return best;
20299
+ }
20300
+ function indexedAdjacency(input) {
20301
+ const idx = new Map(input.nodeIds.map((id, i2) => [id, i2]));
20302
+ const adj = input.nodeIds.map(() => []);
20303
+ for (const id of input.nodeIds) {
20304
+ const i2 = idx.get(id);
20305
+ for (const { to, weight } of input.adj.get(id) ?? []) {
20306
+ const j = idx.get(to);
20307
+ if (j === void 0 || j === i2) continue;
20308
+ adj[i2].push({ j, w: weight });
20309
+ }
20310
+ }
20311
+ return adj;
20312
+ }
20313
+ function largestConnectedComponentSize(input) {
20314
+ return largestConnectedComponent(input.nodeIds.length, indexedAdjacency(input)).length;
20315
+ }
20278
20316
  var init_spectral = __esm({
20279
20317
  "../../packages/math/src/spectral.ts"() {
20280
20318
  "use strict";
@@ -20289,6 +20327,163 @@ var init_community_health = __esm({
20289
20327
  }
20290
20328
  });
20291
20329
 
20330
+ // ../../packages/math/src/detectability.ts
20331
+ function betheDetectability(input, opts = {}) {
20332
+ const unweighted = opts.unweighted ?? true;
20333
+ const maxCommunities = opts.maxCommunities ?? BETHE_DEFAULT_MAX;
20334
+ const n = input.nodeIds.length;
20335
+ const empty = {
20336
+ nodes: n,
20337
+ meanDegree: 0,
20338
+ negativeEigenvalues: [],
20339
+ detectableCommunities: 0,
20340
+ capped: false
20341
+ };
20342
+ if (n < 2) return empty;
20343
+ const idx = /* @__PURE__ */ new Map();
20344
+ input.nodeIds.forEach((id, i2) => idx.set(id, i2));
20345
+ const rows = input.nodeIds.map(() => []);
20346
+ const deg = new Float64Array(n);
20347
+ let twoM = 0;
20348
+ for (const id of input.nodeIds) {
20349
+ const i2 = idx.get(id);
20350
+ for (const { to, weight } of input.adj.get(id) ?? []) {
20351
+ const j = idx.get(to);
20352
+ if (j === void 0 || j === i2 || weight <= 0) continue;
20353
+ const w = unweighted ? 1 : weight;
20354
+ rows[i2].push([j, w]);
20355
+ deg[i2] = deg[i2] + w;
20356
+ twoM += w;
20357
+ }
20358
+ }
20359
+ if (twoM <= 0) return empty;
20360
+ const d = twoM / n;
20361
+ let sumSq = 0;
20362
+ for (let i2 = 0; i2 < n; i2++) sumSq += deg[i2] * deg[i2];
20363
+ const excess = sumSq / twoM - 1;
20364
+ if (excess <= 0) return { ...empty, meanDegree: d };
20365
+ const r = Math.sqrt(excess);
20366
+ const diagShift = r * r - 1;
20367
+ const apply = (v, out2) => {
20368
+ for (let i2 = 0; i2 < n; i2++) {
20369
+ let av = 0;
20370
+ for (const [j, w] of rows[i2]) av += w * v[j];
20371
+ out2[i2] = diagShift * v[i2] - r * av + deg[i2] * v[i2];
20372
+ }
20373
+ };
20374
+ const ritz = lanczosRitzValues(n, apply, Math.min(n, betheSteps(maxCommunities)), 24301);
20375
+ let scale = 1;
20376
+ for (const x of ritz) scale = Math.max(scale, Math.abs(x));
20377
+ const negatives = ritz.filter((x) => x < -NEGATIVE_TOLERANCE * scale);
20378
+ const capped = negatives.length >= maxCommunities;
20379
+ const kept = negatives.slice(0, maxCommunities);
20380
+ return {
20381
+ nodes: n,
20382
+ meanDegree: d,
20383
+ negativeEigenvalues: kept,
20384
+ detectableCommunities: Math.max(0, kept.length - 1),
20385
+ capped
20386
+ };
20387
+ }
20388
+ function lanczosRitzValues(n, apply, steps, seed) {
20389
+ const prng = mulberry32(seed);
20390
+ let v = new Float64Array(n);
20391
+ for (let i2 = 0; i2 < n; i2++) v[i2] = prng() - 0.5;
20392
+ const shaped = new Float64Array(n);
20393
+ apply(v, shaped);
20394
+ if (normalize2(shaped) > 0) v = shaped;
20395
+ else if (normalize2(v) === 0) return [];
20396
+ const basis = [];
20397
+ const alpha = [];
20398
+ const beta = [];
20399
+ let prev = null;
20400
+ let b = 0;
20401
+ for (let j = 0; j < steps; j++) {
20402
+ basis.push(v);
20403
+ const w = new Float64Array(n);
20404
+ apply(v, w);
20405
+ let a = 0;
20406
+ for (let i2 = 0; i2 < n; i2++) a += w[i2] * v[i2];
20407
+ alpha.push(a);
20408
+ for (let i2 = 0; i2 < n; i2++) w[i2] = w[i2] - a * v[i2] - (prev ? b * prev[i2] : 0);
20409
+ for (const u of basis) projectOff(w, u);
20410
+ b = normalize2(w);
20411
+ if (b <= 1e-12 * Math.max(1, Math.abs(a))) break;
20412
+ beta.push(b);
20413
+ prev = v;
20414
+ v = w;
20415
+ }
20416
+ return tridiagonalEigenvalues(alpha, beta.slice(0, alpha.length - 1));
20417
+ }
20418
+ function tridiagonalEigenvalues(alpha, beta) {
20419
+ const m = alpha.length;
20420
+ if (m === 0) return [];
20421
+ let lo = Infinity;
20422
+ let hi = -Infinity;
20423
+ for (let i2 = 0; i2 < m; i2++) {
20424
+ const radius = (i2 > 0 ? Math.abs(beta[i2 - 1]) : 0) + (i2 < m - 1 ? Math.abs(beta[i2]) : 0);
20425
+ lo = Math.min(lo, alpha[i2] - radius);
20426
+ hi = Math.max(hi, alpha[i2] + radius);
20427
+ }
20428
+ const tiny = 1e-300;
20429
+ const countBelow = (x) => {
20430
+ let count2 = 0;
20431
+ let q = alpha[0] - x;
20432
+ if (q < 0) count2++;
20433
+ for (let i2 = 1; i2 < m; i2++) {
20434
+ if (q === 0) q = tiny;
20435
+ q = alpha[i2] - x - beta[i2 - 1] * beta[i2 - 1] / q;
20436
+ if (q < 0) count2++;
20437
+ }
20438
+ return count2;
20439
+ };
20440
+ const out2 = [];
20441
+ const width = Math.max(hi - lo, 1e-12);
20442
+ for (let k = 0; k < m; k++) {
20443
+ let a = lo;
20444
+ let c = hi;
20445
+ for (let it = 0; it < 200 && c - a > 1e-13 * width; it++) {
20446
+ const mid = (a + c) / 2;
20447
+ if (countBelow(mid) > k) c = mid;
20448
+ else a = mid;
20449
+ }
20450
+ out2.push((a + c) / 2);
20451
+ }
20452
+ return out2;
20453
+ }
20454
+ function mulberry32(seed) {
20455
+ let a = seed >>> 0;
20456
+ return () => {
20457
+ a = a + 1831565813 >>> 0;
20458
+ let t = a;
20459
+ t = Math.imul(t ^ t >>> 15, t | 1);
20460
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
20461
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
20462
+ };
20463
+ }
20464
+ function projectOff(v, u) {
20465
+ let dot = 0;
20466
+ for (let i2 = 0; i2 < v.length; i2++) dot += v[i2] * u[i2];
20467
+ if (dot === 0) return;
20468
+ for (let i2 = 0; i2 < v.length; i2++) v[i2] = v[i2] - dot * u[i2];
20469
+ }
20470
+ function normalize2(v) {
20471
+ let s = 0;
20472
+ for (let i2 = 0; i2 < v.length; i2++) s += v[i2] * v[i2];
20473
+ const norm = Math.sqrt(s);
20474
+ if (norm > 0) for (let i2 = 0; i2 < v.length; i2++) v[i2] = v[i2] / norm;
20475
+ return norm;
20476
+ }
20477
+ var BETHE_DEFAULT_MAX, betheSteps, NEGATIVE_TOLERANCE;
20478
+ var init_detectability = __esm({
20479
+ "../../packages/math/src/detectability.ts"() {
20480
+ "use strict";
20481
+ BETHE_DEFAULT_MAX = 32;
20482
+ betheSteps = (maxCommunities) => 2 * maxCommunities + 40;
20483
+ NEGATIVE_TOLERANCE = 1e-7;
20484
+ }
20485
+ });
20486
+
20292
20487
  // ../../packages/math/src/void-demand.ts
20293
20488
  var DEMAND_HOT, DEMAND_WARM;
20294
20489
  var init_void_demand = __esm({
@@ -20626,6 +20821,7 @@ var init_src3 = __esm({
20626
20821
  init_community_floor();
20627
20822
  init_spectral();
20628
20823
  init_community_health();
20824
+ init_detectability();
20629
20825
  init_void_demand();
20630
20826
  init_motif_gate();
20631
20827
  init_bko();
@@ -23882,7 +24078,7 @@ function staticModel(header, scales, weights) {
23882
24078
  return ids;
23883
24079
  }
23884
24080
  function tokenize4(text) {
23885
- return preTokenize(normalize2(text)).flatMap(wordpiece);
24081
+ return preTokenize(normalize3(text)).flatMap(wordpiece);
23886
24082
  }
23887
24083
  function embed2(text) {
23888
24084
  const sum = new Float64Array(dim);
@@ -23911,7 +24107,7 @@ function isBertPunctuation(ch) {
23911
24107
  function isCjk(c) {
23912
24108
  return c >= 19968 && c <= 40959 || c >= 13312 && c <= 19903 || c >= 131072 && c <= 173791 || c >= 173824 && c <= 177983 || c >= 177984 && c <= 178207 || c >= 178208 && c <= 183983 || c >= 63744 && c <= 64255 || c >= 194560 && c <= 195103;
23913
24109
  }
23914
- function normalize2(text) {
24110
+ function normalize3(text) {
23915
24111
  let out2 = "";
23916
24112
  for (const ch of text) {
23917
24113
  const c = ch.codePointAt(0);
@@ -27140,8 +27336,78 @@ function candidateDomains(store, ids, labelOf) {
27140
27336
  }
27141
27337
  return out2;
27142
27338
  }
27339
+ function buildJointAdjacency(store, ids) {
27340
+ const adj = /* @__PURE__ */ new Map();
27341
+ const protect = [];
27342
+ const add = (a, b, w) => {
27343
+ const l = adj.get(a) ?? [];
27344
+ l.push({ to: b, weight: w });
27345
+ adj.set(a, l);
27346
+ };
27347
+ for (const id of ids) {
27348
+ for (const e of store.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
27349
+ if (!ids.has(e.to)) continue;
27350
+ if (e.type === "INSTANCE_OF" && e.extractionSource === "bko-inferred") continue;
27351
+ const conf = e.confidence > 0 ? e.confidence : 0.5;
27352
+ const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
27353
+ add(id, e.to, w);
27354
+ add(e.to, id, w);
27355
+ if (isCausalProtected(e.type)) protect.push([id, e.to]);
27356
+ }
27357
+ }
27358
+ return { adj, protect };
27359
+ }
27360
+ function projectionDetectability(input) {
27361
+ let support2 = 0;
27362
+ for (const id of input.nodeIds) support2 += (input.adj.get(id) ?? []).length;
27363
+ const meanDegree = input.nodeIds.length > 0 ? support2 / input.nodeIds.length : 0;
27364
+ const lccIds = largestConnectedComponentMembers(input);
27365
+ const lcc = lccIds.length >= 3 ? betheDetectability({ nodeIds: lccIds, adj: input.adj }) : null;
27366
+ return {
27367
+ readings: {
27368
+ meanDegree,
27369
+ lccSize: lccIds.length,
27370
+ lccDetectable: lcc?.detectableCommunities ?? 0
27371
+ },
27372
+ lccIds
27373
+ };
27374
+ }
27375
+ function largestConnectedComponentMembers(input) {
27376
+ if (largestConnectedComponentSize(input) <= 1) return input.nodeIds.slice(0, 1);
27377
+ const present = new Set(input.nodeIds);
27378
+ const seen = /* @__PURE__ */ new Set();
27379
+ let best = [];
27380
+ for (const start2 of input.nodeIds) {
27381
+ if (seen.has(start2)) continue;
27382
+ const comp = [];
27383
+ const stack = [start2];
27384
+ seen.add(start2);
27385
+ while (stack.length > 0) {
27386
+ const id = stack.pop();
27387
+ comp.push(id);
27388
+ for (const { to } of input.adj.get(id) ?? []) {
27389
+ if (!present.has(to) || seen.has(to)) continue;
27390
+ seen.add(to);
27391
+ stack.push(to);
27392
+ }
27393
+ }
27394
+ if (comp.length > best.length) best = comp;
27395
+ }
27396
+ return best;
27397
+ }
27143
27398
  function detectLocalCommunities(store, opts = {}) {
27144
- const report = { candidates: 0, communities: 0, joint: 0, assigned: 0, belowFloor: 0, cleared: 0 };
27399
+ const report = {
27400
+ candidates: 0,
27401
+ communities: 0,
27402
+ joint: 0,
27403
+ assigned: 0,
27404
+ belowFloor: 0,
27405
+ cleared: 0,
27406
+ meanDegree: 0,
27407
+ lccSize: 0,
27408
+ lccDetectable: 0,
27409
+ lccPieces: 0
27410
+ };
27145
27411
  const nodes = /* @__PURE__ */ new Map();
27146
27412
  const unowned = [];
27147
27413
  for (const label of JOINT_COMMUNITY_LABELS) {
@@ -27165,25 +27431,12 @@ function detectLocalCommunities(store, opts = {}) {
27165
27431
  }
27166
27432
  });
27167
27433
  if (ids.size === 0) return report;
27168
- const adj = /* @__PURE__ */ new Map();
27169
- const protect = [];
27170
- const add = (a, b, w) => {
27171
- const l = adj.get(a) ?? [];
27172
- l.push({ to: b, weight: w });
27173
- adj.set(a, l);
27174
- };
27175
- for (const id of ids) {
27176
- for (const e of store.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
27177
- if (!ids.has(e.to)) continue;
27178
- if (e.type === "INSTANCE_OF" && e.extractionSource === "bko-inferred") continue;
27179
- const conf = e.confidence > 0 ? e.confidence : 0.5;
27180
- const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
27181
- add(id, e.to, w);
27182
- add(e.to, id, w);
27183
- if (isCausalProtected(e.type)) protect.push([id, e.to]);
27184
- }
27185
- }
27186
- const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
27434
+ const { adj, protect } = buildJointAdjacency(store, ids);
27435
+ const input = { nodeIds: [...ids], adj };
27436
+ const detectability = projectionDetectability(input);
27437
+ Object.assign(report, detectability.readings);
27438
+ const comm = detectCommunitiesLeiden(input, { protect });
27439
+ report.lccPieces = new Set(detectability.lccIds.map((id) => comm.community.get(id))).size;
27187
27440
  const members = /* @__PURE__ */ new Map();
27188
27441
  for (const [id, c] of comm.community) {
27189
27442
  const l = members.get(c) ?? [];
@@ -27955,6 +28208,21 @@ var init_mechanism_liveness = __esm({
27955
28208
  effectCounter: "heldAnswered",
27956
28209
  note: "asked, never inferred; the lean is provisional while asked and replaced by the answer; Network-only priors are asked by id"
27957
28210
  },
28211
+ {
28212
+ id: "neighbour-ask",
28213
+ what: "shows a just-minted problem's nearest existing problems once; the agent's bare cite ([h]) mints RELATES_TO",
28214
+ pass: "capture",
28215
+ // Conditional (a new problem with a neighbour in the band) — written-at-all
28216
+ // bar. Before this, 69% of open agent-observed Problems had no cause,
28217
+ // instance or relation edge and never gained one (live store 2026-10-27).
28218
+ fed: {
28219
+ labels: ["Problem"],
28220
+ attr: "neighbourAskedAt",
28221
+ minFraction: 0
28222
+ },
28223
+ effectCounter: "neighbourLinksMinted",
28224
+ note: "MEL-203; the link is the agent's \u2014 nothing is inferred from similarity"
28225
+ },
27958
28226
  {
27959
28227
  id: "regression-recognize",
27960
28228
  what: "marks a resolved problem as resolutionSuspect when it recurs at the door",
@@ -28329,6 +28597,7 @@ __export(src_exports2, {
28329
28597
  backfillLegacyAnchors: () => backfillLegacyAnchors,
28330
28598
  backfillProblemContext: () => backfillProblemContext,
28331
28599
  backfillProblemPackageLinks: () => backfillProblemPackageLinks,
28600
+ buildJointAdjacency: () => buildJointAdjacency,
28332
28601
  buildLanguageIndex: () => buildLanguageIndex,
28333
28602
  buildPackageIndex: () => buildPackageIndex,
28334
28603
  buildPrincipleSync: () => buildPrincipleSync,
@@ -28426,6 +28695,7 @@ __export(src_exports2, {
28426
28695
  percolate: () => percolate,
28427
28696
  priorExposure: () => priorExposure,
28428
28697
  priorsForFile: () => priorsForFile,
28698
+ projectionDetectability: () => projectionDetectability,
28429
28699
  promoteRouteWithDiscriminator: () => promoteRouteWithDiscriminator,
28430
28700
  propagateFactChange: () => propagateFactChange,
28431
28701
  propagateVersionChange: () => propagateVersionChange,
@@ -28961,6 +29231,19 @@ function renderSnapshot(s) {
28961
29231
  }
28962
29232
  lines.push("");
28963
29233
  }
29234
+ if (s.neighbourAsks?.length) {
29235
+ lines.push("### \u{1F9ED} Neighbours of your new problem");
29236
+ lines.push(
29237
+ "_These existing problems read closest to one you just stated. If one is related, cite it beside yours \u2014 `([h])` links them; a problem nobody links stays an island no later search can reach from here. Unrelated \u2192 ignore:_"
29238
+ );
29239
+ for (const a of s.neighbourAsks) {
29240
+ lines.push(`- yours: **${a.problem.description}** \u2014 \`${a.problem.id}\``);
29241
+ for (const n of a.neighbours) {
29242
+ lines.push(` - near: **${n.description}** \u2014 \`${n.id}\`${n.lifecycle ? ` _(${n.lifecycle})_` : ""} \u2192 related: ([${n.id}])`);
29243
+ }
29244
+ }
29245
+ lines.push("");
29246
+ }
28964
29247
  lines.push("### Recently observed problems in this workspace");
28965
29248
  if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0 && s.regressions.length === 0) {
28966
29249
  lines.push("- _none yet \u2014 errata is still building its model_");
@@ -29265,6 +29548,7 @@ function isolateSnapshot(s) {
29265
29548
  selfCiteSkips: _selfCiteSkips,
29266
29549
  supersessionAsks: _supersessionAsks,
29267
29550
  heldAsks: _heldAsks,
29551
+ neighbourAsks: _neighbourAsks,
29268
29552
  pendingUpdate: _pendingUpdate,
29269
29553
  daemonDistress: _daemonDistress,
29270
29554
  reviewItems: _reviewItems,
@@ -82513,7 +82797,7 @@ init_review2();
82513
82797
  // src/turn.ts
82514
82798
  init_checkout_root();
82515
82799
  import { closeSync as closeSync2, existsSync as existsSync21, fstatSync, openSync as openSync2, readdirSync as readdirSync7, readSync as readSync2, realpathSync as realpathSync2, statSync as statSync10 } from "node:fs";
82516
- import { basename as basename6, delimiter, dirname as dirname14, join as join25, normalize as normalize4 } from "node:path";
82800
+ import { basename as basename6, delimiter, dirname as dirname14, join as join25, normalize as normalize5 } from "node:path";
82517
82801
  import { homedir as homedir4 } from "node:os";
82518
82802
  import { createHash as createHash16 } from "node:crypto";
82519
82803
  function completeJsonlPrefix(buf) {
@@ -82956,7 +83240,7 @@ function subagentTranscripts(mainTranscriptPath, sessionId) {
82956
83240
  }
82957
83241
  function splitPathList(value, sep7 = delimiter) {
82958
83242
  if (!value) return [];
82959
- return value.split(sep7).map((d) => d.trim()).filter(Boolean).map((d) => normalize4(d));
83243
+ return value.split(sep7).map((d) => d.trim()).filter(Boolean).map((d) => normalize5(d));
82960
83244
  }
82961
83245
  function codexSessionRoots(env2 = process.env, home = homedir4()) {
82962
83246
  const roots = /* @__PURE__ */ new Set();
@@ -84569,6 +84853,92 @@ var SupersessionAsks = class {
84569
84853
  }
84570
84854
  };
84571
84855
 
84856
+ // src/neighbour-asks.ts
84857
+ init_src4();
84858
+ var NEIGHBOUR_ASK_MAX = 3;
84859
+ var NEIGHBOUR_ASK_TTL_MS = 30 * 6e4;
84860
+ var NEIGHBOUR_SCAN_CAP = 400;
84861
+ var NEIGHBOUR_MIN_SHARED_TERMS = 1;
84862
+ var NEIGHBOUR_TERM_DF_SHARE = 0.03;
84863
+ var TERM_MIN_LENGTH = 4;
84864
+ var termsOf2 = (text) => [...new Set(text.toLowerCase().split(/[^a-z0-9_.-]+/).filter((w) => w.length >= TERM_MIN_LENGTH))];
84865
+ var RELATED = ["RELATES_TO", "INSTANCE_OF", "REGRESSION_OF", "CAUSED_BY", "CONTRADICTS"];
84866
+ function alreadyRelated(store, a, b) {
84867
+ const types = [...RELATED];
84868
+ return store.outEdges(a, types).some((e) => e.to === b) || store.outEdges(b, types).some((e) => e.to === a);
84869
+ }
84870
+ function nearestProblems(store, workspaceId2, problemId, statement, opts = {}) {
84871
+ const embed2 = opts.embed ?? embedTextSync;
84872
+ const ours = embed2(statement);
84873
+ const bands = cosineBandsFor(ours.version);
84874
+ const excluded = /* @__PURE__ */ new Set([problemId, ...opts.excludeIds ?? []]);
84875
+ const candidates = store.recentOpenProblemCandidates({
84876
+ workspaceId: workspaceId2,
84877
+ constraint: false,
84878
+ limit: NEIGHBOUR_SCAN_CAP,
84879
+ excludeId: problemId
84880
+ });
84881
+ const df = /* @__PURE__ */ new Map();
84882
+ for (const c of candidates) for (const w of termsOf2(c.description)) df.set(w, (df.get(w) ?? 0) + 1);
84883
+ const dfCap = Math.max(2, candidates.length * NEIGHBOUR_TERM_DF_SHARE);
84884
+ const mine = new Set(termsOf2(statement).filter((w) => (df.get(w) ?? 0) <= dfCap));
84885
+ const scored = [];
84886
+ for (const c of candidates) {
84887
+ if (excluded.has(c.id)) continue;
84888
+ const theirs = embed2(c.description);
84889
+ if (theirs.version !== ours.version) continue;
84890
+ const s = cosine(ours.vector, theirs.vector);
84891
+ if (s < bands.relatedStrong || s >= bands.nearMiss) continue;
84892
+ if (termsOf2(c.description).filter((w) => mine.has(w)).length < NEIGHBOUR_MIN_SHARED_TERMS) continue;
84893
+ if (alreadyRelated(store, problemId, c.id)) continue;
84894
+ scored.push({ id: c.id, description: c.description, cosine: s });
84895
+ }
84896
+ scored.sort((a, b) => b.cosine - a.cosine);
84897
+ return scored.slice(0, opts.limit ?? NEIGHBOUR_ASK_MAX);
84898
+ }
84899
+ var NeighbourAsks = class {
84900
+ bySession = /* @__PURE__ */ new Map();
84901
+ /** Ask (or refresh) for a problem the session just minted. */
84902
+ add(sessionId, problemId, neighbourIds, now = Date.now()) {
84903
+ if (!sessionId || neighbourIds.length === 0) return;
84904
+ const asks = (this.bySession.get(sessionId) ?? []).filter((a) => a.problemId !== problemId);
84905
+ asks.push({ problemId, neighbourIds, askedAt: now });
84906
+ this.bySession.set(sessionId, asks);
84907
+ }
84908
+ forSession(sessionId, now = Date.now()) {
84909
+ return (this.bySession.get(sessionId) ?? []).filter((a) => now - a.askedAt < NEIGHBOUR_ASK_TTL_MS);
84910
+ }
84911
+ /**
84912
+ * After a harvest: a neighbour the session cited (any prior edge, confirm or
84913
+ * refute names it) is answered and leaves the ask; what has outlived its TTL
84914
+ * leaves too. Returns how many shown neighbours were linked — the effect this
84915
+ * mechanism exists for.
84916
+ */
84917
+ settle(sessionId, judged, now = Date.now()) {
84918
+ const asks = this.bySession.get(sessionId);
84919
+ if (!asks) return 0;
84920
+ let linked = 0;
84921
+ const next = [];
84922
+ for (const a of asks) {
84923
+ const remaining = a.neighbourIds.filter((id) => {
84924
+ if (judged.has(id)) {
84925
+ linked++;
84926
+ return false;
84927
+ }
84928
+ return true;
84929
+ });
84930
+ if (remaining.length > 0 && now - a.askedAt < NEIGHBOUR_ASK_TTL_MS) next.push({ ...a, neighbourIds: remaining });
84931
+ }
84932
+ if (next.length > 0) this.bySession.set(sessionId, next);
84933
+ else this.bySession.delete(sessionId);
84934
+ return linked;
84935
+ }
84936
+ /** A cache-key fingerprint of what this session's block would show. */
84937
+ version(sessionId, now = Date.now()) {
84938
+ return this.forSession(sessionId, now).map((a) => `${a.problemId}:${a.neighbourIds.join(",")}`).join(";");
84939
+ }
84940
+ };
84941
+
84572
84942
  // src/held-asks.ts
84573
84943
  import { readFileSync as readFileSync22, renameSync as renameSync4, writeFileSync as writeFileSync16 } from "node:fs";
84574
84944
  import { dirname as dirname15, join as join26 } from "node:path";
@@ -89369,7 +89739,7 @@ function createWatchBreaker(deps) {
89369
89739
  }
89370
89740
 
89371
89741
  // src/engine.ts
89372
- var DAEMON_VERSION = true ? "2.0.24-dev.5105" : "2.0.0-alpha.0";
89742
+ var DAEMON_VERSION = true ? "2.0.24-dev.5107" : "2.0.0-alpha.0";
89373
89743
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
89374
89744
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
89375
89745
  var GIT_OP_MUTE_MS = 4e3;
@@ -89822,6 +90192,7 @@ function createWorkspaceEngine(opts) {
89822
90192
  const sessionRenders = /* @__PURE__ */ new Map();
89823
90193
  const selfCiteSkipsBySession = /* @__PURE__ */ new Map();
89824
90194
  const supersessionAsks = new SupersessionAsks();
90195
+ const neighbourAsks = new NeighbourAsks();
89825
90196
  const heldAsks = loadHeldAsks(heldAsksPath(paths.configDir));
89826
90197
  const releaseHeld = (all, t) => {
89827
90198
  const released = all.filter((r) => store.getNode(r.witness.nodeId)?.validTo == null);
@@ -89918,6 +90289,29 @@ function createWorkspaceEngine(opts) {
89918
90289
  const priors = a.priorIds.map((id) => store.getNode(id)).filter((n) => n !== null && n.validTo == null).map((n) => ({ id: n.id, description: n.description, ...lifecycleField(n) }));
89919
90290
  return fix && priors.length > 0 ? [{ fix: { id: fix.id, description: fix.description }, priors }] : [];
89920
90291
  });
90292
+ const neighbourAsksFor = (sessionId) => neighbourAsks.forSession(sessionId).flatMap((a) => {
90293
+ const problem = store.getNode(a.problemId);
90294
+ if (!problem || problem.validTo != null || problem.attrs["resolvedAt"] != null) return [];
90295
+ const neighbours = a.neighbourIds.map((id) => store.getNode(id)).filter((n) => n !== null && n.validTo == null).map((n) => ({ id: n.id, description: n.description, ...lifecycleField(n) }));
90296
+ return neighbours.length > 0 ? [{ problem: { id: problem.id, description: problem.description }, neighbours }] : [];
90297
+ });
90298
+ const askNeighbours = (sessionId, problemId, statement, t) => {
90299
+ if (!sessionId) return false;
90300
+ try {
90301
+ const node2 = store.getNode(problemId);
90302
+ const foldTarget = node2?.attrs["foldCandidate"]?.targetId;
90303
+ const near = nearestProblems(store, profile.id, problemId, statement, {
90304
+ ...foldTarget ? { excludeIds: [foldTarget] } : {}
90305
+ });
90306
+ if (near.length === 0) return false;
90307
+ neighbourAsks.add(sessionId, problemId, near.map((n) => n.id), t);
90308
+ if (node2) store.updateNode(problemId, { attrs: { ...node2.attrs, neighbourAskedAt: t }, lastUpdatedAt: t });
90309
+ return true;
90310
+ } catch (err2) {
90311
+ console.warn("[errata] neighbour ask skipped:", err2 instanceof Error ? err2.message : err2);
90312
+ return false;
90313
+ }
90314
+ };
89921
90315
  const heldAsksFor = (sessionId) => {
89922
90316
  const shown = [];
89923
90317
  let discarded = false;
@@ -90402,6 +90796,7 @@ function createWorkspaceEngine(opts) {
90402
90796
  const key = JSON.stringify([
90403
90797
  supersessionAsks.version(sessionId),
90404
90798
  heldAsks.version(sessionId),
90799
+ neighbourAsks.version(sessionId),
90405
90800
  subagentBaseVersion,
90406
90801
  lastRemoteAt,
90407
90802
  sessions.version(now),
@@ -90428,6 +90823,9 @@ function createWorkspaceEngine(opts) {
90428
90823
  delete scoped.heldAsks;
90429
90824
  const held = heldAsksFor(sessionId);
90430
90825
  if (held.length > 0) scoped.heldAsks = held;
90826
+ delete scoped.neighbourAsks;
90827
+ const near = neighbourAsksFor(sessionId);
90828
+ if (near.length > 0) scoped.neighbourAsks = near;
90431
90829
  if (taskPriorsOn) {
90432
90830
  scoped.recentProblems = [];
90433
90831
  scoped.recentResolved = [];
@@ -90576,6 +90974,8 @@ function createWorkspaceEngine(opts) {
90576
90974
  let confirmsStamped = 0;
90577
90975
  let supersessionAsked = 0;
90578
90976
  let structureLinksMinted = 0;
90977
+ let neighbourAsked = 0;
90978
+ let neighbourLinksMinted = 0;
90579
90979
  let heldAsked = 0;
90580
90980
  let heldAnswered = 0;
90581
90981
  let recurrencesMarked = 0;
@@ -90676,6 +91076,7 @@ function createWorkspaceEngine(opts) {
90676
91076
  }
90677
91077
  }
90678
91078
  if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
91079
+ if (r.created && flag.kind !== "constraint" && !replay2 && askNeighbours(sessionId, r.problemId, flag.problem, t)) neighbourAsked++;
90679
91080
  if (r.created || r.corroborated) {
90680
91081
  try {
90681
91082
  if (editedTurnFile) {
@@ -90972,6 +91373,7 @@ function createWorkspaceEngine(opts) {
90972
91373
  if (!r.rejected && r.problemId) {
90973
91374
  findingIds.set(p.statement, r.problemId);
90974
91375
  if (p.threadId) threads.set(p.threadId, r.problemId);
91376
+ if (r.created && p.kind !== "constraint" && !replay2 && askNeighbours(sessionId, r.problemId, p.statement, t)) neighbourAsked++;
90975
91377
  }
90976
91378
  if (r.created && dedupPath && !replay2) {
90977
91379
  try {
@@ -91316,15 +91718,14 @@ function createWorkspaceEngine(opts) {
91316
91718
  structureLinksMinted++;
91317
91719
  }
91318
91720
  }
91319
- supersessionAsks.settle(
91320
- sessionId,
91321
- /* @__PURE__ */ new Set([
91322
- ...plan.solutionLinks.flatMap((sl) => sl.targetIds),
91323
- ...plan.corroborations.map((c) => c.nodeId),
91324
- ...withheldLeanIds,
91325
- ...plan.refutes.map((r) => r.nodeId)
91326
- ])
91327
- );
91721
+ const judgedPriorIds = /* @__PURE__ */ new Set([
91722
+ ...plan.solutionLinks.flatMap((sl) => sl.targetIds),
91723
+ ...plan.corroborations.map((c) => c.nodeId),
91724
+ ...withheldLeanIds,
91725
+ ...plan.refutes.map((r) => r.nodeId)
91726
+ ]);
91727
+ supersessionAsks.settle(sessionId, judgedPriorIds);
91728
+ neighbourLinksMinted += neighbourAsks.settle(sessionId, judgedPriorIds);
91328
91729
  const citingSession = sessionOriginKey(sessionId);
91329
91730
  const mintedHere = (nodeId) => store.getNode(nodeId)?.attrs["sources"]?.[0] === sessionId;
91330
91731
  const synthetic = sessions.originOf(sessionId) !== void 0;
@@ -91535,6 +91936,9 @@ function createWorkspaceEngine(opts) {
91535
91936
  confirmsStamped,
91536
91937
  supersessionAsked,
91537
91938
  structureLinksMinted,
91939
+ // MEL-203: new problems shown their neighbours / neighbours the agent then linked.
91940
+ neighbourAsked,
91941
+ neighbourLinksMinted,
91538
91942
  heldAsked,
91539
91943
  heldAnswered,
91540
91944
  // RG-recognize tier A: resolved problems the door saw recur this pass.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.24-dev.5105",
3
+ "version": "2.0.24-dev.5107",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -26836,6 +26836,197 @@ function communityDomain(memberDomains, domainSize) {
26836
26836
  return best;
26837
26837
  }
26838
26838
 
26839
+ // ../../packages/math/src/spectral.ts
26840
+ function largestConnectedComponent(n, adj) {
26841
+ const seen = new Uint8Array(n);
26842
+ let best = [];
26843
+ for (let start2 = 0; start2 < n; start2++) {
26844
+ if (seen[start2]) continue;
26845
+ const comp = [];
26846
+ const queue = [start2];
26847
+ seen[start2] = 1;
26848
+ while (queue.length) {
26849
+ const v = queue.shift();
26850
+ comp.push(v);
26851
+ for (const { j } of adj[v]) {
26852
+ if (!seen[j]) {
26853
+ seen[j] = 1;
26854
+ queue.push(j);
26855
+ }
26856
+ }
26857
+ }
26858
+ if (comp.length > best.length) best = comp;
26859
+ }
26860
+ return best;
26861
+ }
26862
+ function indexedAdjacency(input) {
26863
+ const idx = new Map(input.nodeIds.map((id, i2) => [id, i2]));
26864
+ const adj = input.nodeIds.map(() => []);
26865
+ for (const id of input.nodeIds) {
26866
+ const i2 = idx.get(id);
26867
+ for (const { to, weight } of input.adj.get(id) ?? []) {
26868
+ const j = idx.get(to);
26869
+ if (j === void 0 || j === i2) continue;
26870
+ adj[i2].push({ j, w: weight });
26871
+ }
26872
+ }
26873
+ return adj;
26874
+ }
26875
+ function largestConnectedComponentSize(input) {
26876
+ return largestConnectedComponent(input.nodeIds.length, indexedAdjacency(input)).length;
26877
+ }
26878
+
26879
+ // ../../packages/math/src/detectability.ts
26880
+ var BETHE_DEFAULT_MAX = 32;
26881
+ var betheSteps = (maxCommunities) => 2 * maxCommunities + 40;
26882
+ var NEGATIVE_TOLERANCE = 1e-7;
26883
+ function betheDetectability(input, opts = {}) {
26884
+ const unweighted = opts.unweighted ?? true;
26885
+ const maxCommunities = opts.maxCommunities ?? BETHE_DEFAULT_MAX;
26886
+ const n = input.nodeIds.length;
26887
+ const empty = {
26888
+ nodes: n,
26889
+ meanDegree: 0,
26890
+ negativeEigenvalues: [],
26891
+ detectableCommunities: 0,
26892
+ capped: false
26893
+ };
26894
+ if (n < 2) return empty;
26895
+ const idx = /* @__PURE__ */ new Map();
26896
+ input.nodeIds.forEach((id, i2) => idx.set(id, i2));
26897
+ const rows = input.nodeIds.map(() => []);
26898
+ const deg = new Float64Array(n);
26899
+ let twoM = 0;
26900
+ for (const id of input.nodeIds) {
26901
+ const i2 = idx.get(id);
26902
+ for (const { to, weight } of input.adj.get(id) ?? []) {
26903
+ const j = idx.get(to);
26904
+ if (j === void 0 || j === i2 || weight <= 0) continue;
26905
+ const w = unweighted ? 1 : weight;
26906
+ rows[i2].push([j, w]);
26907
+ deg[i2] = deg[i2] + w;
26908
+ twoM += w;
26909
+ }
26910
+ }
26911
+ if (twoM <= 0) return empty;
26912
+ const d = twoM / n;
26913
+ let sumSq = 0;
26914
+ for (let i2 = 0; i2 < n; i2++) sumSq += deg[i2] * deg[i2];
26915
+ const excess = sumSq / twoM - 1;
26916
+ if (excess <= 0) return { ...empty, meanDegree: d };
26917
+ const r = Math.sqrt(excess);
26918
+ const diagShift = r * r - 1;
26919
+ const apply = (v, out2) => {
26920
+ for (let i2 = 0; i2 < n; i2++) {
26921
+ let av = 0;
26922
+ for (const [j, w] of rows[i2]) av += w * v[j];
26923
+ out2[i2] = diagShift * v[i2] - r * av + deg[i2] * v[i2];
26924
+ }
26925
+ };
26926
+ const ritz = lanczosRitzValues(n, apply, Math.min(n, betheSteps(maxCommunities)), 24301);
26927
+ let scale = 1;
26928
+ for (const x of ritz) scale = Math.max(scale, Math.abs(x));
26929
+ const negatives = ritz.filter((x) => x < -NEGATIVE_TOLERANCE * scale);
26930
+ const capped = negatives.length >= maxCommunities;
26931
+ const kept = negatives.slice(0, maxCommunities);
26932
+ return {
26933
+ nodes: n,
26934
+ meanDegree: d,
26935
+ negativeEigenvalues: kept,
26936
+ detectableCommunities: Math.max(0, kept.length - 1),
26937
+ capped
26938
+ };
26939
+ }
26940
+ function lanczosRitzValues(n, apply, steps, seed) {
26941
+ const prng = mulberry32(seed);
26942
+ let v = new Float64Array(n);
26943
+ for (let i2 = 0; i2 < n; i2++) v[i2] = prng() - 0.5;
26944
+ const shaped = new Float64Array(n);
26945
+ apply(v, shaped);
26946
+ if (normalize2(shaped) > 0) v = shaped;
26947
+ else if (normalize2(v) === 0) return [];
26948
+ const basis = [];
26949
+ const alpha = [];
26950
+ const beta = [];
26951
+ let prev = null;
26952
+ let b = 0;
26953
+ for (let j = 0; j < steps; j++) {
26954
+ basis.push(v);
26955
+ const w = new Float64Array(n);
26956
+ apply(v, w);
26957
+ let a = 0;
26958
+ for (let i2 = 0; i2 < n; i2++) a += w[i2] * v[i2];
26959
+ alpha.push(a);
26960
+ for (let i2 = 0; i2 < n; i2++) w[i2] = w[i2] - a * v[i2] - (prev ? b * prev[i2] : 0);
26961
+ for (const u of basis) projectOff(w, u);
26962
+ b = normalize2(w);
26963
+ if (b <= 1e-12 * Math.max(1, Math.abs(a))) break;
26964
+ beta.push(b);
26965
+ prev = v;
26966
+ v = w;
26967
+ }
26968
+ return tridiagonalEigenvalues(alpha, beta.slice(0, alpha.length - 1));
26969
+ }
26970
+ function tridiagonalEigenvalues(alpha, beta) {
26971
+ const m = alpha.length;
26972
+ if (m === 0) return [];
26973
+ let lo = Infinity;
26974
+ let hi = -Infinity;
26975
+ for (let i2 = 0; i2 < m; i2++) {
26976
+ const radius = (i2 > 0 ? Math.abs(beta[i2 - 1]) : 0) + (i2 < m - 1 ? Math.abs(beta[i2]) : 0);
26977
+ lo = Math.min(lo, alpha[i2] - radius);
26978
+ hi = Math.max(hi, alpha[i2] + radius);
26979
+ }
26980
+ const tiny = 1e-300;
26981
+ const countBelow = (x) => {
26982
+ let count = 0;
26983
+ let q = alpha[0] - x;
26984
+ if (q < 0) count++;
26985
+ for (let i2 = 1; i2 < m; i2++) {
26986
+ if (q === 0) q = tiny;
26987
+ q = alpha[i2] - x - beta[i2 - 1] * beta[i2 - 1] / q;
26988
+ if (q < 0) count++;
26989
+ }
26990
+ return count;
26991
+ };
26992
+ const out2 = [];
26993
+ const width = Math.max(hi - lo, 1e-12);
26994
+ for (let k = 0; k < m; k++) {
26995
+ let a = lo;
26996
+ let c = hi;
26997
+ for (let it = 0; it < 200 && c - a > 1e-13 * width; it++) {
26998
+ const mid = (a + c) / 2;
26999
+ if (countBelow(mid) > k) c = mid;
27000
+ else a = mid;
27001
+ }
27002
+ out2.push((a + c) / 2);
27003
+ }
27004
+ return out2;
27005
+ }
27006
+ function mulberry32(seed) {
27007
+ let a = seed >>> 0;
27008
+ return () => {
27009
+ a = a + 1831565813 >>> 0;
27010
+ let t = a;
27011
+ t = Math.imul(t ^ t >>> 15, t | 1);
27012
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
27013
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27014
+ };
27015
+ }
27016
+ function projectOff(v, u) {
27017
+ let dot = 0;
27018
+ for (let i2 = 0; i2 < v.length; i2++) dot += v[i2] * u[i2];
27019
+ if (dot === 0) return;
27020
+ for (let i2 = 0; i2 < v.length; i2++) v[i2] = v[i2] - dot * u[i2];
27021
+ }
27022
+ function normalize2(v) {
27023
+ let s = 0;
27024
+ for (let i2 = 0; i2 < v.length; i2++) s += v[i2] * v[i2];
27025
+ const norm = Math.sqrt(s);
27026
+ if (norm > 0) for (let i2 = 0; i2 < v.length; i2++) v[i2] = v[i2] / norm;
27027
+ return norm;
27028
+ }
27029
+
26839
27030
  // ../../packages/math/src/void-demand.ts
26840
27031
  var DEMAND_HOT = Math.log1p(10);
26841
27032
  var DEMAND_WARM = Math.log1p(5);
@@ -30299,8 +30490,78 @@ function candidateDomains(store2, ids, labelOf) {
30299
30490
  }
30300
30491
  return out2;
30301
30492
  }
30493
+ function buildJointAdjacency(store2, ids) {
30494
+ const adj = /* @__PURE__ */ new Map();
30495
+ const protect = [];
30496
+ const add = (a, b, w) => {
30497
+ const l = adj.get(a) ?? [];
30498
+ l.push({ to: b, weight: w });
30499
+ adj.set(a, l);
30500
+ };
30501
+ for (const id of ids) {
30502
+ for (const e of store2.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
30503
+ if (!ids.has(e.to)) continue;
30504
+ if (e.type === "INSTANCE_OF" && e.extractionSource === "bko-inferred") continue;
30505
+ const conf = e.confidence > 0 ? e.confidence : 0.5;
30506
+ const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
30507
+ add(id, e.to, w);
30508
+ add(e.to, id, w);
30509
+ if (isCausalProtected(e.type)) protect.push([id, e.to]);
30510
+ }
30511
+ }
30512
+ return { adj, protect };
30513
+ }
30514
+ function projectionDetectability(input) {
30515
+ let support2 = 0;
30516
+ for (const id of input.nodeIds) support2 += (input.adj.get(id) ?? []).length;
30517
+ const meanDegree = input.nodeIds.length > 0 ? support2 / input.nodeIds.length : 0;
30518
+ const lccIds = largestConnectedComponentMembers(input);
30519
+ const lcc = lccIds.length >= 3 ? betheDetectability({ nodeIds: lccIds, adj: input.adj }) : null;
30520
+ return {
30521
+ readings: {
30522
+ meanDegree,
30523
+ lccSize: lccIds.length,
30524
+ lccDetectable: lcc?.detectableCommunities ?? 0
30525
+ },
30526
+ lccIds
30527
+ };
30528
+ }
30529
+ function largestConnectedComponentMembers(input) {
30530
+ if (largestConnectedComponentSize(input) <= 1) return input.nodeIds.slice(0, 1);
30531
+ const present = new Set(input.nodeIds);
30532
+ const seen = /* @__PURE__ */ new Set();
30533
+ let best = [];
30534
+ for (const start2 of input.nodeIds) {
30535
+ if (seen.has(start2)) continue;
30536
+ const comp = [];
30537
+ const stack = [start2];
30538
+ seen.add(start2);
30539
+ while (stack.length > 0) {
30540
+ const id = stack.pop();
30541
+ comp.push(id);
30542
+ for (const { to } of input.adj.get(id) ?? []) {
30543
+ if (!present.has(to) || seen.has(to)) continue;
30544
+ seen.add(to);
30545
+ stack.push(to);
30546
+ }
30547
+ }
30548
+ if (comp.length > best.length) best = comp;
30549
+ }
30550
+ return best;
30551
+ }
30302
30552
  function detectLocalCommunities(store2, opts = {}) {
30303
- const report = { candidates: 0, communities: 0, joint: 0, assigned: 0, belowFloor: 0, cleared: 0 };
30553
+ const report = {
30554
+ candidates: 0,
30555
+ communities: 0,
30556
+ joint: 0,
30557
+ assigned: 0,
30558
+ belowFloor: 0,
30559
+ cleared: 0,
30560
+ meanDegree: 0,
30561
+ lccSize: 0,
30562
+ lccDetectable: 0,
30563
+ lccPieces: 0
30564
+ };
30304
30565
  const nodes = /* @__PURE__ */ new Map();
30305
30566
  const unowned = [];
30306
30567
  for (const label of JOINT_COMMUNITY_LABELS) {
@@ -30324,25 +30585,12 @@ function detectLocalCommunities(store2, opts = {}) {
30324
30585
  }
30325
30586
  });
30326
30587
  if (ids.size === 0) return report;
30327
- const adj = /* @__PURE__ */ new Map();
30328
- const protect = [];
30329
- const add = (a, b, w) => {
30330
- const l = adj.get(a) ?? [];
30331
- l.push({ to: b, weight: w });
30332
- adj.set(a, l);
30333
- };
30334
- for (const id of ids) {
30335
- for (const e of store2.outEdges(id, [...JOINT_COMMUNITY_EDGES])) {
30336
- if (!ids.has(e.to)) continue;
30337
- if (e.type === "INSTANCE_OF" && e.extractionSource === "bko-inferred") continue;
30338
- const conf = e.confidence > 0 ? e.confidence : 0.5;
30339
- const w = Math.max(EDGE_WEIGHT[e.type] ?? 1, 0.1) * conf;
30340
- add(id, e.to, w);
30341
- add(e.to, id, w);
30342
- if (isCausalProtected(e.type)) protect.push([id, e.to]);
30343
- }
30344
- }
30345
- const comm = detectCommunitiesLeiden({ nodeIds: [...ids], adj }, { protect });
30588
+ const { adj, protect } = buildJointAdjacency(store2, ids);
30589
+ const input = { nodeIds: [...ids], adj };
30590
+ const detectability = projectionDetectability(input);
30591
+ Object.assign(report, detectability.readings);
30592
+ const comm = detectCommunitiesLeiden(input, { protect });
30593
+ report.lccPieces = new Set(detectability.lccIds.map((id) => comm.community.get(id))).size;
30346
30594
  const members = /* @__PURE__ */ new Map();
30347
30595
  for (const [id, c] of comm.community) {
30348
30596
  const l = members.get(c) ?? [];
@@ -30469,6 +30717,21 @@ var MECHANISMS = [
30469
30717
  effectCounter: "heldAnswered",
30470
30718
  note: "asked, never inferred; the lean is provisional while asked and replaced by the answer; Network-only priors are asked by id"
30471
30719
  },
30720
+ {
30721
+ id: "neighbour-ask",
30722
+ what: "shows a just-minted problem's nearest existing problems once; the agent's bare cite ([h]) mints RELATES_TO",
30723
+ pass: "capture",
30724
+ // Conditional (a new problem with a neighbour in the band) — written-at-all
30725
+ // bar. Before this, 69% of open agent-observed Problems had no cause,
30726
+ // instance or relation edge and never gained one (live store 2026-10-27).
30727
+ fed: {
30728
+ labels: ["Problem"],
30729
+ attr: "neighbourAskedAt",
30730
+ minFraction: 0
30731
+ },
30732
+ effectCounter: "neighbourLinksMinted",
30733
+ note: "MEL-203; the link is the agent's \u2014 nothing is inferred from similarity"
30734
+ },
30472
30735
  {
30473
30736
  id: "regression-recognize",
30474
30737
  what: "marks a resolved problem as resolutionSuspect when it recurs at the door",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.24-dev.5105",
3
+ "version": "2.0.24-dev.5107",
4
4
  "description": "A minimal 3D view of loaded Errata priors and recent graph bursts.",
5
5
  "author": {
6
6
  "name": "inErrata"
@@ -1,4 +1,4 @@
1
- 65ef21d093ccff4f2ae34ab42cad7e42471cbea231b89d529258b2a52dd7a3ab .codex-plugin/plugin.json
1
+ af56c071ef86e48a2112bfca26f77c5f0b1794b0b6eadac1903e12f5a82673a6 .codex-plugin/plugin.json
2
2
  a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
3
3
  fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
4
4
  80431e822def1ed5f5376ff0e67af30b4d26829572982e08fe24f438838f134c dist/client.js