@inerrata-corporation/errata 2.0.2-dev.276 → 2.0.2-dev.288

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 (2) hide show
  1. package/errata.mjs +370 -109
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -20500,6 +20500,56 @@ function induceTriage(l2, opts) {
20500
20500
  }
20501
20501
  return report;
20502
20502
  }
20503
+ function pendingDiscriminators(l2, opts = {}) {
20504
+ const minSources = opts.minSources ?? 1;
20505
+ const minObservations = opts.minObservations ?? 2;
20506
+ const out2 = [];
20507
+ for (const triage of l2.findNodesByLabel("Triage")) {
20508
+ for (const e of l2.outEdges(triage.id, [...ROUTE_EDGE_TYPES])) {
20509
+ if (isRouterRoute(e)) continue;
20510
+ if (typeof e.attrs["discriminator"] === "string") continue;
20511
+ if (opts.onlyUnsurfaced && e.attrs["discriminatorAsked"] === true) continue;
20512
+ const perContext = e.attrs["perContext"] ?? {};
20513
+ const global2 = perContext[TRIAGE_GLOBAL_BUCKET] ?? {};
20514
+ const observations = Number(global2.confirmed ?? 0);
20515
+ const sources = new Set(global2.contributors ?? []).size;
20516
+ if (sources < minSources || observations < minObservations) continue;
20517
+ out2.push({ route: e.id, presenting: triage.description, cause: "", sources, observations, causeId: e.to });
20518
+ }
20519
+ }
20520
+ out2.sort((a, b) => b.sources - a.sources || b.observations - a.observations);
20521
+ const top = typeof opts.limit === "number" ? out2.slice(0, opts.limit) : out2;
20522
+ for (const p of top) p.cause = l2.getNode(p.causeId)?.description ?? "(missing cause)";
20523
+ return top;
20524
+ }
20525
+ function markDiscriminatorAsked(l2, routeIds, ts) {
20526
+ for (const id of routeIds) {
20527
+ const e = l2.getEdge(id);
20528
+ if (!e) continue;
20529
+ l2.updateEdge(id, { attrs: { ...e.attrs, discriminatorAsked: true }, lastSeenAt: ts });
20530
+ }
20531
+ }
20532
+ function promoteRouteWithDiscriminator(l2, routeId, discriminator, ts, source) {
20533
+ const test = discriminator.trim();
20534
+ if (!test) return false;
20535
+ const existing = l2.getEdge(routeId);
20536
+ if (!existing || !ROUTE_EDGE_TYPES.includes(existing.type)) return false;
20537
+ const attrs = {
20538
+ ...existing.attrs,
20539
+ discriminator: test,
20540
+ provisional: false,
20541
+ ...source ? { discriminatorSource: source } : {}
20542
+ };
20543
+ delete attrs["discriminatorAsked"];
20544
+ const wantType = routeTypeFor(false);
20545
+ if (existing.type === wantType) {
20546
+ l2.updateEdge(routeId, { attrs, lastSeenAt: ts });
20547
+ return true;
20548
+ }
20549
+ l2.deleteEdge(routeId);
20550
+ l2.mergeEdge({ ...existing, type: wantType, attrs, lastSeenAt: ts });
20551
+ return true;
20552
+ }
20503
20553
  function revisitStaleRoutes(l2, ts, opts = {}) {
20504
20554
  const minNavFailures = opts.minNavFailures ?? 3;
20505
20555
  const report = { flagged: 0, demoted: 0 };
@@ -20560,15 +20610,20 @@ function parseTriageFences(text) {
20560
20610
  while ((m = block.exec(text)) !== null) {
20561
20611
  const fields = {};
20562
20612
  for (const line of m[1].split(/\r?\n/)) {
20563
- const kv = /^\s*(presenting|cause|test|fix|drift)\s*:\s*(.+?)\s*$/i.exec(line);
20613
+ const kv = /^\s*(presenting|cause|test|fix|drift|route)\s*:\s*(.+?)\s*$/i.exec(line);
20564
20614
  if (kv) fields[kv[1].toLowerCase()] = kv[2].trim();
20565
20615
  }
20616
+ if (fields["route"] && fields["test"]) {
20617
+ out2.push({ presenting: "", cause: "", route: fields["route"], test: fields["test"] });
20618
+ continue;
20619
+ }
20566
20620
  if (!fields["presenting"] || !fields["cause"]) continue;
20567
20621
  const drift = fields["drift"]?.toLowerCase();
20568
20622
  out2.push({
20569
20623
  presenting: fields["presenting"],
20570
20624
  cause: fields["cause"],
20571
20625
  ...fields["test"] ? { test: fields["test"] } : {},
20626
+ ...fields["route"] ? { route: fields["route"] } : {},
20572
20627
  ...fields["fix"] ? { fix: fields["fix"] } : {},
20573
20628
  ...drift && DRIFT_VALUES.has(drift) ? { drift } : {}
20574
20629
  });
@@ -20577,7 +20632,12 @@ function parseTriageFences(text) {
20577
20632
  }
20578
20633
  function harvestTriageFences(l2, text, opts) {
20579
20634
  let recorded = 0;
20635
+ let promoted = 0;
20580
20636
  for (const f of parseTriageFences(text)) {
20637
+ if (f.route && f.test) {
20638
+ if (promoteRouteWithDiscriminator(l2, f.route.trim(), f.test, opts.ts, opts.contributor)) promoted++;
20639
+ continue;
20640
+ }
20581
20641
  const cause = f.cause.trim();
20582
20642
  const causeId = `dcause_${digest({ statement: cause })}`.slice(0, 56);
20583
20643
  recordTriageObservation(
@@ -20608,7 +20668,7 @@ function harvestTriageFences(l2, text, opts) {
20608
20668
  }
20609
20669
  recorded++;
20610
20670
  }
20611
- return { recorded };
20671
+ return { recorded, promoted };
20612
20672
  }
20613
20673
  var COOCCUR_BONUS, routeEdgeId, ROUTE_EDGE_TYPES, routeTypeFor, isRouterRoute, INDUCTED_MARK, DRIFT_VALUES;
20614
20674
  var init_triage2 = __esm({
@@ -20981,6 +21041,7 @@ __export(src_exports2, {
20981
21041
  linkProblemToSymbols: () => linkProblemToSymbols,
20982
21042
  listNeedsRevisit: () => listNeedsRevisit,
20983
21043
  localEdgeViolation: () => localEdgeViolation,
21044
+ markDiscriminatorAsked: () => markDiscriminatorAsked,
20984
21045
  markRevisit: () => markRevisit,
20985
21046
  matchLanguagesInText: () => matchLanguagesInText,
20986
21047
  matchPackagesInText: () => matchPackagesInText,
@@ -20997,8 +21058,10 @@ __export(src_exports2, {
20997
21058
  parseSemver: () => parseSemver,
20998
21059
  parseTriageFences: () => parseTriageFences,
20999
21060
  pendingAbstractions: () => pendingAbstractions,
21061
+ pendingDiscriminators: () => pendingDiscriminators,
21000
21062
  percolate: () => percolate,
21001
21063
  priorsForFile: () => priorsForFile,
21064
+ promoteRouteWithDiscriminator: () => promoteRouteWithDiscriminator,
21002
21065
  propagateFactChange: () => propagateFactChange,
21003
21066
  propagateVersionChange: () => propagateVersionChange,
21004
21067
  rankToolsForHandles: () => rankToolsForHandles,
@@ -21411,15 +21474,24 @@ function dropLowestUnit(s) {
21411
21474
  case "skills":
21412
21475
  if (s.skills.length) return s.skills.pop(), true;
21413
21476
  break;
21477
+ case "motifsOverFloor":
21478
+ if (s.motifs.length > MOTIF_FLOOR) return s.motifs.pop(), true;
21479
+ break;
21414
21480
  case "motifs":
21415
21481
  if (s.motifs.length) return s.motifs.pop(), true;
21416
21482
  break;
21417
21483
  case "workingFile":
21418
21484
  if (s.workingFile) return delete s.workingFile, true;
21419
21485
  break;
21486
+ case "remoteOverFloor":
21487
+ if (s.remote && s.remote.length > REMOTE_FLOOR) return s.remote.pop(), true;
21488
+ break;
21420
21489
  case "remote":
21421
21490
  if (s.remote && s.remote.length) return s.remote.pop(), true;
21422
21491
  break;
21492
+ case "recentProblemsOverFloor":
21493
+ if (s.recentProblems.length > PROBLEM_FLOOR) return s.recentProblems.pop(), true;
21494
+ break;
21423
21495
  case "recentResolved":
21424
21496
  if (s.recentResolved.length) return s.recentResolved.pop(), true;
21425
21497
  break;
@@ -21478,7 +21550,7 @@ _${dropped} lower-priority item${dropped === 1 ? "" : "s"} omitted to fit the pa
21478
21550
  }
21479
21551
  return { body: body2, snapshot, dropped };
21480
21552
  }
21481
- var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, DEFAULT_AGENT_CONTEXT_BUDGET;
21553
+ var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, MOTIF_FLOOR, REMOTE_FLOOR, PROBLEM_FLOOR, DEFAULT_AGENT_CONTEXT_BUDGET;
21482
21554
  var init_render = __esm({
21483
21555
  "../../packages/context-writer/src/render.ts"() {
21484
21556
  "use strict";
@@ -21493,9 +21565,19 @@ ${RECALL_FIRST_BODY}`;
21493
21565
  SEARCH_IMPERATIVE_BODY = "Everything below is a budgeted, top-of-head slice of a much larger graph. Any prior id below is a live burst seed: `errata.burst` it (or read `.errata/g/burst/<id>`) to pull its wider neighborhood \u2014 causes, fixes, siblings. When a prior is adjacent-but-not-quite, or none fit, that gap is exactly when to search deeper before solving cold: a stuck search is itself a signal that routes help to you.";
21494
21566
  EVICTION_ORDER = [
21495
21567
  "skills",
21496
- "motifs",
21568
+ // Collective floors (EE-evidence-live): motifs/remote trim to a FLOOR here and
21569
+ // fully drain only near the very end. The unfloored order zeroed them on every
21570
+ // render (~44 units dropped per block, live 8-01), which starved §5.4 at the
21571
+ // source: every witness channel (corroborate/refute) fires only on a prior the
21572
+ // agent was SHOWN, the self-gate correctly refuses same-session cites, so a
21573
+ // surface showing ONLY the session's own problems produces zero evidence by
21574
+ // construction — corroborationCount was 0 across all 1,785 cloud nodes while
21575
+ // the transport, parser and gate all worked. Same shape as the needsRevisit
21576
+ // inversion below: the budget optimized one objective and silently killed
21577
+ // another channel's entire input.
21578
+ "motifsOverFloor",
21497
21579
  "workingFile",
21498
- "remote",
21580
+ "remoteOverFloor",
21499
21581
  // Resolved priors are jumping-off points, not live defects — under a tight
21500
21582
  // budget they drop before anything open/actionable.
21501
21583
  "recentResolved",
@@ -21512,8 +21594,17 @@ ${RECALL_FIRST_BODY}`;
21512
21594
  // about something ALREADY closed; a live prior is the substrate every link verb
21513
21595
  // needs. Problems outrank it now.
21514
21596
  "needsRevisit",
21597
+ // Problems trim to a floor before the collective floors give way: a starved
21598
+ // budget keeps a few of BOTH (own problems to act on, cross-session priors to
21599
+ // witness against) rather than all of one and none of the other.
21600
+ "recentProblemsOverFloor",
21601
+ "motifs",
21602
+ "remote",
21515
21603
  "recentProblems"
21516
21604
  ];
21605
+ MOTIF_FLOOR = 2;
21606
+ REMOTE_FLOOR = 3;
21607
+ PROBLEM_FLOOR = 4;
21517
21608
  DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
21518
21609
  }
21519
21610
  });
@@ -36969,6 +37060,36 @@ function takeEnrichmentNudge(store) {
36969
37060
  return `\u26A1 errata \u2014 ${pulls.length} recent fix(es) await a one-line root cause (optional, answer in-flow):
36970
37061
  ` + items;
36971
37062
  }
37063
+ function takeDiscriminatorNudge(path2) {
37064
+ if (!path2 || !existsSync10(path2)) return null;
37065
+ let shared;
37066
+ try {
37067
+ shared = openGraphStore({ path: path2 });
37068
+ } catch {
37069
+ return null;
37070
+ }
37071
+ try {
37072
+ const pending = pendingDiscriminators(shared, { limit: 1, onlyUnsurfaced: true });
37073
+ const top = pending[0];
37074
+ if (!top) return null;
37075
+ markDiscriminatorAsked(shared, [top.route], Date.now());
37076
+ return [
37077
+ `\u26A1 errata \u2014 a known wrong-door is one field from ranking: "${top.presenting.slice(0, 90)}"`,
37078
+ ` usually turns out to be: ${top.cause.slice(0, 90)} (seen ${top.observations}x)`,
37079
+ " Know a CHEAP check that confirms it? Answer in-flow \u2014 it promotes the route so the",
37080
+ " next agent gets it first. Only if you actually know one: a guessed test ranks first",
37081
+ " and misroutes everyone after you.",
37082
+ " ```errata-triage",
37083
+ ` route: ${top.route}`,
37084
+ " test: <the cheap check>",
37085
+ " ```"
37086
+ ].join("\n");
37087
+ } catch {
37088
+ return null;
37089
+ } finally {
37090
+ shared.close();
37091
+ }
37092
+ }
36972
37093
  function commentAnchorId(store, commentId) {
36973
37094
  const out2 = store.outEdges(commentId, ["EXPLAINS", "ANNOTATES"]);
36974
37095
  if (out2.length > 0) return out2[0].to;
@@ -37176,6 +37297,7 @@ function createMcpHandler(store, ctx = {}) {
37176
37297
  let auditReadRun = 0;
37177
37298
  let toolCallIndex = 0;
37178
37299
  let lastAuditNudgeAt = Number.NEGATIVE_INFINITY;
37300
+ let lastDiscriminatorNudgeAt = Number.NEGATIVE_INFINITY;
37179
37301
  return async function handle2(req) {
37180
37302
  const id = req.id ?? null;
37181
37303
  const isNotification = req.id === void 0;
@@ -37222,6 +37344,7 @@ function createMcpHandler(store, ctx = {}) {
37222
37344
  { type: "text", text: JSON.stringify(out2, null, 2) }
37223
37345
  ];
37224
37346
  const isEnrichmentTool = params.name === "errata.pending_enrichment" || params.name === "errata.annotate_resolution";
37347
+ const isTriageTool = params.name === "errata.pending_discriminators" || params.name === "errata.triage";
37225
37348
  toolCallIndex++;
37226
37349
  auditReadRun = isEnrichmentTool ? 0 : auditReadRun + 1;
37227
37350
  if (!isEnrichmentTool) {
@@ -37233,6 +37356,13 @@ function createMcpHandler(store, ctx = {}) {
37233
37356
  lastAuditNudgeAt = toolCallIndex;
37234
37357
  auditReadRun = 0;
37235
37358
  }
37359
+ if (!isTriageTool && content.length === 1 && toolCallIndex - lastDiscriminatorNudgeAt >= DISCRIMINATOR_NUDGE_COOLDOWN) {
37360
+ const disc = takeDiscriminatorNudge(ctx.sharedDbPath);
37361
+ if (disc) {
37362
+ content.push({ type: "text", text: disc });
37363
+ lastDiscriminatorNudgeAt = toolCallIndex;
37364
+ }
37365
+ }
37236
37366
  return { jsonrpc: "2.0", id, result: { content } };
37237
37367
  }
37238
37368
  default:
@@ -37282,7 +37412,11 @@ async function runMcpServer(workspaceRoot) {
37282
37412
  }
37283
37413
  return summariesMemo.map;
37284
37414
  };
37285
- const handle2 = createMcpHandler(store, { ...buildToolContext(), symbolSummaries });
37415
+ const handle2 = createMcpHandler(store, {
37416
+ ...buildToolContext(),
37417
+ symbolSummaries,
37418
+ sharedDbPath: sharedStorePath()
37419
+ });
37286
37420
  process.stdin.setEncoding("utf8");
37287
37421
  let buffer = "";
37288
37422
  process.stdin.on("data", (chunk) => {
@@ -37310,7 +37444,7 @@ async function runMcpServer(workspaceRoot) {
37310
37444
  if (resp) process.stdout.write(JSON.stringify(resp) + "\n");
37311
37445
  }
37312
37446
  }
37313
- var INSTRUCTIONS, AUDIT_RUN_THRESHOLD, AUDIT_NUDGE_COOLDOWN, AUDIT_FLAG_NUDGE, TOOLS;
37447
+ var INSTRUCTIONS, AUDIT_RUN_THRESHOLD, AUDIT_NUDGE_COOLDOWN, DISCRIMINATOR_NUDGE_COOLDOWN, AUDIT_FLAG_NUDGE, TOOLS;
37314
37448
  var init_mcp = __esm({
37315
37449
  "src/mcp.ts"() {
37316
37450
  "use strict";
@@ -37371,6 +37505,7 @@ var init_mcp = __esm({
37371
37505
  ].join("\n");
37372
37506
  AUDIT_RUN_THRESHOLD = 5;
37373
37507
  AUDIT_NUDGE_COOLDOWN = 12;
37508
+ DISCRIMINATOR_NUDGE_COOLDOWN = 40;
37374
37509
  AUDIT_FLAG_NUDGE = "\u26A1 errata \u2014 reading/auditing? anything you notice that's off, flag it inline as `[!one line]` (`[?\u2026]` = TODO) \u2014 no tool call, we harvest it.";
37375
37510
  TOOLS = [
37376
37511
  {
@@ -38396,6 +38531,47 @@ var init_mcp = __esm({
38396
38531
  }
38397
38532
  }
38398
38533
  },
38534
+ {
38535
+ name: "errata.pending_discriminators",
38536
+ description: "Routes awaiting the ONE field that promotes them to routers (PLAN_TRIAGE). A route is a known wrong-door \u2014 symptom S usually turns out to be cause C \u2014 but it only ranks as a ROUTER once someone supplies its `discriminator`: the cheap ante-hoc test that confirms C before you commit. Returns provisional routes corroborated by 2+ DISTINCT CONTRIBUTORS, most-corroborated first; these are one field away from ranking every future `errata.triage` on that symptom. Answer only where you actually know a cheap check \u2014 a guessed test is worse than none, because it ranks first and sends the next agent down it. Empty = nothing awaiting. Reads the machine-wide shared store.",
38537
+ inputSchema: {
38538
+ type: "object",
38539
+ properties: {
38540
+ limit: { type: "number", description: "Max routes to return (default 10)." },
38541
+ minSources: { type: "number", description: "Min distinct contributors behind the route (default 2)." }
38542
+ }
38543
+ },
38544
+ handler: (args2) => {
38545
+ const path2 = sharedStorePath();
38546
+ if (!existsSync10(path2)) return { count: 0, pending: [] };
38547
+ const shared = openGraphStore({ path: path2 });
38548
+ try {
38549
+ const pending = pendingDiscriminators(shared, {
38550
+ limit: typeof args2?.limit === "number" ? args2.limit : 10,
38551
+ ...typeof args2?.minSources === "number" ? { minSources: args2.minSources } : {}
38552
+ });
38553
+ return {
38554
+ count: pending.length,
38555
+ pending,
38556
+ ...pending.length > 0 ? {
38557
+ howToAnswer: [
38558
+ "For each route you know a cheap test for, emit an errata-triage fence",
38559
+ "carrying its `route` id and the `test` \u2014 no presenting/cause needed, the",
38560
+ "route id identifies both endpoints. Attaching a test does NOT count as",
38561
+ "another sighting, so it cannot inflate the route's evidence.",
38562
+ "",
38563
+ "```errata-triage",
38564
+ "route: <route id from above>",
38565
+ "test: <the cheap check that confirms this cause>",
38566
+ "```"
38567
+ ].join("\n")
38568
+ } : {}
38569
+ };
38570
+ } finally {
38571
+ shared.close();
38572
+ }
38573
+ }
38574
+ },
38399
38575
  {
38400
38576
  name: "errata.triage",
38401
38577
  description: "Before you chase a problem, TRIAGE it \u2014 is it a known wrong-door? (PLAN_TRIAGE). Pass the problem you're about to work on (`problem`: a one-line statement, or `id`: a dprob_\u2026 id) and YOUR context (`stack`, `domain`) so the probabilities are conditioned on it \u2014 a symptom routes to different causes on different stacks. Returns the differential: candidate causes ranked router-first then by context-conditioned probability \u2014 {route, cause:{description}, probability, sampleSize, trust, router, discriminator, solutions:[\u2026]}. `probability` is P(cause | symptom, your context); `trust`/`sampleSize` tell you how much evidence stands behind it. Run the top router's `discriminator` (a cheap check) before committing. Empty = not a known wrong-door. Reads the machine-wide shared store.",
@@ -46989,6 +47165,69 @@ var init_webui = __esm({
46989
47165
  }
46990
47166
  });
46991
47167
 
47168
+ // src/witness-ledger.ts
47169
+ var witness_ledger_exports = {};
47170
+ __export(witness_ledger_exports, {
47171
+ appendWitnessLedger: () => appendWitnessLedger,
47172
+ readWitnessLedger: () => readWitnessLedger,
47173
+ summarizeWitnessLedger: () => summarizeWitnessLedger,
47174
+ witnessLedgerPath: () => witnessLedgerPath
47175
+ });
47176
+ import { appendFileSync as appendFileSync2, existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "node:fs";
47177
+ import { join as join25 } from "node:path";
47178
+ function witnessLedgerPath(configDir) {
47179
+ return join25(configDir, "witness-ledger.jsonl");
47180
+ }
47181
+ function appendWitnessLedger(configDir, entry) {
47182
+ const path2 = witnessLedgerPath(configDir);
47183
+ try {
47184
+ appendFileSync2(path2, JSON.stringify(entry) + "\n");
47185
+ const lines = readFileSync20(path2, "utf8").split("\n").filter(Boolean);
47186
+ if (lines.length > LEDGER_MAX_LINES) {
47187
+ writeFileSync17(path2, lines.slice(-Math.floor(LEDGER_MAX_LINES / 2)).join("\n") + "\n");
47188
+ }
47189
+ } catch {
47190
+ }
47191
+ }
47192
+ function readWitnessLedger(configDir) {
47193
+ const path2 = witnessLedgerPath(configDir);
47194
+ if (!existsSync20(path2)) return [];
47195
+ try {
47196
+ return readFileSync20(path2, "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l)).filter((e) => typeof e.ts === "number" && typeof e.channel === "string");
47197
+ } catch {
47198
+ return [];
47199
+ }
47200
+ }
47201
+ function summarizeWitnessLedger(configDir, nowMs) {
47202
+ const entries = readWitnessLedger(configDir);
47203
+ if (entries.length === 0) return [];
47204
+ const byChannel = /* @__PURE__ */ new Map();
47205
+ for (const e of entries) {
47206
+ const c = byChannel.get(e.channel) ?? { recorded: 0, selfSkipped: 0, selfGated: 0, unmatched: 0, lastTs: 0 };
47207
+ c.recorded += e.recorded ?? 0;
47208
+ c.selfSkipped += e.clientSelfSkipped ?? 0;
47209
+ c.selfGated += e.selfGated ?? 0;
47210
+ c.unmatched += e.unmatched ?? 0;
47211
+ c.lastTs = Math.max(c.lastTs, e.ts);
47212
+ byChannel.set(e.channel, c);
47213
+ }
47214
+ const out2 = [];
47215
+ for (const [channel, c] of byChannel) {
47216
+ const ageH = Math.round((nowMs - c.lastTs) / 36e5);
47217
+ out2.push(
47218
+ `${channel}: recorded ${c.recorded} \xB7 self-gated ${c.selfSkipped + c.selfGated} \xB7 unmatched ${c.unmatched} \xB7 last activity ${ageH}h ago`
47219
+ );
47220
+ }
47221
+ return out2;
47222
+ }
47223
+ var LEDGER_MAX_LINES;
47224
+ var init_witness_ledger = __esm({
47225
+ "src/witness-ledger.ts"() {
47226
+ "use strict";
47227
+ LEDGER_MAX_LINES = 500;
47228
+ }
47229
+ });
47230
+
46992
47231
  // src/report-render.ts
46993
47232
  var report_render_exports = {};
46994
47233
  __export(report_render_exports, {
@@ -47380,12 +47619,12 @@ var init_report_render = __esm({
47380
47619
 
47381
47620
  // src/cli.ts
47382
47621
  init_src5();
47383
- import { closeSync as closeSync2, existsSync as existsSync26, openSync as openSync2, readFileSync as readFileSync25, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47384
- import { join as join29 } from "node:path";
47622
+ import { closeSync as closeSync2, existsSync as existsSync27, openSync as openSync2, readFileSync as readFileSync26, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47623
+ import { join as join30 } from "node:path";
47385
47624
  import { spawn as spawn3 } from "node:child_process";
47386
47625
 
47387
47626
  // src/daemon.ts
47388
- import { existsSync as existsSync21, writeFileSync as writeFileSync18 } from "node:fs";
47627
+ import { existsSync as existsSync22, writeFileSync as writeFileSync19 } from "node:fs";
47389
47628
 
47390
47629
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
47391
47630
  import { createServer as createServerHTTP } from "http";
@@ -47965,8 +48204,8 @@ init_config();
47965
48204
 
47966
48205
  // src/engine.ts
47967
48206
  import { execFileSync as execFileSync3 } from "node:child_process";
47968
- import { existsSync as existsSync20, statSync as statSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync9, renameSync as renameSync3, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "node:fs";
47969
- import { join as join25, relative as relative6, sep as sep4 } from "node:path";
48207
+ import { existsSync as existsSync21, statSync as statSync5, appendFileSync as appendFileSync3, readdirSync as readdirSync9, renameSync as renameSync3, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "node:fs";
48208
+ import { join as join26, relative as relative6, sep as sep4 } from "node:path";
47970
48209
 
47971
48210
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
47972
48211
  import { stat as statcb } from "fs";
@@ -52412,6 +52651,9 @@ function retireWitnesses(queue, channel, settledKeys) {
52412
52651
  return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
52413
52652
  }
52414
52653
 
52654
+ // src/engine.ts
52655
+ init_witness_ledger();
52656
+
52415
52657
  // src/causal.ts
52416
52658
  var SUPPRESSORS = [
52417
52659
  { kind: "ts-nocheck", re: /@ts-nocheck\b/g },
@@ -52664,7 +52906,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52664
52906
  }
52665
52907
 
52666
52908
  // src/engine.ts
52667
- var DAEMON_VERSION = true ? "2.0.2-dev.276" : "2.0.0-alpha.0";
52909
+ var DAEMON_VERSION = true ? "2.0.2-dev.288" : "2.0.0-alpha.0";
52668
52910
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52669
52911
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52670
52912
  var GIT_OP_MUTE_MS = 4e3;
@@ -52674,17 +52916,17 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
52674
52916
  function appendIdentityAudit(path2, record2, line) {
52675
52917
  if (!record2.accepted && record2.score <= 0) return;
52676
52918
  try {
52677
- if (existsSync20(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52919
+ if (existsSync21(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52678
52920
  renameSync3(path2, `${path2}.1`);
52679
52921
  }
52680
- appendFileSync2(path2, line);
52922
+ appendFileSync3(path2, line);
52681
52923
  } catch {
52682
52924
  }
52683
52925
  }
52684
52926
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
52685
52927
  function loadTurnCursors(path2) {
52686
52928
  try {
52687
- const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52929
+ const raw2 = JSON.parse(readFileSync21(path2, "utf8"));
52688
52930
  return new Map(
52689
52931
  Object.entries(raw2).map(([k, v]) => [k, typeof v === "string" ? v : String(v?.uuid ?? "")])
52690
52932
  );
@@ -52694,7 +52936,7 @@ function loadTurnCursors(path2) {
52694
52936
  }
52695
52937
  function loadTurnOffsets(path2) {
52696
52938
  try {
52697
- const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52939
+ const raw2 = JSON.parse(readFileSync21(path2, "utf8"));
52698
52940
  const out2 = /* @__PURE__ */ new Map();
52699
52941
  for (const [k, v] of Object.entries(raw2)) {
52700
52942
  const off = typeof v === "object" && v !== null ? v.offset : void 0;
@@ -52710,7 +52952,7 @@ function saveTurnCursors(path2, cursors, offsets) {
52710
52952
  const merged = {};
52711
52953
  for (const [k, uuid3] of cursors) merged[k] = { uuid: uuid3, offset: offsets.get(k) ?? 0 };
52712
52954
  for (const [k, offset] of offsets) if (!merged[k]) merged[k] = { uuid: "", offset };
52713
- writeFileSync17(path2, JSON.stringify(merged), "utf8");
52955
+ writeFileSync18(path2, JSON.stringify(merged), "utf8");
52714
52956
  } catch {
52715
52957
  }
52716
52958
  }
@@ -52732,7 +52974,7 @@ function gitSourceWatchTargets(root) {
52732
52974
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
52733
52975
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
52734
52976
  );
52735
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join25(root, d) + sep4));
52977
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join26(root, d) + sep4));
52736
52978
  } catch {
52737
52979
  }
52738
52980
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -52744,19 +52986,19 @@ function gitSourceWatchTargets(root) {
52744
52986
  if (!f.startsWith(prefix)) continue;
52745
52987
  const rest2 = f.slice(prefix.length);
52746
52988
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
52747
- else targets.add(join25(root, f));
52989
+ else targets.add(join26(root, f));
52748
52990
  }
52749
52991
  for (const c of children) {
52750
- if (IGNORED_PATH.test(join25(root, c) + sep4)) continue;
52992
+ if (IGNORED_PATH.test(join26(root, c) + sep4)) continue;
52751
52993
  if (hasIgnoredChild(c)) addUnder(c);
52752
- else targets.add(join25(root, c));
52994
+ else targets.add(join26(root, c));
52753
52995
  }
52754
52996
  };
52755
52997
  addUnder("");
52756
52998
  if (targets.size > 0) return [...targets];
52757
52999
  } catch {
52758
53000
  }
52759
- return readdirSync9(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join25(root, String(e.name)) + sep4)).map((e) => join25(root, String(e.name)));
53001
+ return readdirSync9(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join26(root, String(e.name)) + sep4)).map((e) => join26(root, String(e.name)));
52760
53002
  }
52761
53003
  function createWorkspaceEngine(opts) {
52762
53004
  const paths = workspacePaths(opts.workspaceRoot);
@@ -52914,7 +53156,7 @@ function createWorkspaceEngine(opts) {
52914
53156
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
52915
53157
  let episodeId2;
52916
53158
  if (srcPaths.length > 0) {
52917
- const abs = srcPaths.map((p) => join25(opts.workspaceRoot, p));
53159
+ const abs = srcPaths.map((p) => join26(opts.workspaceRoot, p));
52918
53160
  try {
52919
53161
  const r = await runReindexPass(
52920
53162
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -52950,8 +53192,8 @@ function createWorkspaceEngine(opts) {
52950
53192
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
52951
53193
  );
52952
53194
  };
52953
- const gitDir = join25(opts.workspaceRoot, ".git");
52954
- if (existsSync20(gitDir)) {
53195
+ const gitDir = join26(opts.workspaceRoot, ".git");
53196
+ if (existsSync21(gitDir)) {
52955
53197
  stopGit = startGitSensor(gitDir, (ev) => {
52956
53198
  void handleGitEvent(ev).catch((err2) => {
52957
53199
  console.warn("[errata] git event handler failed:", err2);
@@ -53021,10 +53263,10 @@ function createWorkspaceEngine(opts) {
53021
53263
  });
53022
53264
  doneRender?.();
53023
53265
  writeContextFile(opts.workspaceRoot, body2);
53024
- const target = join25(opts.workspaceRoot, "AGENTS.md");
53266
+ const target = join26(opts.workspaceRoot, "AGENTS.md");
53025
53267
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
53026
53268
  if (elicit) {
53027
- writePrimingHandles(join25(paths.configDir, "priming-handles.json"), [
53269
+ writePrimingHandles(join26(paths.configDir, "priming-handles.json"), [
53028
53270
  ...snapshot.recentProblems.map((r) => r.node),
53029
53271
  // Resolved-band handles: the ✓ problem AND its Solution are citable
53030
53272
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -53054,7 +53296,7 @@ function createWorkspaceEngine(opts) {
53054
53296
  remoteInFlight = true;
53055
53297
  lastRemoteAt = now;
53056
53298
  try {
53057
- const norm = (xs) => (xs ?? []).map((t) => t.trim().toLowerCase().replace(/[@\s].*$/, "")).filter(Boolean);
53299
+ const norm = (xs) => (xs ?? []).map((t) => t.trim().toLowerCase().replace(/(?!^)[@\s].*$/, "")).filter(Boolean);
53058
53300
  const seed = store.findNodesByLabel("Problem").sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 6).map((p) => p.id);
53059
53301
  const q = {
53060
53302
  stack: norm(profile.stack),
@@ -53240,7 +53482,7 @@ function createWorkspaceEngine(opts) {
53240
53482
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
53241
53483
  });
53242
53484
  };
53243
- const turnCursorPath = join25(paths.configDir, "turn-cursors.json");
53485
+ const turnCursorPath = join26(paths.configDir, "turn-cursors.json");
53244
53486
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
53245
53487
  const turnOffset = loadTurnOffsets(turnCursorPath);
53246
53488
  const sessionLastProblem = /* @__PURE__ */ new Map();
@@ -53264,7 +53506,7 @@ function createWorkspaceEngine(opts) {
53264
53506
  const t = Date.now();
53265
53507
  let processedTurns = 0;
53266
53508
  const elicit = isEdgeElicitationEnabled();
53267
- const handleMap = elicit ? readPrimingHandles(join25(paths.configDir, "priming-handles.json")) : {};
53509
+ const handleMap = elicit ? readPrimingHandles(join26(paths.configDir, "priming-handles.json")) : {};
53268
53510
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
53269
53511
  const toRel = (abs) => {
53270
53512
  const p = abs.replace(/\\/g, "/");
@@ -53655,6 +53897,15 @@ function createWorkspaceEngine(opts) {
53655
53897
  console.log(
53656
53898
  `[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "")
53657
53899
  );
53900
+ appendWitnessLedger(paths.configDir, {
53901
+ ts: Date.now(),
53902
+ channel,
53903
+ recorded,
53904
+ unmatched,
53905
+ duplicate,
53906
+ selfGated,
53907
+ parked: pendingFor(witnessQueue, channel).length
53908
+ });
53658
53909
  };
53659
53910
  if (typeof cloud.reportContradictions === "function") {
53660
53911
  await sendWitnesses(
@@ -53671,7 +53922,10 @@ function createWorkspaceEngine(opts) {
53671
53922
  if (typeof cloud.reportCorroborations === "function") {
53672
53923
  const emit = plan.corroborations.filter((c) => !mintedHere(c.nodeId));
53673
53924
  const skipped = plan.corroborations.length - emit.length;
53674
- if (skipped > 0) console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
53925
+ if (skipped > 0) {
53926
+ console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
53927
+ appendWitnessLedger(paths.configDir, { ts: Date.now(), channel: "corroborate", clientSelfSkipped: skipped });
53928
+ }
53675
53929
  await sendWitnesses(
53676
53930
  "corroborate",
53677
53931
  emit,
@@ -53873,7 +54127,7 @@ function createWorkspaceEngine(opts) {
53873
54127
  try {
53874
54128
  const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
53875
54129
  emitAndProjectSkills(opts.workspaceRoot, inputs);
53876
- writePrimingHandles(join25(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
54130
+ writePrimingHandles(join26(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
53877
54131
  } catch (err2) {
53878
54132
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
53879
54133
  }
@@ -54060,7 +54314,7 @@ function createWorkspaceEngine(opts) {
54060
54314
  console.log(
54061
54315
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
54062
54316
  );
54063
- const pending = existsSync20(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
54317
+ const pending = existsSync21(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
54064
54318
  return { uploaded: 0, failed: 0, remaining: pending };
54065
54319
  }
54066
54320
  try {
@@ -54147,7 +54401,7 @@ async function startDaemon(opts) {
54147
54401
  reviewUrl: () => webUiUrl + "/review"
54148
54402
  });
54149
54403
  const writeLockFile = (url2) => {
54150
- writeFileSync18(
54404
+ writeFileSync19(
54151
54405
  engine.paths.daemonLock,
54152
54406
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
54153
54407
  "utf8"
@@ -54190,7 +54444,7 @@ async function startDaemon(opts) {
54190
54444
  );
54191
54445
  await engine.stop();
54192
54446
  try {
54193
- if (existsSync21(engine.paths.daemonLock)) {
54447
+ if (existsSync22(engine.paths.daemonLock)) {
54194
54448
  }
54195
54449
  } catch {
54196
54450
  }
@@ -54207,16 +54461,16 @@ async function listenServer(fetchFn, port) {
54207
54461
 
54208
54462
  // src/registry.ts
54209
54463
  init_paths();
54210
- import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
54211
- import { join as join26 } from "node:path";
54464
+ import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "node:fs";
54465
+ import { join as join27 } from "node:path";
54212
54466
  function registryPath() {
54213
- return process.env["ERRATA_REGISTRY_PATH"] ?? join26(globalDir(), "workspaces.json");
54467
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join27(globalDir(), "workspaces.json");
54214
54468
  }
54215
54469
  function read() {
54216
54470
  const p = registryPath();
54217
- if (!existsSync22(p)) return { version: 1, workspaces: {} };
54471
+ if (!existsSync23(p)) return { version: 1, workspaces: {} };
54218
54472
  try {
54219
- const parsed = JSON.parse(readFileSync21(p, "utf8"));
54473
+ const parsed = JSON.parse(readFileSync22(p, "utf8"));
54220
54474
  return { version: 1, workspaces: parsed.workspaces ?? {} };
54221
54475
  } catch {
54222
54476
  return { version: 1, workspaces: {} };
@@ -54224,7 +54478,7 @@ function read() {
54224
54478
  }
54225
54479
  function write(reg) {
54226
54480
  ensureDir(globalDir());
54227
- writeFileSync19(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54481
+ writeFileSync20(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54228
54482
  }
54229
54483
  function registerWorkspace(profile, root, now = Date.now()) {
54230
54484
  const reg = read();
@@ -54241,7 +54495,7 @@ function pruneMissingWorkspaces() {
54241
54495
  const reg = read();
54242
54496
  const removed = [];
54243
54497
  for (const [id, entry] of Object.entries(reg.workspaces)) {
54244
- if (!existsSync22(entry.path)) {
54498
+ if (!existsSync23(entry.path)) {
54245
54499
  removed.push(entry);
54246
54500
  delete reg.workspaces[id];
54247
54501
  }
@@ -54250,13 +54504,13 @@ function pruneMissingWorkspaces() {
54250
54504
  return removed;
54251
54505
  }
54252
54506
  function workspaceStatus(entry) {
54253
- const missing = !existsSync22(entry.path);
54507
+ const missing = !existsSync23(entry.path);
54254
54508
  const lockPath = workspacePaths(entry.path).daemonLock;
54255
54509
  let running = false;
54256
54510
  let webUiUrl = null;
54257
- if (existsSync22(lockPath)) {
54511
+ if (existsSync23(lockPath)) {
54258
54512
  try {
54259
- const lock = JSON.parse(readFileSync21(lockPath, "utf8"));
54513
+ const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
54260
54514
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
54261
54515
  running = true;
54262
54516
  webUiUrl = lock.webUiUrl;
@@ -54284,7 +54538,7 @@ function pidAlive(pid) {
54284
54538
  // src/multi.ts
54285
54539
  init_dist();
54286
54540
  init_src4();
54287
- import { readFileSync as readFileSync24, unlinkSync as unlinkSync3, writeFileSync as writeFileSync20 } from "node:fs";
54541
+ import { readFileSync as readFileSync25, unlinkSync as unlinkSync3, writeFileSync as writeFileSync21 } from "node:fs";
54288
54542
 
54289
54543
  // src/principle-sync.ts
54290
54544
  init_src4();
@@ -54312,8 +54566,8 @@ init_reconcile();
54312
54566
 
54313
54567
  // src/lockfile-auto.ts
54314
54568
  init_src();
54315
- import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
54316
- import { join as join27 } from "node:path";
54569
+ import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
54570
+ import { join as join28 } from "node:path";
54317
54571
 
54318
54572
  // src/package-index.ts
54319
54573
  init_src();
@@ -54462,11 +54716,11 @@ function runLockfilePass(opts) {
54462
54716
  { file: "package-lock.json", parse: parsePackageLockJson }
54463
54717
  ];
54464
54718
  for (const c of candidates) {
54465
- const p = join27(opts.root, c.file);
54466
- if (!existsSync23(p)) continue;
54719
+ const p = join28(opts.root, c.file);
54720
+ if (!existsSync24(p)) continue;
54467
54721
  let sbom;
54468
54722
  try {
54469
- sbom = c.parse(readFileSync22(p, "utf8"));
54723
+ sbom = c.parse(readFileSync23(p, "utf8"));
54470
54724
  } catch {
54471
54725
  continue;
54472
54726
  }
@@ -54914,7 +55168,7 @@ var ConsolidateWorker = class {
54914
55168
  init_paths();
54915
55169
 
54916
55170
  // src/lock.ts
54917
- import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
55171
+ import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
54918
55172
  function isProcessAlive(pid) {
54919
55173
  if (!pid || pid <= 0) return false;
54920
55174
  try {
@@ -54925,9 +55179,9 @@ function isProcessAlive(pid) {
54925
55179
  }
54926
55180
  }
54927
55181
  function readDaemonLock(lockPath) {
54928
- if (!existsSync24(lockPath)) return null;
55182
+ if (!existsSync25(lockPath)) return null;
54929
55183
  try {
54930
- const lock = JSON.parse(readFileSync23(lockPath, "utf8"));
55184
+ const lock = JSON.parse(readFileSync24(lockPath, "utf8"));
54931
55185
  return typeof lock.pid === "number" ? lock : null;
54932
55186
  } catch {
54933
55187
  return null;
@@ -55206,12 +55460,12 @@ async function reanchorProject(opts) {
55206
55460
  }
55207
55461
 
55208
55462
  // src/adopt.ts
55209
- import { existsSync as existsSync25 } from "node:fs";
55210
- import { dirname as dirname10, join as join28 } from "node:path";
55463
+ import { existsSync as existsSync26 } from "node:fs";
55464
+ import { dirname as dirname10, join as join29 } from "node:path";
55211
55465
  function findGitRoot(absPath) {
55212
55466
  let dir = absPath;
55213
55467
  for (let depth = 0; depth < 64; depth++) {
55214
- if (existsSync25(join28(dir, ".git"))) return dir;
55468
+ if (existsSync26(join29(dir, ".git"))) return dir;
55215
55469
  const parent = dirname10(dir);
55216
55470
  if (parent === dir) return null;
55217
55471
  dir = parent;
@@ -55460,7 +55714,7 @@ async function startMultiDaemon(opts = {}) {
55460
55714
  void ambientLinkAll();
55461
55715
  app.route(`/ws/${rec.id}`, rec.webApp);
55462
55716
  try {
55463
- writeFileSync20(
55717
+ writeFileSync21(
55464
55718
  rec.engine.paths.daemonLock,
55465
55719
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
55466
55720
  "utf8"
@@ -55649,7 +55903,7 @@ async function startMultiDaemon(opts = {}) {
55649
55903
  baseUrl = `http://127.0.0.1:${port}`;
55650
55904
  try {
55651
55905
  ensureDir(globalDir());
55652
- writeFileSync20(
55906
+ writeFileSync21(
55653
55907
  lockPath,
55654
55908
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
55655
55909
  "utf8"
@@ -55658,7 +55912,7 @@ async function startMultiDaemon(opts = {}) {
55658
55912
  }
55659
55913
  for (const r of records) {
55660
55914
  try {
55661
- writeFileSync20(
55915
+ writeFileSync21(
55662
55916
  r.engine.paths.daemonLock,
55663
55917
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
55664
55918
  "utf8"
@@ -56156,7 +56410,7 @@ async function startMultiDaemon(opts = {}) {
56156
56410
  },
56157
56411
  async stop() {
56158
56412
  try {
56159
- const cur = readFileSync24(lockPath, "utf8");
56413
+ const cur = readFileSync25(lockPath, "utf8");
56160
56414
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
56161
56415
  } catch {
56162
56416
  }
@@ -57173,21 +57427,21 @@ async function cmdInit() {
57173
57427
  if (!skipHooks) {
57174
57428
  console.log("");
57175
57429
  console.log("installing harness hooks...");
57176
- const { existsSync: existsSync27 } = await import("node:fs");
57177
- const { join: join30 } = await import("node:path");
57430
+ const { existsSync: existsSync28 } = await import("node:fs");
57431
+ const { join: join31 } = await import("node:path");
57178
57432
  try {
57179
57433
  await installClaudeHooks(port);
57180
57434
  } catch (err2) {
57181
57435
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
57182
57436
  }
57183
- if (existsSync27(join30(ROOT, ".cursor"))) {
57437
+ if (existsSync28(join31(ROOT, ".cursor"))) {
57184
57438
  try {
57185
57439
  await installCursorMcpConfig();
57186
57440
  } catch (err2) {
57187
57441
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
57188
57442
  }
57189
57443
  }
57190
- if (existsSync27(join30(ROOT, ".codex"))) {
57444
+ if (existsSync28(join31(ROOT, ".codex"))) {
57191
57445
  try {
57192
57446
  await installCodexHooks(port);
57193
57447
  } catch (err2) {
@@ -57344,9 +57598,9 @@ async function cmdStatus() {
57344
57598
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
57345
57599
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
57346
57600
  }
57347
- console.log(` graph db: ${existsSync26(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57348
- console.log(` event log: ${existsSync26(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57349
- if (existsSync26(paths.castalia)) {
57601
+ console.log(` graph db: ${existsSync27(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57602
+ console.log(` event log: ${existsSync27(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57603
+ if (existsSync27(paths.castalia)) {
57350
57604
  try {
57351
57605
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57352
57606
  const store = openGraphStore2({ path: paths.castalia });
@@ -57378,6 +57632,13 @@ async function cmdStatus() {
57378
57632
  } catch {
57379
57633
  }
57380
57634
  }
57635
+ try {
57636
+ const { summarizeWitnessLedger: summarizeWitnessLedger2 } = await Promise.resolve().then(() => (init_witness_ledger(), witness_ledger_exports));
57637
+ const lines = summarizeWitnessLedger2(paths.configDir, Date.now());
57638
+ if (lines.length === 0) console.log(" evidence: no witness activity ledgered yet (corroborate/contradict have never fired here)");
57639
+ else for (const l of lines) console.log(` evidence: ${l}`);
57640
+ } catch {
57641
+ }
57381
57642
  const lockPath = globalDaemonLock();
57382
57643
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
57383
57644
  console.log(
@@ -58020,11 +58281,11 @@ function cmdInstallationProfile(args2) {
58020
58281
  }
58021
58282
  async function cmdReview() {
58022
58283
  const paths = workspacePaths(ROOT);
58023
- if (!existsSync26(paths.reviewQueue)) {
58284
+ if (!existsSync27(paths.reviewQueue)) {
58024
58285
  console.log("(review queue empty)");
58025
58286
  return;
58026
58287
  }
58027
- const queue = JSON.parse(readFileSync25(paths.reviewQueue, "utf8"));
58288
+ const queue = JSON.parse(readFileSync26(paths.reviewQueue, "utf8"));
58028
58289
  if (queue.length === 0) {
58029
58290
  console.log("(review queue empty)");
58030
58291
  return;
@@ -58695,7 +58956,7 @@ async function gatherRepo(store, ws) {
58695
58956
  };
58696
58957
  }
58697
58958
  async function gatherReportData(generatedAt) {
58698
- const { existsSync: existsSync27 } = await import("node:fs");
58959
+ const { existsSync: existsSync28 } = await import("node:fs");
58699
58960
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
58700
58961
  const cfg = loadConfig();
58701
58962
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -58703,7 +58964,7 @@ async function gatherReportData(generatedAt) {
58703
58964
  for (const ws of listWorkspaces()) {
58704
58965
  if (ws.missing) continue;
58705
58966
  const dbPath = workspacePaths(ws.path).castalia;
58706
- if (!existsSync27(dbPath)) continue;
58967
+ if (!existsSync28(dbPath)) continue;
58707
58968
  let store = null;
58708
58969
  try {
58709
58970
  store = openGraphStore2({ path: dbPath });
@@ -58734,7 +58995,7 @@ async function gatherReportData(generatedAt) {
58734
58995
  };
58735
58996
  }
58736
58997
  async function cmdReport(args2) {
58737
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync21 } = await import("node:fs");
58998
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync22 } = await import("node:fs");
58738
58999
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
58739
59000
  const includeFutureVerbs = args2.includes("--future-verbs");
58740
59001
  const now = /* @__PURE__ */ new Date();
@@ -58747,8 +59008,8 @@ async function cmdReport(args2) {
58747
59008
  const outDir = workspacePaths(ROOT).configDir;
58748
59009
  mkdirSync8(outDir, { recursive: true });
58749
59010
  const files = renderReport2(data, { includeFutureVerbs });
58750
- for (const f of files) writeFileSync21(join29(outDir, f.name), f.html, "utf8");
58751
- const indexPath = join29(outDir, "report.html");
59011
+ for (const f of files) writeFileSync22(join30(outDir, f.name), f.html, "utf8");
59012
+ const indexPath = join30(outDir, "report.html");
58752
59013
  console.log(`report \u2192 ${indexPath}`);
58753
59014
  console.log(
58754
59015
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -58866,15 +59127,15 @@ function hookRelayCommand(port, path2) {
58866
59127
  return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
58867
59128
  }
58868
59129
  async function installClaudeHooks(port) {
58869
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58870
- const { join: join30 } = await import("node:path");
58871
- const dir = join30(ROOT, ".claude");
58872
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58873
- const file2 = join30(dir, "settings.json");
59130
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59131
+ const { join: join31 } = await import("node:path");
59132
+ const dir = join31(ROOT, ".claude");
59133
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59134
+ const file2 = join31(dir, "settings.json");
58874
59135
  let settings = {};
58875
- if (existsSync27(file2)) {
59136
+ if (existsSync28(file2)) {
58876
59137
  try {
58877
- settings = JSON.parse(readFileSync26(file2, "utf8"));
59138
+ settings = JSON.parse(readFileSync27(file2, "utf8"));
58878
59139
  } catch {
58879
59140
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58880
59141
  process.exit(2);
@@ -58920,10 +59181,10 @@ async function installClaudeHooks(port) {
58920
59181
  dropErrata(list);
58921
59182
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
58922
59183
  }
58923
- writeFileSync21(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
59184
+ writeFileSync22(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58924
59185
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
58925
59186
  await installClaudeMcpConfig();
58926
- const claudeMd = join30(ROOT, "CLAUDE.md");
59187
+ const claudeMd = join31(ROOT, "CLAUDE.md");
58927
59188
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
58928
59189
  if (recall.kind === "collision") {
58929
59190
  console.warn(
@@ -58935,15 +59196,15 @@ async function installClaudeHooks(port) {
58935
59196
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
58936
59197
  }
58937
59198
  async function installClaudeMcpConfig() {
58938
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58939
- const { join: join30, dirname: dirname11 } = await import("node:path");
58940
- const file2 = join30(ROOT, ".mcp.json");
59199
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59200
+ const { join: join31, dirname: dirname11 } = await import("node:path");
59201
+ const file2 = join31(ROOT, ".mcp.json");
58941
59202
  const dir = dirname11(file2);
58942
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59203
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
58943
59204
  let cfg = {};
58944
- if (existsSync27(file2)) {
59205
+ if (existsSync28(file2)) {
58945
59206
  try {
58946
- cfg = JSON.parse(readFileSync26(file2, "utf8"));
59207
+ cfg = JSON.parse(readFileSync27(file2, "utf8"));
58947
59208
  } catch {
58948
59209
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58949
59210
  process.exit(2);
@@ -58951,21 +59212,21 @@ async function installClaudeMcpConfig() {
58951
59212
  }
58952
59213
  cfg.mcpServers ??= {};
58953
59214
  cfg.mcpServers["errata"] = errataMcpInvocation();
58954
- writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59215
+ writeFileSync22(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58955
59216
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
58956
59217
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
58957
59218
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
58958
59219
  }
58959
59220
  async function installCursorMcpConfig() {
58960
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58961
- const { join: join30 } = await import("node:path");
58962
- const dir = join30(ROOT, ".cursor");
58963
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58964
- const file2 = join30(dir, "mcp.json");
59221
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59222
+ const { join: join31 } = await import("node:path");
59223
+ const dir = join31(ROOT, ".cursor");
59224
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59225
+ const file2 = join31(dir, "mcp.json");
58965
59226
  let cfg = {};
58966
- if (existsSync27(file2)) {
59227
+ if (existsSync28(file2)) {
58967
59228
  try {
58968
- cfg = JSON.parse(readFileSync26(file2, "utf8"));
59229
+ cfg = JSON.parse(readFileSync27(file2, "utf8"));
58969
59230
  } catch {
58970
59231
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58971
59232
  process.exit(2);
@@ -58973,7 +59234,7 @@ async function installCursorMcpConfig() {
58973
59234
  }
58974
59235
  cfg.mcpServers ??= {};
58975
59236
  cfg.mcpServers["errata"] = errataMcpInvocation();
58976
- writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59237
+ writeFileSync22(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58977
59238
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
58978
59239
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
58979
59240
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -58981,16 +59242,16 @@ async function installCursorMcpConfig() {
58981
59242
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
58982
59243
  }
58983
59244
  async function installCodexHooks(port) {
58984
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58985
- const { join: join30 } = await import("node:path");
58986
- const dir = join30(ROOT, ".codex");
58987
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58988
- const file2 = join30(dir, "config.toml");
59245
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59246
+ const { join: join31 } = await import("node:path");
59247
+ const dir = join31(ROOT, ".codex");
59248
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59249
+ const file2 = join31(dir, "config.toml");
58989
59250
  const BEGIN = `# >>> errata hooks (errata-managed)`;
58990
59251
  const END = `# <<< errata hooks`;
58991
59252
  let existing = "";
58992
- if (existsSync27(file2)) {
58993
- existing = readFileSync26(file2, "utf8");
59253
+ if (existsSync28(file2)) {
59254
+ existing = readFileSync27(file2, "utf8");
58994
59255
  const beginIdx = existing.indexOf(BEGIN);
58995
59256
  const endIdx = existing.indexOf(END);
58996
59257
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -59019,7 +59280,7 @@ ${END}
59019
59280
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
59020
59281
 
59021
59282
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
59022
- writeFileSync21(file2, final, "utf8");
59283
+ writeFileSync22(file2, final, "utf8");
59023
59284
  console.log(`installed Codex hooks \u2192 ${file2}`);
59024
59285
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
59025
59286
  console.log("");
@@ -59333,7 +59594,7 @@ async function cmdDash(args2) {
59333
59594
  await yieldToLoop2();
59334
59595
  try {
59335
59596
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
59336
- const res = bleedRules(join29(r.root, ".claude", "rules"), items);
59597
+ const res = bleedRules(join30(r.root, ".claude", "rules"), items);
59337
59598
  if (res.written || res.pruned) {
59338
59599
  console.log(
59339
59600
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.276",
3
+ "version": "2.0.2-dev.288",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {