@inerrata-corporation/errata 2.0.2-dev.1166 → 2.0.2-dev.1179

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([
@@ -22357,6 +22431,37 @@ var init_mechanism_liveness = __esm({
22357
22431
  STALL_MIN_RUNS = 3;
22358
22432
  STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
22359
22433
  MECHANISMS = [
22434
+ {
22435
+ id: "refute-local-stamp",
22436
+ what: "stamps a harvested refute witness onto the LOCAL node (refutedAt/refutations/reason)",
22437
+ // Stamped at harvest, so the capture pass carries the effect counter.
22438
+ pass: "capture",
22439
+ // Refutes are rare by design (a verdict, not a lean-on) — the bar is "the
22440
+ // field is written at all", the revisitAnsweredWhen precedent. RG-mark
22441
+ // ships the producer; RG-recall is the ranking consumer — until it lands,
22442
+ // THIS descriptor is the consumer, which is exactly the condition the
22443
+ // probe exists to keep visible rather than let rot silently.
22444
+ fed: {
22445
+ labels: ["Problem", "Solution", "RootCause", "Pattern"],
22446
+ attr: "refutedAt",
22447
+ minFraction: 0
22448
+ },
22449
+ effectCounter: "refutesStamped",
22450
+ note: "RG-mark; reason text is local-only (wire ships nodeId+witnessKey)"
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
+ },
22360
22465
  {
22361
22466
  id: "fix-candidate-ask",
22362
22467
  what: "asks an agent whether an edit to a problem's anchor fixed it",
@@ -22616,6 +22721,7 @@ __export(src_exports2, {
22616
22721
  PERCOLATING_DRIFT_EDGES: () => PERCOLATING_DRIFT_EDGES,
22617
22722
  PERCOLATING_EDGES: () => PERCOLATING_EDGES,
22618
22723
  PERCOLATING_LABELS: () => PERCOLATING_LABELS,
22724
+ RECURRENCE_DEBOUNCE_MS: () => RECURRENCE_DEBOUNCE_MS,
22619
22725
  SAME_ANCHOR_DEDUP_JACCARD: () => SAME_ANCHOR_DEDUP_JACCARD,
22620
22726
  STALL_MIN_RUNS: () => STALL_MIN_RUNS,
22621
22727
  STALL_MIN_SPAN_MS: () => STALL_MIN_SPAN_MS,
@@ -22658,6 +22764,7 @@ __export(src_exports2, {
22658
22764
  findMintTimeDuplicate: () => findMintTimeDuplicate,
22659
22765
  findOrCreatePackage: () => findOrCreatePackage,
22660
22766
  findPath: () => findPath,
22767
+ findResolvedAnchorMatch: () => findResolvedAnchorMatch,
22661
22768
  findReusableSolution: () => findReusableSolution,
22662
22769
  flattenCloudCounts: () => flattenCloudCounts,
22663
22770
  foldDuplicateProblem: () => foldDuplicateProblem,
@@ -22680,6 +22787,7 @@ __export(src_exports2, {
22680
22787
  localEdgeViolation: () => localEdgeViolation,
22681
22788
  markDiscriminatorAsked: () => markDiscriminatorAsked,
22682
22789
  markFixCandidates: () => markFixCandidates,
22790
+ markResolvedRecurrence: () => markResolvedRecurrence,
22683
22791
  markRevisit: () => markRevisit,
22684
22792
  matchLanguagesInText: () => matchLanguagesInText,
22685
22793
  matchPackagesInText: () => matchPackagesInText,
@@ -29400,6 +29508,50 @@ function mergeProblemsByEmbedding(store, opts) {
29400
29508
  });
29401
29509
  return report;
29402
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
+ }
29403
29555
  function bindCanonicalNeighbors(store, opts) {
29404
29556
  const k = opts.k ?? 3;
29405
29557
  const live2 = (n) => n.attrs["mergedInto"] === void 0;
@@ -30881,6 +31033,7 @@ __export(src_exports4, {
30881
31033
  annotateResolution: () => annotateResolution,
30882
31034
  attributeRootCause: () => attributeRootCause,
30883
31035
  bindCanonicalNeighbors: () => bindCanonicalNeighbors,
31036
+ bindResolvedRegressions: () => bindResolvedRegressions,
30884
31037
  closeProblem: () => closeProblem,
30885
31038
  codeAnchorProjection: () => codeAnchorProjection,
30886
31039
  commandSignature: () => commandSignature,
@@ -53880,6 +54033,43 @@ function typePriorEdge(sourceLabel, targetLabel2, sentence = "") {
53880
54033
  }
53881
54034
  return "RELATES_TO";
53882
54035
  }
54036
+ function refuteReason(statement) {
54037
+ const s = (statement ?? "").trim();
54038
+ return s.length > 0 ? s.slice(0, 240) : void 0;
54039
+ }
54040
+ function wireWitnessItems(items) {
54041
+ return items.map(({ nodeId, witnessKey }) => ({ nodeId, witnessKey }));
54042
+ }
54043
+ var REFUTE_REASONS_CAP = 5;
54044
+ var REFUTE_WITNESS_KEYS_CAP = 16;
54045
+ function applyLocalRefutations(store, refutes, ts) {
54046
+ const out2 = { stamped: 0, duplicate: 0, unknownNode: 0 };
54047
+ for (const r of refutes) {
54048
+ const node2 = store.getNode(r.nodeId);
54049
+ if (!node2) {
54050
+ out2.unknownNode++;
54051
+ continue;
54052
+ }
54053
+ const keys = node2.attrs["refuteWitnessKeys"] ?? [];
54054
+ if (keys.includes(r.witnessKey)) {
54055
+ out2.duplicate++;
54056
+ continue;
54057
+ }
54058
+ const reasons = node2.attrs["refuteReasons"] ?? [];
54059
+ store.updateNode(r.nodeId, {
54060
+ attrs: {
54061
+ ...node2.attrs,
54062
+ refutedAt: ts,
54063
+ refutations: (Number(node2.attrs["refutations"]) || 0) + 1,
54064
+ ...r.reason ? { refuteReasons: [r.reason, ...reasons].slice(0, REFUTE_REASONS_CAP) } : {},
54065
+ refuteWitnessKeys: [...keys, r.witnessKey].slice(-REFUTE_WITNESS_KEYS_CAP)
54066
+ },
54067
+ lastUpdatedAt: ts
54068
+ });
54069
+ out2.stamped++;
54070
+ }
54071
+ return out2;
54072
+ }
53883
54073
  var PRIOR_TAG_INSTRUCTION = buildAgentInstruction();
53884
54074
  function isEdgeElicitationEnabled() {
53885
54075
  return (process.env["EDGE_ELICITATION_ENABLED"] ?? "true").toLowerCase() !== "false";
@@ -54140,7 +54330,8 @@ function harvestInlineTags(store, text, opts) {
54140
54330
  if (nodeId) {
54141
54331
  const witnessKey = `refute:${nodeId}:${digest({ s: tag.statement ?? "" })}`.slice(0, 72);
54142
54332
  if (!plan.refutes.some((r) => r.witnessKey === witnessKey)) {
54143
- plan.refutes.push({ nodeId, witnessKey });
54333
+ const reason = refuteReason(tag.statement);
54334
+ plan.refutes.push({ nodeId, witnessKey, ...reason ? { reason } : {} });
54144
54335
  }
54145
54336
  }
54146
54337
  }
@@ -54175,7 +54366,8 @@ function harvestInlineTags(store, text, opts) {
54175
54366
  } else {
54176
54367
  const witnessKey = `refute:${targetId}:${digest({ s: tag.statement ?? "" })}`.slice(0, 72);
54177
54368
  if (!plan.refutes.some((r) => r.witnessKey === witnessKey)) {
54178
- plan.refutes.push({ nodeId: targetId, witnessKey });
54369
+ const reason = refuteReason(tag.statement);
54370
+ plan.refutes.push({ nodeId: targetId, witnessKey, ...reason ? { reason } : {} });
54179
54371
  }
54180
54372
  }
54181
54373
  }
@@ -56761,7 +56953,7 @@ function createWatchBreaker(deps) {
56761
56953
  }
56762
56954
 
56763
56955
  // src/engine.ts
56764
- var DAEMON_VERSION = true ? "2.0.2-dev.1166" : "2.0.0-alpha.0";
56956
+ var DAEMON_VERSION = true ? "2.0.2-dev.1179" : "2.0.0-alpha.0";
56765
56957
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
56766
56958
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
56767
56959
  var GIT_OP_MUTE_MS = 4e3;
@@ -57596,6 +57788,8 @@ function createWorkspaceEngine(opts) {
57596
57788
  let exposureEvicted = 0;
57597
57789
  let exposureUnshown = 0;
57598
57790
  let corroboratedEdges = 0;
57791
+ let refutesStamped = 0;
57792
+ let recurrencesMarked = 0;
57599
57793
  const dispositions = emptyTagDispositions();
57600
57794
  let unresolvedHandles = 0;
57601
57795
  let priorEdgesFlagOff = 0;
@@ -57660,6 +57854,12 @@ function createWorkspaceEngine(opts) {
57660
57854
  if (r.created || r.corroborated) minted++;
57661
57855
  else if (r.rejected) tagsRejected++;
57662
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
+ }
57663
57863
  if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
57664
57864
  if (r.created || r.corroborated) {
57665
57865
  try {
@@ -57737,6 +57937,7 @@ function createWorkspaceEngine(opts) {
57737
57937
  });
57738
57938
  priorEdges += plan.priorEdges;
57739
57939
  corroboratedEdges += plan.corroboratedEdges;
57940
+ refutesStamped += applyLocalRefutations(store, plan.refutes, t).stamped;
57740
57941
  addTagDispositions(dispositions, plan.dispositions);
57741
57942
  unresolvedHandles += plan.unresolvedHandles.length;
57742
57943
  priorEdgesFlagOff += plan.priorEdgesSuppressed.flagOff;
@@ -57777,6 +57978,35 @@ function createWorkspaceEngine(opts) {
57777
57978
  ts: t,
57778
57979
  hostHarness: turn.hostHarness
57779
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
+ }
57780
58010
  if (r.created || r.corroborated) {
57781
58011
  minted++;
57782
58012
  if (p.kind !== "constraint") {
@@ -58112,7 +58342,7 @@ function createWorkspaceEngine(opts) {
58112
58342
  if (typeof cloud.reportContradictions === "function") {
58113
58343
  await sendWitnesses(
58114
58344
  "contradict",
58115
- plan.refutes,
58345
+ wireWitnessItems(plan.refutes),
58116
58346
  (items2, session) => cloud.reportContradictions({
58117
58347
  daemonVersion: DAEMON_VERSION,
58118
58348
  projectId: profile.id,
@@ -58180,6 +58410,11 @@ function createWorkspaceEngine(opts) {
58180
58410
  triaged,
58181
58411
  priorEdges,
58182
58412
  corroboratedEdges,
58413
+ // RG-mark: refute witnesses stamped onto local nodes (the local half of
58414
+ // the contradict channel; the wire half rides the witness ledger).
58415
+ refutesStamped,
58416
+ // RG-recognize tier A: resolved problems the door saw recur this pass.
58417
+ recurrencesMarked,
58183
58418
  // WM-labels calibration cells (see prior-tags exposure split).
58184
58419
  exposureShown,
58185
58420
  exposureEvicted,
@@ -58623,6 +58858,10 @@ function createWorkspaceEngine(opts) {
58623
58858
  if (e.merged > 0) {
58624
58859
  console.log(`[errata] dedup: merged ${e.merged} paraphrased problem(s) by embedding across ${e.clusters} cluster(s)`);
58625
58860
  }
58861
+ const rg = bindResolvedRegressions(store, { ts: Date.now() });
58862
+ if (rg.bound > 0) {
58863
+ console.warn(`[errata] REGRESSION: bound ${rg.bound} open problem(s) to resolved predecessors${rg.full ? " (first full sweep)" : ""}`);
58864
+ }
58626
58865
  const cb = bindCanonicalNeighbors(store, { ts: Date.now() });
58627
58866
  if (cb.bound > 0) {
58628
58867
  console.log(`[errata] canonical-bind: +${cb.bound} machine-proposed link(s) across ${cb.scanned} problem(s)`);
@@ -58663,6 +58902,10 @@ function createWorkspaceEngine(opts) {
58663
58902
  console.log(`[errata] dedup: merged ${e.merged} paraphrased problem(s) by embedding across ${e.clusters} cluster(s)`);
58664
58903
  refreshContextNow();
58665
58904
  }
58905
+ const rg = bindResolvedRegressions(store, { ts: Date.now() });
58906
+ if (rg.bound > 0) {
58907
+ console.warn(`[errata] REGRESSION: bound ${rg.bound} open problem(s) to resolved predecessors${rg.full ? " (first full sweep)" : ""}`);
58908
+ }
58666
58909
  const cb = bindCanonicalNeighbors(store, { ts: Date.now() });
58667
58910
  if (cb.bound > 0) {
58668
58911
  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.1166",
3
+ "version": "2.0.2-dev.1179",
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",