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

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.
Files changed (3) hide show
  1. package/errata.mjs +147 -17
  2. package/package.json +1 -1
  3. package/pass-worker.mjs +14 -1
package/errata.mjs CHANGED
@@ -19131,14 +19131,19 @@ function ingestDesignProblem(store, flag, opts) {
19131
19131
  let created = false;
19132
19132
  let corroborated = false;
19133
19133
  let recurredResolved = false;
19134
+ let reopenedFromRegression = false;
19134
19135
  if (existing) {
19135
19136
  const resolvedUnretracted = existing.attrs["resolvedAt"] != null && existing.attrs["resolvedAs"] == null;
19136
19137
  const recurAttrs = () => {
19137
19138
  recurredResolved = true;
19139
+ const sources2 = existing.attrs["recurrenceSources"] ?? [];
19138
19140
  return {
19139
19141
  resolutionSuspect: true,
19140
19142
  recurredAt: opts.ts,
19141
- recurrences: (Number(existing.attrs["recurrences"]) || 0) + 1
19143
+ recurrences: (Number(existing.attrs["recurrences"]) || 0) + 1,
19144
+ // RG-reopen: the door touch carries its session — the reopen check
19145
+ // after the update counts DISTINCT sources against REOPEN_MIN_SOURCES.
19146
+ ...sources2.includes(opts.source) ? {} : { recurrenceSources: [...sources2, opts.source].slice(-8) }
19142
19147
  };
19143
19148
  };
19144
19149
  const sources = existing.attrs["sources"] ?? [];
@@ -19177,11 +19182,13 @@ function ingestDesignProblem(store, flag, opts) {
19177
19182
  ...reinforcedSeqAttrs(store, existing.label),
19178
19183
  lastUpdatedAt: opts.ts
19179
19184
  });
19185
+ if (recurredResolved) reopenedFromRegression = maybeReopenFromRegression(store, problemId, opts.ts);
19180
19186
  } else if (resolvedUnretracted && opts.ts - (Number(existing.attrs["recurredAt"]) || 0) > RECURRENCE_DEBOUNCE_MS) {
19181
19187
  store.updateNode(problemId, {
19182
19188
  attrs: { ...existing.attrs, ...recurAttrs() },
19183
19189
  lastUpdatedAt: opts.ts
19184
19190
  });
19191
+ reopenedFromRegression = maybeReopenFromRegression(store, problemId, opts.ts);
19185
19192
  }
19186
19193
  } else {
19187
19194
  created = true;
@@ -19245,7 +19252,8 @@ function ingestDesignProblem(store, flag, opts) {
19245
19252
  solutionId,
19246
19253
  anchored,
19247
19254
  linkedPackages,
19248
- ...recurredResolved ? { recurredResolved } : {}
19255
+ ...recurredResolved ? { recurredResolved } : {},
19256
+ ...reopenedFromRegression ? { reopenedFromRegression } : {}
19249
19257
  };
19250
19258
  }
19251
19259
  function canonicalizePatternText(name2) {
@@ -19453,20 +19461,54 @@ function findResolvedAnchorMatch(store, relPath, workspaceId2, statement, kind =
19453
19461
  }
19454
19462
  return best?.id ?? null;
19455
19463
  }
19456
- function markResolvedRecurrence(store, nodeId, ts) {
19464
+ function maybeReopenFromRegression(store, nodeId, ts) {
19457
19465
  const n = store.getNode(nodeId);
19458
19466
  if (!n) return false;
19459
19467
  if (n.attrs["resolvedAt"] == null || n.attrs["resolvedAs"] != null) return false;
19468
+ const sources = n.attrs["recurrenceSources"] ?? [];
19469
+ if (new Set(sources).size < REOPEN_MIN_SOURCES) return false;
19470
+ const attrs = { ...n.attrs };
19471
+ delete attrs["resolvedAt"];
19472
+ delete attrs["resolutionSuspect"];
19473
+ attrs["reopenedFrom"] = "regression";
19474
+ attrs["reopenedAt"] = ts;
19475
+ store.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
19476
+ return true;
19477
+ }
19478
+ function markResolvedRecurrence(store, nodeId, ts, source) {
19479
+ const n = store.getNode(nodeId);
19480
+ if (!n) return { marked: false, reopened: false };
19481
+ if (n.attrs["resolvedAt"] == null || n.attrs["resolvedAs"] != null) {
19482
+ return { marked: false, reopened: false };
19483
+ }
19484
+ const sources = n.attrs["recurrenceSources"] ?? [];
19460
19485
  store.updateNode(nodeId, {
19461
19486
  attrs: {
19462
19487
  ...n.attrs,
19463
19488
  resolutionSuspect: true,
19464
19489
  recurredAt: ts,
19465
- recurrences: (Number(n.attrs["recurrences"]) || 0) + 1
19490
+ recurrences: (Number(n.attrs["recurrences"]) || 0) + 1,
19491
+ ...source && !sources.includes(source) ? { recurrenceSources: [...sources, source].slice(-RECURRENCE_SOURCES_CAP) } : {}
19466
19492
  },
19467
19493
  lastUpdatedAt: ts
19468
19494
  });
19469
- return true;
19495
+ const reopened = source ? maybeReopenFromRegression(store, nodeId, ts) : false;
19496
+ return { marked: true, reopened };
19497
+ }
19498
+ function confirmFixHeld(store, solutionId, ts) {
19499
+ let acknowledged = 0;
19500
+ for (const e of store.inEdges(solutionId, ["SOLVED_BY", "FIXED_BY"])) {
19501
+ const p = store.getNode(e.from);
19502
+ if (!p || p.label !== "Problem") continue;
19503
+ if (p.attrs["resolutionSuspect"] !== true || p.attrs["resolvedAt"] == null) continue;
19504
+ const attrs = { ...p.attrs };
19505
+ delete attrs["resolutionSuspect"];
19506
+ attrs["fixHeldConfirmedAt"] = ts;
19507
+ delete attrs["recurrenceSources"];
19508
+ store.updateNode(p.id, { attrs, lastUpdatedAt: ts });
19509
+ acknowledged++;
19510
+ }
19511
+ return acknowledged;
19470
19512
  }
19471
19513
  function isFragmentFixNote(note) {
19472
19514
  const t = note.trim();
@@ -19869,7 +19911,7 @@ function priorExposure(store, nodeId, atSeq) {
19869
19911
  if ((n.attrs["evictedCount"] ?? 0) > 0) return "evicted";
19870
19912
  return "unshown";
19871
19913
  }
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;
19914
+ var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, RECURRENCE_DEBOUNCE_MS, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, REOPEN_MIN_SOURCES, RECURRENCE_SOURCES_CAP, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX, FIX_CANDIDATE_ASK_LIMIT;
19873
19915
  var init_design_problem = __esm({
19874
19916
  "../../packages/local-graph/src/design-problem.ts"() {
19875
19917
  "use strict";
@@ -19909,6 +19951,8 @@ var init_design_problem = __esm({
19909
19951
  "where"
19910
19952
  ]);
19911
19953
  SAME_ANCHOR_DEDUP_JACCARD = 0.6;
19954
+ REOPEN_MIN_SOURCES = 2;
19955
+ RECURRENCE_SOURCES_CAP = 8;
19912
19956
  CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
19913
19957
  "Solution",
19914
19958
  "RootCause",
@@ -22462,6 +22506,19 @@ var init_mechanism_liveness = __esm({
22462
22506
  effectCounter: "recurrencesMarked",
22463
22507
  note: "RG-recognize tier A (exact-id door); tiers B/C add REGRESSION_OF edges; reopen is RG-reopen's"
22464
22508
  },
22509
+ {
22510
+ id: "regression-reopen",
22511
+ what: "flips a marked close back open once two DISTINCT sessions witnessed the recurrence",
22512
+ pass: "capture",
22513
+ // Reopens should be rarer than recurrences by construction — written-at-all.
22514
+ fed: {
22515
+ labels: ["Problem"],
22516
+ attr: "reopenedFrom",
22517
+ minFraction: 0
22518
+ },
22519
+ effectCounter: "reopenedFromRegression",
22520
+ note: "RG-reopen; the machine sweep never counts as a witness \u2014 only sessions do"
22521
+ },
22465
22522
  {
22466
22523
  id: "fix-candidate-ask",
22467
22524
  what: "asks an agent whether an edit to a problem's anchor fixed it",
@@ -22722,6 +22779,7 @@ __export(src_exports2, {
22722
22779
  PERCOLATING_EDGES: () => PERCOLATING_EDGES,
22723
22780
  PERCOLATING_LABELS: () => PERCOLATING_LABELS,
22724
22781
  RECURRENCE_DEBOUNCE_MS: () => RECURRENCE_DEBOUNCE_MS,
22782
+ REOPEN_MIN_SOURCES: () => REOPEN_MIN_SOURCES,
22725
22783
  SAME_ANCHOR_DEDUP_JACCARD: () => SAME_ANCHOR_DEDUP_JACCARD,
22726
22784
  STALL_MIN_RUNS: () => STALL_MIN_RUNS,
22727
22785
  STALL_MIN_SPAN_MS: () => STALL_MIN_SPAN_MS,
@@ -22750,6 +22808,7 @@ __export(src_exports2, {
22750
22808
  communityInductionRequests: () => communityInductionRequests,
22751
22809
  communitySeeds: () => communitySeeds,
22752
22810
  compareSemver: () => compareSemver,
22811
+ confirmFixHeld: () => confirmFixHeld,
22753
22812
  corroborateClaim: () => corroborateClaim,
22754
22813
  creditAssignment: () => creditAssignment,
22755
22814
  crystallize: () => crystallize,
@@ -22792,6 +22851,7 @@ __export(src_exports2, {
22792
22851
  matchLanguagesInText: () => matchLanguagesInText,
22793
22852
  matchPackagesInText: () => matchPackagesInText,
22794
22853
  matchSymbolsInText: () => matchSymbolsInText,
22854
+ maybeReopenFromRegression: () => maybeReopenFromRegression,
22795
22855
  mergeCloudCounts: () => mergeCloudCounts,
22796
22856
  mergeDuplicateProblems: () => mergeDuplicateProblems,
22797
22857
  migrateProjectAlias: () => migrateProjectAlias,
@@ -22893,7 +22953,19 @@ function buildSnapshot(opts) {
22893
22953
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
22894
22954
  }));
22895
22955
  const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
22896
- const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
22956
+ const regressions = problems.filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0).flatMap(
22957
+ (open2) => opts.store.outEdges(open2.id, ["REGRESSION_OF"]).flatMap((e) => {
22958
+ const closed = opts.store.getNode(e.to);
22959
+ if (!closed || closed.attrs["resolutionSuspect"] !== true || closed.attrs["resolvedAt"] == null) {
22960
+ return [];
22961
+ }
22962
+ const solEdge = opts.store.outEdges(closed.id, ["SOLVED_BY"])[0];
22963
+ const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
22964
+ return [{ open: open2, closed, ...solution ? { solution } : {} }];
22965
+ })
22966
+ ).sort((a, b) => Number(b.closed.attrs["recurredAt"] ?? 0) - Number(a.closed.attrs["recurredAt"] ?? 0)).slice(0, 2);
22967
+ const regressionClosedIds = new Set(regressions.map((r) => r.closed.id));
22968
+ const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).filter((p) => !regressionClosedIds.has(p.id)).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
22897
22969
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
22898
22970
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
22899
22971
  return { node: p, ...solution ? { solution } : {} };
@@ -22963,6 +23035,7 @@ function buildSnapshot(opts) {
22963
23035
  recentProblems: recent2,
22964
23036
  recentResolved,
22965
23037
  recentConstraints,
23038
+ regressions,
22966
23039
  ...causalNudge ? { causalNudge } : {},
22967
23040
  ...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
22968
23041
  ...domainNudge ? { domainNudge } : {},
@@ -23127,7 +23200,7 @@ function renderSnapshot(s) {
23127
23200
  lines.push("");
23128
23201
  }
23129
23202
  lines.push("### Recently observed problems in this workspace");
23130
- if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
23203
+ if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0 && s.regressions.length === 0) {
23131
23204
  lines.push("- _none yet \u2014 errata is still building its model_");
23132
23205
  } else {
23133
23206
  if (s.recentProblems.length > 0) {
@@ -23149,6 +23222,22 @@ function renderSnapshot(s) {
23149
23222
  );
23150
23223
  }
23151
23224
  }
23225
+ if (s.regressions.length > 0) {
23226
+ lines.push("_\u{1F501} RECURRED \u2014 a previously-solved problem appears to be back; did the fix hold?_");
23227
+ for (const g of s.regressions) {
23228
+ lines.push(`- **${g.open.description}** \u2014 \`${g.open.id}\`${tagOf(g.open)}`);
23229
+ lines.push(` - previously resolved as: ${g.closed.description.slice(0, 120)} \u2014 \`${g.closed.id}\``);
23230
+ if (g.solution) {
23231
+ lines.push(
23232
+ ` - the fix on record: ${g.solution.description.slice(0, 120)} \u2014 \`${g.solution.id}\` \xB7 fix still holds \u2192 (confirm:[${g.solution.id}]) \xB7 hitting it again \u2192 flag it (a second independent witness reopens the close)`
23233
+ );
23234
+ } else {
23235
+ lines.push(
23236
+ ` - no fix prose on record \xB7 hitting it again \u2192 flag it (a second independent witness reopens the close)`
23237
+ );
23238
+ }
23239
+ }
23240
+ }
23152
23241
  if (s.recentConstraints.length > 0) {
23153
23242
  lines.push("_Design tensions \u2014 constraints this work is shaped around, not defects to fix:_");
23154
23243
  for (const c of s.recentConstraints) {
@@ -29532,7 +29621,7 @@ function bindResolvedRegressions(store, opts) {
29532
29621
  }
29533
29622
  if (!best) continue;
29534
29623
  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;
29624
+ if (!markResolvedRecurrence(store, best.node.id, opts.ts).marked) continue;
29536
29625
  store.mergeEdge({
29537
29626
  id: `edge_${digest({ from: f.id, type: "REGRESSION_OF", to: best.node.id })}`.slice(0, 24),
29538
29627
  from: f.id,
@@ -54043,7 +54132,7 @@ function wireWitnessItems(items) {
54043
54132
  var REFUTE_REASONS_CAP = 5;
54044
54133
  var REFUTE_WITNESS_KEYS_CAP = 16;
54045
54134
  function applyLocalRefutations(store, refutes, ts) {
54046
- const out2 = { stamped: 0, duplicate: 0, unknownNode: 0 };
54135
+ const out2 = { stamped: 0, duplicate: 0, unknownNode: 0, stampedIds: [] };
54047
54136
  for (const r of refutes) {
54048
54137
  const node2 = store.getNode(r.nodeId);
54049
54138
  if (!node2) {
@@ -54067,6 +54156,7 @@ function applyLocalRefutations(store, refutes, ts) {
54067
54156
  lastUpdatedAt: ts
54068
54157
  });
54069
54158
  out2.stamped++;
54159
+ out2.stampedIds.push(r.nodeId);
54070
54160
  }
54071
54161
  return out2;
54072
54162
  }
@@ -56953,7 +57043,7 @@ function createWatchBreaker(deps) {
56953
57043
  }
56954
57044
 
56955
57045
  // src/engine.ts
56956
- var DAEMON_VERSION = true ? "2.0.2-dev.1182" : "2.0.0-alpha.0";
57046
+ var DAEMON_VERSION = true ? "2.0.2-dev.1196" : "2.0.0-alpha.0";
56957
57047
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
56958
57048
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
56959
57049
  var GIT_OP_MUTE_MS = 4e3;
@@ -57790,6 +57880,8 @@ function createWorkspaceEngine(opts) {
57790
57880
  let corroboratedEdges = 0;
57791
57881
  let refutesStamped = 0;
57792
57882
  let recurrencesMarked = 0;
57883
+ let reopenedFromRegression = 0;
57884
+ let fixHeldConfirmed = 0;
57793
57885
  const dispositions = emptyTagDispositions();
57794
57886
  let unresolvedHandles = 0;
57795
57887
  let priorEdgesFlagOff = 0;
@@ -57856,9 +57948,16 @@ function createWorkspaceEngine(opts) {
57856
57948
  else tagsRestated++;
57857
57949
  if (r.recurredResolved) {
57858
57950
  recurrencesMarked++;
57859
- console.warn(
57860
- `[errata] REGRESSION candidate: a resolved problem recurred (${r.problemId}) \u2014 marked resolutionSuspect, not reopened`
57861
- );
57951
+ if (r.reopenedFromRegression) {
57952
+ reopenedFromRegression++;
57953
+ console.warn(
57954
+ `[errata] REOPENED from regression: ${r.problemId} \u2014 second independent witness; the fix did not hold`
57955
+ );
57956
+ } else {
57957
+ console.warn(
57958
+ `[errata] REGRESSION candidate: a resolved problem recurred (${r.problemId}) \u2014 marked resolutionSuspect, not reopened`
57959
+ );
57960
+ }
57862
57961
  }
57863
57962
  if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
57864
57963
  if (r.created || r.corroborated) {
@@ -57937,7 +58036,32 @@ function createWorkspaceEngine(opts) {
57937
58036
  });
57938
58037
  priorEdges += plan.priorEdges;
57939
58038
  corroboratedEdges += plan.corroboratedEdges;
57940
- refutesStamped += applyLocalRefutations(store, plan.refutes, t).stamped;
58039
+ const refuteResult = applyLocalRefutations(store, plan.refutes, t);
58040
+ refutesStamped += refuteResult.stamped;
58041
+ for (const c of plan.corroborations) {
58042
+ const node2 = store.getNode(c.nodeId);
58043
+ if (node2?.label !== "Solution") continue;
58044
+ const held = confirmFixHeld(store, c.nodeId, t);
58045
+ if (held > 0) {
58046
+ fixHeldConfirmed += held;
58047
+ console.log(`[errata] fix-held confirmed: ${node2.id} \u2014 ${held} regression question(s) acknowledged`);
58048
+ }
58049
+ }
58050
+ for (const id of refuteResult.stampedIds) {
58051
+ const node2 = store.getNode(id);
58052
+ if (node2?.label !== "Solution") continue;
58053
+ for (const e of store.inEdges(id, ["SOLVED_BY", "FIXED_BY"])) {
58054
+ const touch = markResolvedRecurrence(store, e.from, t, sessionId);
58055
+ if (!touch.marked) continue;
58056
+ recurrencesMarked++;
58057
+ if (touch.reopened) {
58058
+ reopenedFromRegression++;
58059
+ console.warn(
58060
+ `[errata] REOPENED from regression: ${e.from} \u2014 its solution ${id} was refuted by a second independent witness`
58061
+ );
58062
+ }
58063
+ }
58064
+ }
57941
58065
  addTagDispositions(dispositions, plan.dispositions);
57942
58066
  unresolvedHandles += plan.unresolvedHandles.length;
57943
58067
  priorEdgesFlagOff += plan.priorEdgesSuppressed.flagOff;
@@ -57981,7 +58105,8 @@ function createWorkspaceEngine(opts) {
57981
58105
  if (r.created && dedupPath) {
57982
58106
  try {
57983
58107
  const closedId = findResolvedAnchorMatch(store, dedupPath, profile.id, p.statement, p.kind);
57984
- if (closedId && markResolvedRecurrence(store, closedId, t)) {
58108
+ const touch = closedId ? markResolvedRecurrence(store, closedId, t, sessionId) : { marked: false, reopened: false };
58109
+ if (closedId && touch.marked) {
57985
58110
  store.mergeEdge({
57986
58111
  id: `edge_${digest({ from: r.problemId, type: "REGRESSION_OF", to: closedId })}`.slice(0, 24),
57987
58112
  from: r.problemId,
@@ -57996,8 +58121,9 @@ function createWorkspaceEngine(opts) {
57996
58121
  attrs: { provisional: true, machineProposed: true, session: sessionId }
57997
58122
  });
57998
58123
  recurrencesMarked++;
58124
+ if (touch.reopened) reopenedFromRegression++;
57999
58125
  console.warn(
58000
- `[errata] REGRESSION candidate: new problem ${r.problemId} paraphrases resolved ${closedId} on ${dedupPath} \u2014 bound REGRESSION_OF, marked resolutionSuspect`
58126
+ `[errata] REGRESSION candidate: new problem ${r.problemId} paraphrases resolved ${closedId} on ${dedupPath} \u2014 bound REGRESSION_OF, marked resolutionSuspect` + (touch.reopened ? "; second independent witness \u2014 REOPENED" : "")
58001
58127
  );
58002
58128
  }
58003
58129
  } catch (err2) {
@@ -58415,6 +58541,10 @@ function createWorkspaceEngine(opts) {
58415
58541
  refutesStamped,
58416
58542
  // RG-recognize tier A: resolved problems the door saw recur this pass.
58417
58543
  recurrencesMarked,
58544
+ // RG-reopen: closes flipped open on the two-touch evidence bar.
58545
+ reopenedFromRegression,
58546
+ // RG-prime: regression questions acknowledged by a fix-held confirm.
58547
+ fixHeldConfirmed,
58418
58548
  // WM-labels calibration cells (see prior-tags exposure split).
58419
58549
  exposureShown,
58420
58550
  exposureEvicted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.1182",
3
+ "version": "2.0.2-dev.1196",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -27052,7 +27052,19 @@ function buildSnapshot(opts) {
27052
27052
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
27053
27053
  }));
27054
27054
  const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
27055
- const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
27055
+ const regressions = problems.filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0).flatMap(
27056
+ (open) => opts.store.outEdges(open.id, ["REGRESSION_OF"]).flatMap((e) => {
27057
+ const closed = opts.store.getNode(e.to);
27058
+ if (!closed || closed.attrs["resolutionSuspect"] !== true || closed.attrs["resolvedAt"] == null) {
27059
+ return [];
27060
+ }
27061
+ const solEdge = opts.store.outEdges(closed.id, ["SOLVED_BY"])[0];
27062
+ const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
27063
+ return [{ open, closed, ...solution ? { solution } : {} }];
27064
+ })
27065
+ ).sort((a, b) => Number(b.closed.attrs["recurredAt"] ?? 0) - Number(a.closed.attrs["recurredAt"] ?? 0)).slice(0, 2);
27066
+ const regressionClosedIds = new Set(regressions.map((r) => r.closed.id));
27067
+ const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).filter((p) => !regressionClosedIds.has(p.id)).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
27056
27068
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
27057
27069
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
27058
27070
  return { node: p, ...solution ? { solution } : {} };
@@ -27122,6 +27134,7 @@ function buildSnapshot(opts) {
27122
27134
  recentProblems: recent,
27123
27135
  recentResolved,
27124
27136
  recentConstraints,
27137
+ regressions,
27125
27138
  ...causalNudge ? { causalNudge } : {},
27126
27139
  ...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
27127
27140
  ...domainNudge ? { domainNudge } : {},