@inerrata-corporation/errata 2.0.2-dev.540 → 2.0.2-dev.548

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16580,6 +16580,16 @@ var SqliteGraphStore = class {
16580
16580
  if (revived > 0) this.mutations++;
16581
16581
  return revived;
16582
16582
  }
16583
+ recentEdgeAttrCoverage(marker, attr, limit) {
16584
+ const r = this.db.prepare(
16585
+ `SELECT COUNT(*) AS total,
16586
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
16587
+ FROM (SELECT attrs_json FROM edges
16588
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
16589
+ ORDER BY created_at DESC LIMIT ?)`
16590
+ ).get(attr, `%${marker}%`, limit);
16591
+ return { count: Number(r.count), total: Number(r.total) };
16592
+ }
16583
16593
  liveNodeIds(ids) {
16584
16594
  const live = /* @__PURE__ */ new Set();
16585
16595
  const CHUNK = 900;
package/errata.mjs CHANGED
@@ -17255,6 +17255,16 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
17255
17255
  if (revived > 0) this.mutations++;
17256
17256
  return revived;
17257
17257
  }
17258
+ recentEdgeAttrCoverage(marker, attr, limit) {
17259
+ const r = this.db.prepare(
17260
+ `SELECT COUNT(*) AS total,
17261
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
17262
+ FROM (SELECT attrs_json FROM edges
17263
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
17264
+ ORDER BY created_at DESC LIMIT ?)`
17265
+ ).get(attr, `%${marker}%`, limit);
17266
+ return { count: Number(r.count), total: Number(r.total) };
17267
+ }
17258
17268
  liveNodeIds(ids) {
17259
17269
  const live = /* @__PURE__ */ new Set();
17260
17270
  const CHUNK = 900;
@@ -19287,16 +19297,24 @@ function closeDesignProblem(store, problemId, fixNote, t) {
19287
19297
  const p = store.getNode(problemId);
19288
19298
  if (!p || p.label !== "Problem") return false;
19289
19299
  if (p.attrs["resolvedAs"]) return false;
19290
- if (fixNote?.trim() && store.outEdges(problemId, ["SOLVED_BY"]).length === 0) {
19291
- const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
19292
- store.mergeNode(
19293
- buildNode(store, solId, "Solution", fixNote.trim(), t, { source: "convo", provisional: false })
19294
- );
19295
- mergeEdge(store, problemId, solId, "SOLVED_BY", t);
19300
+ if (fixNote?.trim()) {
19301
+ const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
19302
+ if (sols.every((s) => isAutoMinted(s))) {
19303
+ const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
19304
+ store.mergeNode(
19305
+ buildNode(store, solId, "Solution", fixNote.trim(), t, { source: "convo", provisional: false })
19306
+ );
19307
+ mergeEdge(store, problemId, solId, "SOLVED_BY", t);
19308
+ }
19296
19309
  }
19297
19310
  if (p.attrs["resolvedAt"]) return false;
19298
19311
  store.updateNode(problemId, {
19299
- attrs: { ...p.attrs, provisional: false, resolvedAt: t },
19312
+ // `resolutionWitnessed` records WHO closed it an agent's explicit fix tag
19313
+ // — so the reopen sweep can tell this close from the heuristic auto-closes
19314
+ // it polices. The solution SET is the wrong proxy: a bare `(fix:[handle])`
19315
+ // mints no Solution, and a legacy placeholder beside it reads as
19316
+ // all-heuristic either way.
19317
+ attrs: { ...p.attrs, provisional: false, resolvedAt: t, resolutionWitnessed: true },
19300
19318
  lastUpdatedAt: t
19301
19319
  });
19302
19320
  return true;
@@ -19307,6 +19325,10 @@ function reopenAutoClosedProblems(store, t) {
19307
19325
  if (p.attrs["resolvedAt"] == null) continue;
19308
19326
  if (p.attrs["resolvedAs"] != null) continue;
19309
19327
  if (p.attrs["mergedInto"] !== void 0) continue;
19328
+ if (p.attrs["resolutionWitnessed"] === true) {
19329
+ report.agentDescribed++;
19330
+ continue;
19331
+ }
19310
19332
  const sols = store.outEdges(p.id, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
19311
19333
  if (sols.length === 0) continue;
19312
19334
  if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
@@ -21838,14 +21860,24 @@ var init_principle_sync = __esm({
21838
21860
 
21839
21861
  // ../../packages/local-graph/src/mechanism-liveness.ts
21840
21862
  function evaluateFed(store, d) {
21863
+ if (d.fedEdges) {
21864
+ const { count: count2, total } = store.recentEdgeAttrCoverage(
21865
+ d.fedEdges.marker,
21866
+ d.fedEdges.attr,
21867
+ d.fedEdges.recent
21868
+ );
21869
+ return { count: count2, total, fraction: total === 0 ? 0 : count2 / total };
21870
+ }
21871
+ if (!d.fed) return { count: 0, total: 0, fraction: 0 };
21841
21872
  const column = COLUMN_INPUTS[d.fed.attr];
21842
- const has = (n) => column ? column(n) : n.attrs[d.fed.attr] !== void 0;
21843
- const nodes = d.fed.labels.flatMap((label) => store.findNodesByLabel(label));
21873
+ const fed = d.fed;
21874
+ const has = (n) => column ? column(n) : n.attrs[fed.attr] !== void 0;
21875
+ const nodes = fed.labels.flatMap((label) => store.findNodesByLabel(label));
21844
21876
  let window2 = nodes;
21845
- if (d.fed.recent !== void 0) {
21846
- window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, d.fed.recent);
21877
+ if (fed.recent !== void 0) {
21878
+ window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, fed.recent);
21847
21879
  }
21848
- if (d.fed.sinceFirstStamp) {
21880
+ if (fed.sinceFirstStamp) {
21849
21881
  let anchor = Infinity;
21850
21882
  for (const n of window2) if (has(n) && n.createdAt < anchor) anchor = n.createdAt;
21851
21883
  window2 = anchor === Infinity ? [] : window2.filter((n) => n.createdAt >= anchor);
@@ -21865,7 +21897,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
21865
21897
  const stalled = backlog !== void 0 && backlog > 0 && effectCount === 0 && (inv?.runs ?? 0) >= STALL_MIN_RUNS && span >= STALL_MIN_SPAN_MS && (backlogWas === void 0 || backlog >= backlogWas);
21866
21898
  let verdict;
21867
21899
  if (!inv || inv.runs === 0) verdict = "never-invoked";
21868
- else if (fed.fraction < d.fed.minFraction) verdict = "starved";
21900
+ else if (fed.fraction < (d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0)) verdict = "starved";
21869
21901
  else if (stalled) verdict = "stalled";
21870
21902
  else if (effectCount === 0) verdict = "no-effect";
21871
21903
  else verdict = "ok";
@@ -21876,7 +21908,7 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
21876
21908
  fedCount: fed.count,
21877
21909
  fedTotal: fed.total,
21878
21910
  fedFraction: fed.fraction,
21879
- minFraction: d.fed.minFraction,
21911
+ minFraction: d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0,
21880
21912
  ...inv ? { lastInvokedMs: inv.lastTs } : {},
21881
21913
  runs: inv?.runs ?? 0,
21882
21914
  effectCount,
@@ -21992,6 +22024,20 @@ var init_mechanism_liveness = __esm({
21992
22024
  fed: { labels: ["File"], attr: "relPath", minFraction: 0.9 },
21993
22025
  effectCounter: "reaped"
21994
22026
  },
22027
+ {
22028
+ id: "prior-corroboration",
22029
+ what: "grounds a cited prior by overlap with what the session actually touched",
22030
+ // CAPTURE mints the edges and stamps the verdict; the fed metric is the
22031
+ // corroborated fraction of the newest priorTag edges. Measured 2026-08-08:
22032
+ // 1 of 139 recent despite 84 having a live grounding path — reading STARVED
22033
+ // is the honest state until the touched-set plumbing is fixed
22034
+ // (VS-corroboration-rate); an alarm on a genuinely broken channel is what
22035
+ // alarms are for.
22036
+ pass: "capture",
22037
+ fedEdges: { marker: '"priorTag":true', attr: "corroborated", minFraction: 0.02, recent: 100 },
22038
+ effectCounter: "corroboratedEdges",
22039
+ note: "expected STARVED until VS-corroboration-rate lands \u2014 the channel is genuinely starved"
22040
+ },
21995
22041
  {
21996
22042
  id: "liveness-watch",
21997
22043
  what: "the daemon-side schedule of this very verdict \u2014 alarms when a mechanism stops working",
@@ -51952,13 +51998,13 @@ function mintPriorEdge(store, source, target, sentence, touched, ts) {
51952
51998
  } catch {
51953
51999
  }
51954
52000
  }
51955
- return true;
52001
+ return { corroborated };
51956
52002
  }
51957
52003
  function harvestInlineTags(store, text, opts) {
51958
52004
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
51959
52005
  const mintPriors = opts.mintPriors ?? true;
51960
52006
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
51961
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], causalLinks: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
52007
+ const plan = { priorEdges: 0, corroboratedEdges: 0, problems: [], fixes: [], triages: [], causalLinks: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
51962
52008
  const tags = parseInlineTags(text);
51963
52009
  const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
51964
52010
  const bindSymptom = (seq, threadId) => {
@@ -52151,11 +52197,23 @@ function harvestInlineTags(store, text, opts) {
52151
52197
  plan.corroborations.push({ nodeId: targetId, witnessKey });
52152
52198
  }
52153
52199
  }
52154
- if (mintPriors && source && target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
52200
+ if (mintPriors && source && target) {
52201
+ const m = mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts);
52202
+ if (m) {
52203
+ plan.priorEdges++;
52204
+ if (m.corroborated) plan.corroboratedEdges++;
52205
+ }
52206
+ }
52155
52207
  } else if (mintPriors && source) {
52156
52208
  const targetId = resolveHandle(store, tag.handle, opts.handleMap);
52157
52209
  const target = targetId ? store.getNode(targetId) : null;
52158
- if (target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
52210
+ if (target) {
52211
+ const m = mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts);
52212
+ if (m) {
52213
+ plan.priorEdges++;
52214
+ if (m.corroborated) plan.corroboratedEdges++;
52215
+ }
52216
+ }
52159
52217
  }
52160
52218
  }
52161
52219
  return plan;
@@ -54316,7 +54374,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
54316
54374
  }
54317
54375
 
54318
54376
  // src/engine.ts
54319
- var DAEMON_VERSION = true ? "2.0.2-dev.540" : "2.0.0-alpha.0";
54377
+ var DAEMON_VERSION = true ? "2.0.2-dev.548" : "2.0.0-alpha.0";
54320
54378
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
54321
54379
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
54322
54380
  var GIT_OP_MUTE_MS = 4e3;
@@ -54989,6 +55047,9 @@ function createWorkspaceEngine(opts) {
54989
55047
  let abstracted = 0;
54990
55048
  let triaged = 0;
54991
55049
  let priorEdges = 0;
55050
+ let corroboratedEdges = 0;
55051
+ let touchedFileTurns = 0;
55052
+ let touchedToolTurns = 0;
54992
55053
  let linked = 0;
54993
55054
  const t = Date.now();
54994
55055
  let processedTurns = 0;
@@ -55108,6 +55169,9 @@ function createWorkspaceEngine(opts) {
55108
55169
  ...touched.size > 0 ? { sessionTouchedIds: touched } : {}
55109
55170
  });
55110
55171
  priorEdges += plan.priorEdges;
55172
+ corroboratedEdges += plan.corroboratedEdges;
55173
+ if (touchedFileId) touchedFileTurns++;
55174
+ if ((toolRuns.get(sessionId)?.size ?? 0) > 0) touchedToolTurns++;
55111
55175
  const wf = workingFiles.get(sessionId);
55112
55176
  let inScopeProblemId;
55113
55177
  const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
@@ -55172,7 +55236,16 @@ function createWorkspaceEngine(opts) {
55172
55236
  }
55173
55237
  continue;
55174
55238
  }
55175
- if (resolveDesignProblemById(store, pid, fx.fixNote, t)) {
55239
+ const closed = resolveDesignProblemById(store, pid, fx.fixNote, t);
55240
+ if (!closed) {
55241
+ const n = store.getNode(pid);
55242
+ if (!n || n.label !== "Problem" || n.attrs["resolvedAs"] != null) {
55243
+ console.warn(
55244
+ `[errata] fix tag went nowhere: ${!n ? `id ${pid.slice(0, 32)} unknown` : n.label !== "Problem" ? `${pid.slice(0, 32)} is a ${n.label}, not a Problem` : "the problem was retracted \u2014 a retraction is not fixable"} \u2014 the fix was NOT recorded`
55245
+ );
55246
+ }
55247
+ }
55248
+ if (closed) {
55176
55249
  resolved++;
55177
55250
  const fxAnchor = editedTurnFile ?? turnFile ?? wf;
55178
55251
  if (fxAnchor) {
@@ -55508,6 +55581,9 @@ function createWorkspaceEngine(opts) {
55508
55581
  resolved,
55509
55582
  triaged,
55510
55583
  priorEdges,
55584
+ corroboratedEdges,
55585
+ touchedFileTurns,
55586
+ touchedToolTurns,
55511
55587
  seqAdvanced: store.currentIngestSeq() - seqAtStart
55512
55588
  });
55513
55589
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.540",
3
+ "version": "2.0.2-dev.548",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -24921,6 +24921,16 @@ var SqliteGraphStore = class {
24921
24921
  if (revived > 0) this.mutations++;
24922
24922
  return revived;
24923
24923
  }
24924
+ recentEdgeAttrCoverage(marker, attr, limit) {
24925
+ const r = this.db.prepare(
24926
+ `SELECT COUNT(*) AS total,
24927
+ COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
24928
+ FROM (SELECT attrs_json FROM edges
24929
+ WHERE valid_to IS NULL AND attrs_json LIKE ?
24930
+ ORDER BY created_at DESC LIMIT ?)`
24931
+ ).get(attr, `%${marker}%`, limit);
24932
+ return { count: Number(r.count), total: Number(r.total) };
24933
+ }
24924
24934
  liveNodeIds(ids) {
24925
24935
  const live = /* @__PURE__ */ new Set();
24926
24936
  const CHUNK = 900;
@@ -25627,6 +25637,10 @@ function reopenAutoClosedProblems(store2, t) {
25627
25637
  if (p.attrs["resolvedAt"] == null) continue;
25628
25638
  if (p.attrs["resolvedAs"] != null) continue;
25629
25639
  if (p.attrs["mergedInto"] !== void 0) continue;
25640
+ if (p.attrs["resolutionWitnessed"] === true) {
25641
+ report.agentDescribed++;
25642
+ continue;
25643
+ }
25630
25644
  const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
25631
25645
  if (sols.length === 0) continue;
25632
25646
  if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {