@inerrata-corporation/errata 2.0.2-dev.1172 → 2.0.2-dev.1182

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.
@@ -201,7 +201,7 @@ var init_castalia = __esm({
201
201
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
202
202
  TRANSFER_EDGES = ["MAY_RESOLVE"];
203
203
  SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
204
- SUSPICION_EDGES = ["SUSPECTED_LINK"];
204
+ SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
205
205
  TAXONOMY_EDGES = ["IS_A"];
206
206
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
207
207
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -336,6 +336,7 @@ var init_castalia = __esm({
336
336
  ALTERNATIVE_TO: 0,
337
337
  // Suspicion — a hypothesis, weightless + PageRank-excluded (never flows rank / EIG).
338
338
  SUSPECTED_LINK: 0,
339
+ REGRESSION_OF: 0,
339
340
  // Git — topology/authorship, weightless (not domain signal-flow)
340
341
  POINTS_AT: 0,
341
342
  PARENT: 0,
@@ -15862,7 +15863,13 @@ var LOCAL_RULE_OVERRIDES = {
15862
15863
  // the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
15863
15864
  // stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
15864
15865
  // this rule exists at all.
15865
- FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
15866
+ FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] },
15867
+ // RG-recognize: a NEW open Problem re-observing a RESOLVED one ("this is
15868
+ // back — the fix didn't hold"). Local-only for now: the instance drain's
15869
+ // INSTANCE_EDGES allowlist doesn't ship it, and the cloud door's matrix
15870
+ // doesn't know it — crossing the membrane is a deliberate later step, never
15871
+ // a side effect (the half-shipped-edge-type lesson).
15872
+ REGRESSION_OF: { from: ["Problem"], to: ["Problem"] }
15866
15873
  };
15867
15874
  function localEdgeViolation(fromLabel, type, toLabel) {
15868
15875
  if (type in LOCAL_RULE_OVERRIDES) {
@@ -16849,6 +16856,9 @@ init_src2();
16849
16856
  init_src();
16850
16857
  init_src();
16851
16858
 
16859
+ // ../../packages/local-graph/src/design-problem.ts
16860
+ var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
16861
+
16852
16862
  // ../../packages/local-graph/src/intent.ts
16853
16863
  init_src();
16854
16864
 
package/errata.mjs CHANGED
@@ -278,7 +278,7 @@ var init_castalia = __esm({
278
278
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
279
279
  TRANSFER_EDGES = ["MAY_RESOLVE"];
280
280
  SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
281
- SUSPICION_EDGES = ["SUSPECTED_LINK"];
281
+ SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
282
282
  TAXONOMY_EDGES = ["IS_A"];
283
283
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
284
284
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -343,8 +343,10 @@ var init_castalia = __esm({
343
343
  "ALTERNATIVE_TO",
344
344
  // symmetric peer marker — weight would let alternatives inflate each other
345
345
  // NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
346
- "SUSPECTED_LINK"
346
+ "SUSPECTED_LINK",
347
347
  // Somnus hypothesis (PLAN_SOMNUS_V2 §4) — never flows rank; navigation-invisible.
348
+ "REGRESSION_OF"
349
+ // recurrence marker (RG-recognize) — a question about a close, not signal-flow.
348
350
  ]);
349
351
  EDGE_WEIGHT = {
350
352
  // Causal — high signal
@@ -457,6 +459,7 @@ var init_castalia = __esm({
457
459
  ALTERNATIVE_TO: 0,
458
460
  // Suspicion — a hypothesis, weightless + PageRank-excluded (never flows rank / EIG).
459
461
  SUSPECTED_LINK: 0,
462
+ REGRESSION_OF: 0,
460
463
  // Git — topology/authorship, weightless (not domain signal-flow)
461
464
  POINTS_AT: 0,
462
465
  PARENT: 0,
@@ -16720,7 +16723,13 @@ var init_store = __esm({
16720
16723
  // the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
16721
16724
  // stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
16722
16725
  // this rule exists at all.
16723
- FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
16726
+ FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] },
16727
+ // RG-recognize: a NEW open Problem re-observing a RESOLVED one ("this is
16728
+ // back — the fix didn't hold"). Local-only for now: the instance drain's
16729
+ // INSTANCE_EDGES allowlist doesn't ship it, and the cloud door's matrix
16730
+ // doesn't know it — crossing the membrane is a deliberate later step, never
16731
+ // a side effect (the half-shipped-edge-type lesson).
16732
+ REGRESSION_OF: { from: ["Problem"], to: ["Problem"] }
16724
16733
  };
16725
16734
  SCHEMA_VERSION = 6;
16726
16735
  SCHEMA_SQL = `
@@ -19121,7 +19130,17 @@ function ingestDesignProblem(store, flag, opts) {
19121
19130
  const existing = store.getNode(problemId);
19122
19131
  let created = false;
19123
19132
  let corroborated = false;
19133
+ let recurredResolved = false;
19124
19134
  if (existing) {
19135
+ const resolvedUnretracted = existing.attrs["resolvedAt"] != null && existing.attrs["resolvedAs"] == null;
19136
+ const recurAttrs = () => {
19137
+ recurredResolved = true;
19138
+ return {
19139
+ resolutionSuspect: true,
19140
+ recurredAt: opts.ts,
19141
+ recurrences: (Number(existing.attrs["recurrences"]) || 0) + 1
19142
+ };
19143
+ };
19125
19144
  const sources = existing.attrs["sources"] ?? [];
19126
19145
  if (!sources.includes(opts.source)) {
19127
19146
  corroborated = true;
@@ -19130,6 +19149,9 @@ function ingestDesignProblem(store, flag, opts) {
19130
19149
  store.updateNode(problemId, {
19131
19150
  attrs: {
19132
19151
  ...existing.attrs,
19152
+ // An INDEPENDENT source re-deriving a resolved problem marks
19153
+ // immediately — that is the strongest recurrence evidence there is.
19154
+ ...resolvedUnretracted ? recurAttrs() : {},
19133
19155
  sources: [...sources, opts.source],
19134
19156
  corroborations: corroborations2,
19135
19157
  ...promoted ? { provisional: false } : {},
@@ -19155,6 +19177,11 @@ function ingestDesignProblem(store, flag, opts) {
19155
19177
  ...reinforcedSeqAttrs(store, existing.label),
19156
19178
  lastUpdatedAt: opts.ts
19157
19179
  });
19180
+ } else if (resolvedUnretracted && opts.ts - (Number(existing.attrs["recurredAt"]) || 0) > RECURRENCE_DEBOUNCE_MS) {
19181
+ store.updateNode(problemId, {
19182
+ attrs: { ...existing.attrs, ...recurAttrs() },
19183
+ lastUpdatedAt: opts.ts
19184
+ });
19158
19185
  }
19159
19186
  } else {
19160
19187
  created = true;
@@ -19210,7 +19237,16 @@ function ingestDesignProblem(store, flag, opts) {
19210
19237
  const linkText = flag.cause?.trim() ? `${statement}
19211
19238
  ${flag.cause.trim()}` : statement;
19212
19239
  const linkedPackages = linkProblemToPackages(store, problemId, linkText, opts.ts).linked;
19213
- return { problemId, created, corroborated, rootCauseId, solutionId, anchored, linkedPackages };
19240
+ return {
19241
+ problemId,
19242
+ created,
19243
+ corroborated,
19244
+ rootCauseId,
19245
+ solutionId,
19246
+ anchored,
19247
+ linkedPackages,
19248
+ ...recurredResolved ? { recurredResolved } : {}
19249
+ };
19214
19250
  }
19215
19251
  function canonicalizePatternText(name2) {
19216
19252
  const stripped = name2.replace(/^\s*pattern(?:\s*[×x]\s*\d+)?\s*(?:\(exemplar\))?\s*[:—–-]\s*/i, "").replace(/\s+/g, " ").trim();
@@ -19395,6 +19431,43 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
19395
19431
  }
19396
19432
  return existing.id;
19397
19433
  }
19434
+ function findResolvedAnchorMatch(store, relPath, workspaceId2, statement, kind = "problem") {
19435
+ const stmt = statement.trim();
19436
+ if (!isAnchorableCodePath(relPath)) return null;
19437
+ const file2 = resolveFileNode(store, relPath, workspaceId2);
19438
+ if (!file2) return null;
19439
+ const selfId = identityId({ kind: "DesignProblem", statement: stmt });
19440
+ let best = null;
19441
+ for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
19442
+ const cand = store.getNode(e.from);
19443
+ if (!cand || cand.label !== "Problem") continue;
19444
+ if (cand.attrs["resolvedAt"] == null || cand.attrs["resolvedAs"] != null) continue;
19445
+ if (cand.attrs["mergedInto"] !== void 0) continue;
19446
+ if (cand.id === selfId) continue;
19447
+ const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
19448
+ if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
19449
+ const score2 = tokenJaccard(stmt, cand.description);
19450
+ if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) {
19451
+ best = { id: cand.id, score: score2 };
19452
+ }
19453
+ }
19454
+ return best?.id ?? null;
19455
+ }
19456
+ function markResolvedRecurrence(store, nodeId, ts) {
19457
+ const n = store.getNode(nodeId);
19458
+ if (!n) return false;
19459
+ if (n.attrs["resolvedAt"] == null || n.attrs["resolvedAs"] != null) return false;
19460
+ store.updateNode(nodeId, {
19461
+ attrs: {
19462
+ ...n.attrs,
19463
+ resolutionSuspect: true,
19464
+ recurredAt: ts,
19465
+ recurrences: (Number(n.attrs["recurrences"]) || 0) + 1
19466
+ },
19467
+ lastUpdatedAt: ts
19468
+ });
19469
+ return true;
19470
+ }
19398
19471
  function isFragmentFixNote(note) {
19399
19472
  const t = note.trim();
19400
19473
  if (t.length === 0) return true;
@@ -19796,7 +19869,7 @@ function priorExposure(store, nodeId, atSeq) {
19796
19869
  if ((n.attrs["evictedCount"] ?? 0) > 0) return "evicted";
19797
19870
  return "unshown";
19798
19871
  }
19799
- var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX, FIX_CANDIDATE_ASK_LIMIT;
19872
+ var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, RECURRENCE_DEBOUNCE_MS, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX, FIX_CANDIDATE_ASK_LIMIT;
19800
19873
  var init_design_problem = __esm({
19801
19874
  "../../packages/local-graph/src/design-problem.ts"() {
19802
19875
  "use strict";
@@ -19809,6 +19882,7 @@ var init_design_problem = __esm({
19809
19882
  init_problem_dedup();
19810
19883
  DESIGN_PROMOTE_AT = 1;
19811
19884
  STATEMENT_ALIAS_CAP = 5;
19885
+ RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
19812
19886
  PATTERN_DEDUP_COSINE = 0.85;
19813
19887
  PATTERN_ALIAS_CAP = 5;
19814
19888
  DEDUP_STOPWORDS = /* @__PURE__ */ new Set([
@@ -22375,6 +22449,19 @@ var init_mechanism_liveness = __esm({
22375
22449
  effectCounter: "refutesStamped",
22376
22450
  note: "RG-mark; reason text is local-only (wire ships nodeId+witnessKey)"
22377
22451
  },
22452
+ {
22453
+ id: "regression-recognize",
22454
+ what: "marks a resolved problem as resolutionSuspect when it recurs at the door",
22455
+ pass: "capture",
22456
+ // Regressions are rare and precious — written-at-all bar, like refutedAt.
22457
+ fed: {
22458
+ labels: ["Problem"],
22459
+ attr: "recurredAt",
22460
+ minFraction: 0
22461
+ },
22462
+ effectCounter: "recurrencesMarked",
22463
+ note: "RG-recognize tier A (exact-id door); tiers B/C add REGRESSION_OF edges; reopen is RG-reopen's"
22464
+ },
22378
22465
  {
22379
22466
  id: "fix-candidate-ask",
22380
22467
  what: "asks an agent whether an edit to a problem's anchor fixed it",
@@ -22634,6 +22721,7 @@ __export(src_exports2, {
22634
22721
  PERCOLATING_DRIFT_EDGES: () => PERCOLATING_DRIFT_EDGES,
22635
22722
  PERCOLATING_EDGES: () => PERCOLATING_EDGES,
22636
22723
  PERCOLATING_LABELS: () => PERCOLATING_LABELS,
22724
+ RECURRENCE_DEBOUNCE_MS: () => RECURRENCE_DEBOUNCE_MS,
22637
22725
  SAME_ANCHOR_DEDUP_JACCARD: () => SAME_ANCHOR_DEDUP_JACCARD,
22638
22726
  STALL_MIN_RUNS: () => STALL_MIN_RUNS,
22639
22727
  STALL_MIN_SPAN_MS: () => STALL_MIN_SPAN_MS,
@@ -22676,6 +22764,7 @@ __export(src_exports2, {
22676
22764
  findMintTimeDuplicate: () => findMintTimeDuplicate,
22677
22765
  findOrCreatePackage: () => findOrCreatePackage,
22678
22766
  findPath: () => findPath,
22767
+ findResolvedAnchorMatch: () => findResolvedAnchorMatch,
22679
22768
  findReusableSolution: () => findReusableSolution,
22680
22769
  flattenCloudCounts: () => flattenCloudCounts,
22681
22770
  foldDuplicateProblem: () => foldDuplicateProblem,
@@ -22698,6 +22787,7 @@ __export(src_exports2, {
22698
22787
  localEdgeViolation: () => localEdgeViolation,
22699
22788
  markDiscriminatorAsked: () => markDiscriminatorAsked,
22700
22789
  markFixCandidates: () => markFixCandidates,
22790
+ markResolvedRecurrence: () => markResolvedRecurrence,
22701
22791
  markRevisit: () => markRevisit,
22702
22792
  matchLanguagesInText: () => matchLanguagesInText,
22703
22793
  matchPackagesInText: () => matchPackagesInText,
@@ -29418,6 +29508,50 @@ function mergeProblemsByEmbedding(store, opts) {
29418
29508
  });
29419
29509
  return report;
29420
29510
  }
29511
+ function bindResolvedRegressions(store, opts) {
29512
+ const minCosine = opts.minCosine ?? NEMORI_DEDUP_COSINE;
29513
+ const watermarkRaw = store.getMeta?.("regression_sweep_at") ?? null;
29514
+ const full = watermarkRaw === null;
29515
+ const since = full ? 0 : Number(watermarkRaw) || 0;
29516
+ const live2 = (n) => n.attrs["mergedInto"] === void 0;
29517
+ const problems = store.findNodesByLabel("Problem").filter((p) => p.embedding.length > 0 && live2(p));
29518
+ const fresh = problems.filter((p) => p.attrs["resolvedAt"] == null && p.createdAt > since);
29519
+ const resolved = problems.filter(
29520
+ (p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null
29521
+ );
29522
+ const report = { scanned: fresh.length, bound: 0, full };
29523
+ if (fresh.length > 0 && resolved.length > 0) {
29524
+ for (const f of fresh) {
29525
+ let best = null;
29526
+ for (const r of resolved) {
29527
+ if (r.embedding.length !== f.embedding.length) continue;
29528
+ if ((r.attrs["embeddingVersion"] ?? "") !== (f.attrs["embeddingVersion"] ?? "")) continue;
29529
+ if (isConstraintProblem(f) !== isConstraintProblem(r)) continue;
29530
+ const s = cosine(f.embedding, r.embedding);
29531
+ if (s >= minCosine && (!best || s > best.s)) best = { node: r, s };
29532
+ }
29533
+ if (!best) continue;
29534
+ if (store.outEdges(f.id, ["REGRESSION_OF"]).some((e) => e.to === best.node.id)) continue;
29535
+ if (!markResolvedRecurrence(store, best.node.id, opts.ts)) continue;
29536
+ store.mergeEdge({
29537
+ id: `edge_${digest({ from: f.id, type: "REGRESSION_OF", to: best.node.id })}`.slice(0, 24),
29538
+ from: f.id,
29539
+ to: best.node.id,
29540
+ type: "REGRESSION_OF",
29541
+ confidence: 0.5,
29542
+ extractionSource: "daemon-extracted",
29543
+ createdAt: opts.ts,
29544
+ lastSeenAt: opts.ts,
29545
+ navSuccesses: 0,
29546
+ navFailures: 0,
29547
+ attrs: { provisional: true, machineProposed: true, cosine: Math.round(best.s * 100) / 100 }
29548
+ });
29549
+ report.bound++;
29550
+ }
29551
+ }
29552
+ store.setMeta?.("regression_sweep_at", String(opts.ts));
29553
+ return report;
29554
+ }
29421
29555
  function bindCanonicalNeighbors(store, opts) {
29422
29556
  const k = opts.k ?? 3;
29423
29557
  const live2 = (n) => n.attrs["mergedInto"] === void 0;
@@ -30899,6 +31033,7 @@ __export(src_exports4, {
30899
31033
  annotateResolution: () => annotateResolution,
30900
31034
  attributeRootCause: () => attributeRootCause,
30901
31035
  bindCanonicalNeighbors: () => bindCanonicalNeighbors,
31036
+ bindResolvedRegressions: () => bindResolvedRegressions,
30902
31037
  closeProblem: () => closeProblem,
30903
31038
  codeAnchorProjection: () => codeAnchorProjection,
30904
31039
  commandSignature: () => commandSignature,
@@ -56818,7 +56953,7 @@ function createWatchBreaker(deps) {
56818
56953
  }
56819
56954
 
56820
56955
  // src/engine.ts
56821
- var DAEMON_VERSION = true ? "2.0.2-dev.1172" : "2.0.0-alpha.0";
56956
+ var DAEMON_VERSION = true ? "2.0.2-dev.1182" : "2.0.0-alpha.0";
56822
56957
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
56823
56958
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
56824
56959
  var GIT_OP_MUTE_MS = 4e3;
@@ -57654,6 +57789,7 @@ function createWorkspaceEngine(opts) {
57654
57789
  let exposureUnshown = 0;
57655
57790
  let corroboratedEdges = 0;
57656
57791
  let refutesStamped = 0;
57792
+ let recurrencesMarked = 0;
57657
57793
  const dispositions = emptyTagDispositions();
57658
57794
  let unresolvedHandles = 0;
57659
57795
  let priorEdgesFlagOff = 0;
@@ -57718,6 +57854,12 @@ function createWorkspaceEngine(opts) {
57718
57854
  if (r.created || r.corroborated) minted++;
57719
57855
  else if (r.rejected) tagsRejected++;
57720
57856
  else tagsRestated++;
57857
+ if (r.recurredResolved) {
57858
+ recurrencesMarked++;
57859
+ console.warn(
57860
+ `[errata] REGRESSION candidate: a resolved problem recurred (${r.problemId}) \u2014 marked resolutionSuspect, not reopened`
57861
+ );
57862
+ }
57721
57863
  if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
57722
57864
  if (r.created || r.corroborated) {
57723
57865
  try {
@@ -57836,6 +57978,35 @@ function createWorkspaceEngine(opts) {
57836
57978
  ts: t,
57837
57979
  hostHarness: turn.hostHarness
57838
57980
  });
57981
+ if (r.created && dedupPath) {
57982
+ try {
57983
+ const closedId = findResolvedAnchorMatch(store, dedupPath, profile.id, p.statement, p.kind);
57984
+ if (closedId && markResolvedRecurrence(store, closedId, t)) {
57985
+ store.mergeEdge({
57986
+ id: `edge_${digest({ from: r.problemId, type: "REGRESSION_OF", to: closedId })}`.slice(0, 24),
57987
+ from: r.problemId,
57988
+ to: closedId,
57989
+ type: "REGRESSION_OF",
57990
+ confidence: 0.5,
57991
+ extractionSource: "daemon-extracted",
57992
+ createdAt: t,
57993
+ lastSeenAt: t,
57994
+ navSuccesses: 0,
57995
+ navFailures: 0,
57996
+ attrs: { provisional: true, machineProposed: true, session: sessionId }
57997
+ });
57998
+ recurrencesMarked++;
57999
+ console.warn(
58000
+ `[errata] REGRESSION candidate: new problem ${r.problemId} paraphrases resolved ${closedId} on ${dedupPath} \u2014 bound REGRESSION_OF, marked resolutionSuspect`
58001
+ );
58002
+ }
58003
+ } catch (err2) {
58004
+ console.warn(
58005
+ "[errata] regression recognition failed (mint unaffected):",
58006
+ err2 instanceof Error ? err2.message : err2
58007
+ );
58008
+ }
58009
+ }
57839
58010
  if (r.created || r.corroborated) {
57840
58011
  minted++;
57841
58012
  if (p.kind !== "constraint") {
@@ -58242,6 +58413,8 @@ function createWorkspaceEngine(opts) {
58242
58413
  // RG-mark: refute witnesses stamped onto local nodes (the local half of
58243
58414
  // the contradict channel; the wire half rides the witness ledger).
58244
58415
  refutesStamped,
58416
+ // RG-recognize tier A: resolved problems the door saw recur this pass.
58417
+ recurrencesMarked,
58245
58418
  // WM-labels calibration cells (see prior-tags exposure split).
58246
58419
  exposureShown,
58247
58420
  exposureEvicted,
@@ -58691,6 +58864,11 @@ function createWorkspaceEngine(opts) {
58691
58864
  }
58692
58865
  refreshContextNow();
58693
58866
  }
58867
+ const rg = bindResolvedRegressions(store, { ts: Date.now() });
58868
+ if (rg.bound > 0) {
58869
+ console.warn(`[errata] REGRESSION: bound ${rg.bound} open problem(s) to resolved predecessors${rg.full ? " (first full sweep)" : ""}`);
58870
+ refreshContextNow();
58871
+ }
58694
58872
  return { embedded: sem.embedded };
58695
58873
  } catch (err2) {
58696
58874
  console.warn("[errata] settled embed failed:", err2);
@@ -58725,6 +58903,10 @@ function createWorkspaceEngine(opts) {
58725
58903
  console.log(`[errata] dedup: merged ${e.merged} paraphrased problem(s) by embedding across ${e.clusters} cluster(s)`);
58726
58904
  refreshContextNow();
58727
58905
  }
58906
+ const rg = bindResolvedRegressions(store, { ts: Date.now() });
58907
+ if (rg.bound > 0) {
58908
+ console.warn(`[errata] REGRESSION: bound ${rg.bound} open problem(s) to resolved predecessors${rg.full ? " (first full sweep)" : ""}`);
58909
+ }
58728
58910
  const cb = bindCanonicalNeighbors(store, { ts: Date.now() });
58729
58911
  if (cb.bound > 0) {
58730
58912
  console.log(`[errata] canonical-bind: +${cb.bound} machine-proposed link(s) across ${cb.scanned} problem(s)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.1172",
3
+ "version": "2.0.2-dev.1182",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -212,7 +212,7 @@ var init_castalia = __esm({
212
212
  DECOMPOSITION_EDGES = ["SPLIT_INTO"];
213
213
  TRANSFER_EDGES = ["MAY_RESOLVE"];
214
214
  SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
215
- SUSPICION_EDGES = ["SUSPECTED_LINK"];
215
+ SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
216
216
  TAXONOMY_EDGES = ["IS_A"];
217
217
  TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
218
218
  GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
@@ -277,8 +277,10 @@ var init_castalia = __esm({
277
277
  "ALTERNATIVE_TO",
278
278
  // symmetric peer marker — weight would let alternatives inflate each other
279
279
  // NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
280
- "SUSPECTED_LINK"
280
+ "SUSPECTED_LINK",
281
281
  // Somnus hypothesis (PLAN_SOMNUS_V2 §4) — never flows rank; navigation-invisible.
282
+ "REGRESSION_OF"
283
+ // recurrence marker (RG-recognize) — a question about a close, not signal-flow.
282
284
  ]);
283
285
  EDGE_WEIGHT = {
284
286
  // Causal — high signal
@@ -391,6 +393,7 @@ var init_castalia = __esm({
391
393
  ALTERNATIVE_TO: 0,
392
394
  // Suspicion — a hypothesis, weightless + PageRank-excluded (never flows rank / EIG).
393
395
  SUSPECTED_LINK: 0,
396
+ REGRESSION_OF: 0,
394
397
  // Git — topology/authorship, weightless (not domain signal-flow)
395
398
  POINTS_AT: 0,
396
399
  PARENT: 0,
@@ -24296,7 +24299,13 @@ var LOCAL_RULE_OVERRIDES = {
24296
24299
  // the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
24297
24300
  // stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
24298
24301
  // this rule exists at all.
24299
- FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
24302
+ FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] },
24303
+ // RG-recognize: a NEW open Problem re-observing a RESOLVED one ("this is
24304
+ // back — the fix didn't hold"). Local-only for now: the instance drain's
24305
+ // INSTANCE_EDGES allowlist doesn't ship it, and the cloud door's matrix
24306
+ // doesn't know it — crossing the membrane is a deliberate later step, never
24307
+ // a side effect (the half-shipped-edge-type lesson).
24308
+ REGRESSION_OF: { from: ["Problem"], to: ["Problem"] }
24300
24309
  };
24301
24310
  function localEdgeViolation(fromLabel, type, toLabel) {
24302
24311
  if (type in LOCAL_RULE_OVERRIDES) {
@@ -25758,6 +25767,7 @@ function foldDuplicateProblem(store2, dup, survivor, ts) {
25758
25767
  function isConstraintProblem(node) {
25759
25768
  return node.attrs["kind"] === "constraint";
25760
25769
  }
25770
+ var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
25761
25771
  var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
25762
25772
  "Solution",
25763
25773
  "RootCause",