@inerrata-corporation/errata 2.0.2-dev.282 → 2.0.2-dev.295

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 +246 -158
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -21283,13 +21283,10 @@ function renderSnapshot(s) {
21283
21283
  const lines = [];
21284
21284
  lines.push("## Errata context");
21285
21285
  lines.push("");
21286
- lines.push("### \u270D\uFE0F CAPTURE PROTOCOL \u2014 do this as you work, it IS the work");
21286
+ lines.push("### \u270D\uFE0F CAPTURE PROTOCOL \u2014 tags are the record");
21287
21287
  lines.push(
21288
- "errata auto-derives problems/fixes from your edits/commands. But two signals exist ONLY if you write them inline \u2014 and the tag is the ONLY thing recorded: a problem or fix you state in plain prose, a markdown list, or your own numbering is SILENTLY LOST. So the moment you state one, wrap it (no tool call, no fence):"
21288
+ "errata records ONLY inline tags: `[!one line]` for every problem you state, `(fix:[id])` when your edit fixed a shown problem. Untagged prose is silently lost \u2014 tag as you write (no fences), we filter downstream."
21289
21289
  );
21290
- lines.push("- `[!one line]` \u2014 EVERY problem you notice or report (a bug you read past, an audit/review finding, anything you call out)");
21291
- lines.push("- `(fix:[id])` \u2014 your edit fixed a problem errata showed you (ids appear in the priors below / in file priors)");
21292
- lines.push("Tag every one you state \u2014 we filter downstream, so don't self-censor or batch. This is the witness only you can give; untagged, it's lost.");
21293
21290
  lines.push("");
21294
21291
  lines.push(SEARCH_IMPERATIVE_HEADER);
21295
21292
  lines.push(SEARCH_IMPERATIVE_BODY);
@@ -21474,15 +21471,24 @@ function dropLowestUnit(s) {
21474
21471
  case "skills":
21475
21472
  if (s.skills.length) return s.skills.pop(), true;
21476
21473
  break;
21474
+ case "motifsOverFloor":
21475
+ if (s.motifs.length > MOTIF_FLOOR) return s.motifs.pop(), true;
21476
+ break;
21477
21477
  case "motifs":
21478
21478
  if (s.motifs.length) return s.motifs.pop(), true;
21479
21479
  break;
21480
21480
  case "workingFile":
21481
21481
  if (s.workingFile) return delete s.workingFile, true;
21482
21482
  break;
21483
+ case "remoteOverFloor":
21484
+ if (s.remote && s.remote.length > REMOTE_FLOOR) return s.remote.pop(), true;
21485
+ break;
21483
21486
  case "remote":
21484
21487
  if (s.remote && s.remote.length) return s.remote.pop(), true;
21485
21488
  break;
21489
+ case "recentProblemsOverFloor":
21490
+ if (s.recentProblems.length > PROBLEM_FLOOR) return s.recentProblems.pop(), true;
21491
+ break;
21486
21492
  case "recentResolved":
21487
21493
  if (s.recentResolved.length) return s.recentResolved.pop(), true;
21488
21494
  break;
@@ -21541,7 +21547,7 @@ _${dropped} lower-priority item${dropped === 1 ? "" : "s"} omitted to fit the pa
21541
21547
  }
21542
21548
  return { body: body2, snapshot, dropped };
21543
21549
  }
21544
- var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, DEFAULT_AGENT_CONTEXT_BUDGET;
21550
+ 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;
21545
21551
  var init_render = __esm({
21546
21552
  "../../packages/context-writer/src/render.ts"() {
21547
21553
  "use strict";
@@ -21556,9 +21562,19 @@ ${RECALL_FIRST_BODY}`;
21556
21562
  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.";
21557
21563
  EVICTION_ORDER = [
21558
21564
  "skills",
21559
- "motifs",
21565
+ // Collective floors (EE-evidence-live): motifs/remote trim to a FLOOR here and
21566
+ // fully drain only near the very end. The unfloored order zeroed them on every
21567
+ // render (~44 units dropped per block, live 8-01), which starved §5.4 at the
21568
+ // source: every witness channel (corroborate/refute) fires only on a prior the
21569
+ // agent was SHOWN, the self-gate correctly refuses same-session cites, so a
21570
+ // surface showing ONLY the session's own problems produces zero evidence by
21571
+ // construction — corroborationCount was 0 across all 1,785 cloud nodes while
21572
+ // the transport, parser and gate all worked. Same shape as the needsRevisit
21573
+ // inversion below: the budget optimized one objective and silently killed
21574
+ // another channel's entire input.
21575
+ "motifsOverFloor",
21560
21576
  "workingFile",
21561
- "remote",
21577
+ "remoteOverFloor",
21562
21578
  // Resolved priors are jumping-off points, not live defects — under a tight
21563
21579
  // budget they drop before anything open/actionable.
21564
21580
  "recentResolved",
@@ -21575,8 +21591,17 @@ ${RECALL_FIRST_BODY}`;
21575
21591
  // about something ALREADY closed; a live prior is the substrate every link verb
21576
21592
  // needs. Problems outrank it now.
21577
21593
  "needsRevisit",
21594
+ // Problems trim to a floor before the collective floors give way: a starved
21595
+ // budget keeps a few of BOTH (own problems to act on, cross-session priors to
21596
+ // witness against) rather than all of one and none of the other.
21597
+ "recentProblemsOverFloor",
21598
+ "motifs",
21599
+ "remote",
21578
21600
  "recentProblems"
21579
21601
  ];
21602
+ MOTIF_FLOOR = 2;
21603
+ REMOTE_FLOOR = 3;
21604
+ PROBLEM_FLOOR = 4;
21580
21605
  DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
21581
21606
  }
21582
21607
  });
@@ -25496,61 +25521,36 @@ function buildFileRecallInstruction(referent = "\u2026") {
25496
25521
  }
25497
25522
  function triageBullet(ref) {
25498
25523
  return [
25499
- " \u2022 you diagnosed a bug \u2014 write the CAUSAL CHAIN in one flag with `<-` (caused-by).",
25500
- " Don't split it into a second tag you'll skip under load; fold it the way you'd say it:",
25501
- " \xB7 [!the symptom <- the mechanism] \u2014 leftmost is what breaks observably (what a test",
25502
- " or user reports); the step right is what caused it. e.g. [!the runner deadlocks after",
25503
- " a task rejects <- await fn() has no try/finally, so a rejection never decrements running].",
25504
- " \xB7 chain it as deep as you actually diagnosed: [!symptom <- cause <- root cause] \u2014 every",
25505
- " ` <- ` is a link we record, and the RIGHTMOST is the reusable root cause (the node other",
25506
- " agents find). Stop at the deepest mechanism you're sure of; don't guess a link.",
25507
- " When symptom and cause turn up FAR APART \u2014 the cause lands turns later, several problems",
25508
- " in play \u2014 name the thread instead: [!#slug the symptom] once, then (cause:#slug the",
25509
- " mechanism) binds to it however far away (same slug, your choice of word). Explaining a",
25510
- ` problem we SHOWED you? bind by its handle/id: (cause:[${ref}] \u2026) / (cause:#dprob_\u2026 \u2026).`
25524
+ " \u2022 you diagnosed a cause \u2014 fold the chain into the flag with <- (caused-by), deepest",
25525
+ " sure mechanism rightmost: [!symptom <- cause <- root cause]. Symptom and cause far",
25526
+ " apart? open a thread [!#slug symptom] \u2026 later bind (cause:#slug mechanism);",
25527
+ ` explaining a problem we SHOWED you \u2192 (cause:[${ref}] \u2026)`
25511
25528
  ].join("\n");
25512
25529
  }
25513
25530
  function linkBullet(ref) {
25514
25531
  return [
25515
- // Lead-in states the trigger without the old "resembles prior knowledge"
25516
- // conditional (which gated the whole bullet on recognising a prior). Kept to ONE
25517
- // line on purpose: Cycle 13 measured this instruction at 55% of the 9,000-char
25518
- // passive-context budget, and every char here is paid for by evicting a prior the
25519
- // agent could have cited — so unproven framing is a net loss, however good it reads.
25520
- " \u2022 you flagged a problem \u2014 LINK it, ESPECIALLY when the finding feels novel to here:",
25521
- ` \xB7 (instance:[${ref}],[another-prior],\u2026) \u2014 the priors this problem is an instance of.`,
25522
- " Aim for ~3 DIFFERENT relevant priors; one link is weak, three triangulate it.",
25523
- ` \xB7 ${TAG_EXAMPLE.pattern()} \u2014 name the general shape it instantiates (e.g. (pattern: unbounded`,
25524
- " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
25525
- " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
25526
- ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC PACKAGE the problem is about, even when this`,
25527
- " workspace doesn't depend on it (e.g. (package: chokidar), (package: pypi/requests)) \u2014 a",
25528
- " public-registry anchor lets the knowledge cross to the collective; internal names stay private.",
25529
- ` \xB7 ${TAG_EXAMPLE.component()} \u2014 the framework/product-level UNIT it concerns when it's not a`,
25530
- " package or a language (e.g. (component: React Router), (component: V8 Isolate)).",
25531
- ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
25532
- " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
25533
- ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
25534
- ` "already solved by" line): (builds-on:[${ref}]) if you layer on it \xB7 (supersedes:[${ref}])`,
25535
- " if you replace it \xB7 (alternative:[h]) if it's a different approach \u2014 pick one. This is the",
25536
- " only way N solutions on one problem stop being a flat bag."
25532
+ " \u2022 link what you flag:",
25533
+ ` \xB7 (instance:[${ref}],[another],[a-third]) \u2014 priors it instantiates; ~3 DIFFERENT ones triangulate`,
25534
+ " \xB7 (pattern:[handle]) \u2014 cite a shown Pattern",
25535
+ ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC package it's about, dependency or not`,
25536
+ ` \xB7 ${TAG_EXAMPLE.component()} \u2014 the framework/product unit`,
25537
+ ` \xB7 (aids:[${ref}],\u2026) \u2014 other problems your fix might also resolve (hypothesis)`,
25538
+ ` \xB7 your fix vs a shown solution: (builds-on:[${ref}]) \xB7 (supersedes:[${ref}]) \xB7 (alternative:[h]) \u2014 pick one`
25537
25539
  ].join("\n");
25538
25540
  }
25539
25541
  function attemptBullet() {
25540
25542
  return [
25541
- " \u2022 you're attempting an approach to a flagged problem, or an attempt didn't pan out:",
25542
- ` \xB7 ${TAG_EXAMPLE.tried()}`,
25543
- ` \xB7 ${TAG_EXAMPLE.failed()}`,
25544
- " Failed attempts are knowledge \u2014 they rule out a path. Add #slug to bind to a named",
25545
- " thread: (tried:#slug \u2026) / (failed:#slug \u2026)."
25543
+ ` \u2022 attempting an approach / an attempt failed \u2192 ${TAG_EXAMPLE.tried()} \xB7`,
25544
+ ` ${TAG_EXAMPLE.failed()} \u2014 a failed attempt is knowledge;`,
25545
+ " bind to a thread with (tried:#slug \u2026) / (failed:#slug \u2026)"
25546
25546
  ].join("\n");
25547
25547
  }
25548
25548
  function buildAgentInstruction(opts = {}) {
25549
25549
  const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
25550
25550
  const ref = opts.referent ?? "its-handle";
25551
25551
  const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : TAG_EXAMPLE[s](ref);
25552
- const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. This is a capture protocol, not optional notes: what you DON'T tag is silently lost \u2014 the tag is the ONLY thing recorded. Whenever one of these is true, wrap it inline in your prose (no fences, no extra calls). Tag every one you state; we filter downstream, so don't self-censor or batch:`;
25553
- const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 if a tag above appeared only mid-turn, RESTATE it in your final message of the turn; the final message always survives.";
25552
+ const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. Tags are the ONLY signal recorded \u2014 anything you state but don't tag is silently lost. Weave each tag inline in your prose as you state it (no fences or backticks, no extra calls); tag everything, we filter downstream:`;
25553
+ const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 RESTATE every tag in your turn-final message; the final message is what reliably survives.";
25554
25554
  return [
25555
25555
  head2,
25556
25556
  ...signals.map(
@@ -25608,12 +25608,12 @@ var init_agent_signals = __esm({
25608
25608
  aids: (ref) => `(aids:[${ref}])`
25609
25609
  };
25610
25610
  GLOSS = {
25611
- prior: "a prior we showed you helped \u2014 or this work sits in a primed language/package",
25612
- problem: "you hit a real problem \u2014 incidental, or the one you're fixing. About specific code? NAME the file in the tag: [!what's wrong @ src/file.ts:42] \u2014 we anchor to what you name, or to a file you edited this turn, never to one you merely read; a design/topic problem is fine with no @",
25613
- fix: "your edit fixes a problem \u2014 cite the one we showed you, or just say how you fixed one you found yourself: (fix: what you changed)",
25614
- constraint: "you made a design decision because requirements conflict or something's constrained \u2014 tag the tension, ESPECIALLY when your fix makes it 'only look' contradictory (a clean solution still hides a real trap the next agent needs)",
25615
- triage: "you diagnosed a bug \u2014 SPLIT the symptom (what breaks \u2192 [!\u2026]) from the cause (the mechanism you'd change \u2192 (cause:\u2026)); if the line says WHY it breaks, it's a cause, not a problem",
25616
- attempt: "you're attempting an approach, or an attempt didn't pan out \u2014 (tried: \u2026) / (failed: \u2026); a failed attempt rules out a path for the next agent",
25611
+ prior: "a shown prior helped, or you work in a primed language/package",
25612
+ problem: "a problem you notice or report \u2014 any bug, finding, or thing wrong; about specific code? name the file: [!what's wrong @ src/file.ts:42]",
25613
+ fix: "your edit fixed a PROBLEM we showed you; for one you flagged yourself: (fix: what you changed) \u2014 a fix resolves a stated problem, completing routine work is not a fix",
25614
+ constraint: "a design decision forced by conflicting requirements or constraints \u2014 tag the tension, especially when your fix makes it 'only look' resolved",
25615
+ triage: "you diagnosed a cause \u2014 fold the chain into the flag with <- (caused-by), deepest sure mechanism rightmost",
25616
+ attempt: "attempting an approach / an attempt failed \u2014 a failed attempt is knowledge, it rules out a path",
25617
25617
  // DOMAIN — promoted to a FIRST-CLASS signal (was nested inside linkBullet, whose
25618
25618
  // lead-in gates on "a problem you flagged resembles prior knowledge"). Naming the
25619
25619
  // area a problem is about is unconditional and has nothing to do with recognizing
@@ -25629,7 +25629,10 @@ var init_agent_signals = __esm({
25629
25629
  // contribution, so stating the general area reads as vague restatement — exactly
25630
25630
  // when it matters most, because precise wording is what makes a problem
25631
25631
  // unfindable to anyone who doesn't already share it.
25632
- domain: "you flagged a problem that isn't about one specific file \u2014 name the AREA it's about. The sharper your wording, the LESS anyone else will search for it; an abstract problem with no area is an invisible island. Title Case",
25632
+ // Sim note (2026-08-02 shrink): domain emission held 4/4 with just the trigger
25633
+ // clause; the pattern convergence hook now rides here too (it fell 0/4 when it
25634
+ // only appeared in the link bullet's tail).
25635
+ domain: "a problem with no single file \u2014 name the area it's about (Title Case). Also name the general shape it instantiates: (pattern: the shape) \u2014 needs no code anchor, and two agents naming the same shape converge on one node",
25633
25636
  link: "the problem you flagged is an instance of priors/abstractions you can name \u2014 (instance:[h1],[h2],\u2026) / (pattern: \u2026) / (aids:[h]); rendered whole via linkBullet"
25634
25637
  };
25635
25638
  }
@@ -47137,6 +47140,69 @@ var init_webui = __esm({
47137
47140
  }
47138
47141
  });
47139
47142
 
47143
+ // src/witness-ledger.ts
47144
+ var witness_ledger_exports = {};
47145
+ __export(witness_ledger_exports, {
47146
+ appendWitnessLedger: () => appendWitnessLedger,
47147
+ readWitnessLedger: () => readWitnessLedger,
47148
+ summarizeWitnessLedger: () => summarizeWitnessLedger,
47149
+ witnessLedgerPath: () => witnessLedgerPath
47150
+ });
47151
+ import { appendFileSync as appendFileSync2, existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "node:fs";
47152
+ import { join as join25 } from "node:path";
47153
+ function witnessLedgerPath(configDir) {
47154
+ return join25(configDir, "witness-ledger.jsonl");
47155
+ }
47156
+ function appendWitnessLedger(configDir, entry) {
47157
+ const path2 = witnessLedgerPath(configDir);
47158
+ try {
47159
+ appendFileSync2(path2, JSON.stringify(entry) + "\n");
47160
+ const lines = readFileSync20(path2, "utf8").split("\n").filter(Boolean);
47161
+ if (lines.length > LEDGER_MAX_LINES) {
47162
+ writeFileSync17(path2, lines.slice(-Math.floor(LEDGER_MAX_LINES / 2)).join("\n") + "\n");
47163
+ }
47164
+ } catch {
47165
+ }
47166
+ }
47167
+ function readWitnessLedger(configDir) {
47168
+ const path2 = witnessLedgerPath(configDir);
47169
+ if (!existsSync20(path2)) return [];
47170
+ try {
47171
+ return readFileSync20(path2, "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l)).filter((e) => typeof e.ts === "number" && typeof e.channel === "string");
47172
+ } catch {
47173
+ return [];
47174
+ }
47175
+ }
47176
+ function summarizeWitnessLedger(configDir, nowMs) {
47177
+ const entries = readWitnessLedger(configDir);
47178
+ if (entries.length === 0) return [];
47179
+ const byChannel = /* @__PURE__ */ new Map();
47180
+ for (const e of entries) {
47181
+ const c = byChannel.get(e.channel) ?? { recorded: 0, selfSkipped: 0, selfGated: 0, unmatched: 0, lastTs: 0 };
47182
+ c.recorded += e.recorded ?? 0;
47183
+ c.selfSkipped += e.clientSelfSkipped ?? 0;
47184
+ c.selfGated += e.selfGated ?? 0;
47185
+ c.unmatched += e.unmatched ?? 0;
47186
+ c.lastTs = Math.max(c.lastTs, e.ts);
47187
+ byChannel.set(e.channel, c);
47188
+ }
47189
+ const out2 = [];
47190
+ for (const [channel, c] of byChannel) {
47191
+ const ageH = Math.round((nowMs - c.lastTs) / 36e5);
47192
+ out2.push(
47193
+ `${channel}: recorded ${c.recorded} \xB7 self-gated ${c.selfSkipped + c.selfGated} \xB7 unmatched ${c.unmatched} \xB7 last activity ${ageH}h ago`
47194
+ );
47195
+ }
47196
+ return out2;
47197
+ }
47198
+ var LEDGER_MAX_LINES;
47199
+ var init_witness_ledger = __esm({
47200
+ "src/witness-ledger.ts"() {
47201
+ "use strict";
47202
+ LEDGER_MAX_LINES = 500;
47203
+ }
47204
+ });
47205
+
47140
47206
  // src/report-render.ts
47141
47207
  var report_render_exports = {};
47142
47208
  __export(report_render_exports, {
@@ -47528,12 +47594,12 @@ var init_report_render = __esm({
47528
47594
 
47529
47595
  // src/cli.ts
47530
47596
  init_src5();
47531
- import { closeSync as closeSync2, existsSync as existsSync26, openSync as openSync2, readFileSync as readFileSync25, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47532
- import { join as join29 } from "node:path";
47597
+ import { closeSync as closeSync2, existsSync as existsSync27, openSync as openSync2, readFileSync as readFileSync26, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47598
+ import { join as join30 } from "node:path";
47533
47599
  import { spawn as spawn3 } from "node:child_process";
47534
47600
 
47535
47601
  // src/daemon.ts
47536
- import { existsSync as existsSync21, writeFileSync as writeFileSync18 } from "node:fs";
47602
+ import { existsSync as existsSync22, writeFileSync as writeFileSync19 } from "node:fs";
47537
47603
 
47538
47604
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
47539
47605
  import { createServer as createServerHTTP } from "http";
@@ -48113,8 +48179,8 @@ init_config();
48113
48179
 
48114
48180
  // src/engine.ts
48115
48181
  import { execFileSync as execFileSync3 } from "node:child_process";
48116
- 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";
48117
- import { join as join25, relative as relative6, sep as sep4 } from "node:path";
48182
+ 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";
48183
+ import { join as join26, relative as relative6, sep as sep4 } from "node:path";
48118
48184
 
48119
48185
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
48120
48186
  import { stat as statcb } from "fs";
@@ -52560,6 +52626,9 @@ function retireWitnesses(queue, channel, settledKeys) {
52560
52626
  return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
52561
52627
  }
52562
52628
 
52629
+ // src/engine.ts
52630
+ init_witness_ledger();
52631
+
52563
52632
  // src/causal.ts
52564
52633
  var SUPPRESSORS = [
52565
52634
  { kind: "ts-nocheck", re: /@ts-nocheck\b/g },
@@ -52812,7 +52881,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52812
52881
  }
52813
52882
 
52814
52883
  // src/engine.ts
52815
- var DAEMON_VERSION = true ? "2.0.2-dev.282" : "2.0.0-alpha.0";
52884
+ var DAEMON_VERSION = true ? "2.0.2-dev.295" : "2.0.0-alpha.0";
52816
52885
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52817
52886
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52818
52887
  var GIT_OP_MUTE_MS = 4e3;
@@ -52822,17 +52891,17 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
52822
52891
  function appendIdentityAudit(path2, record2, line) {
52823
52892
  if (!record2.accepted && record2.score <= 0) return;
52824
52893
  try {
52825
- if (existsSync20(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52894
+ if (existsSync21(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52826
52895
  renameSync3(path2, `${path2}.1`);
52827
52896
  }
52828
- appendFileSync2(path2, line);
52897
+ appendFileSync3(path2, line);
52829
52898
  } catch {
52830
52899
  }
52831
52900
  }
52832
52901
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
52833
52902
  function loadTurnCursors(path2) {
52834
52903
  try {
52835
- const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52904
+ const raw2 = JSON.parse(readFileSync21(path2, "utf8"));
52836
52905
  return new Map(
52837
52906
  Object.entries(raw2).map(([k, v]) => [k, typeof v === "string" ? v : String(v?.uuid ?? "")])
52838
52907
  );
@@ -52842,7 +52911,7 @@ function loadTurnCursors(path2) {
52842
52911
  }
52843
52912
  function loadTurnOffsets(path2) {
52844
52913
  try {
52845
- const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52914
+ const raw2 = JSON.parse(readFileSync21(path2, "utf8"));
52846
52915
  const out2 = /* @__PURE__ */ new Map();
52847
52916
  for (const [k, v] of Object.entries(raw2)) {
52848
52917
  const off = typeof v === "object" && v !== null ? v.offset : void 0;
@@ -52858,7 +52927,7 @@ function saveTurnCursors(path2, cursors, offsets) {
52858
52927
  const merged = {};
52859
52928
  for (const [k, uuid3] of cursors) merged[k] = { uuid: uuid3, offset: offsets.get(k) ?? 0 };
52860
52929
  for (const [k, offset] of offsets) if (!merged[k]) merged[k] = { uuid: "", offset };
52861
- writeFileSync17(path2, JSON.stringify(merged), "utf8");
52930
+ writeFileSync18(path2, JSON.stringify(merged), "utf8");
52862
52931
  } catch {
52863
52932
  }
52864
52933
  }
@@ -52880,7 +52949,7 @@ function gitSourceWatchTargets(root) {
52880
52949
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
52881
52950
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
52882
52951
  );
52883
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join25(root, d) + sep4));
52952
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join26(root, d) + sep4));
52884
52953
  } catch {
52885
52954
  }
52886
52955
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -52892,19 +52961,19 @@ function gitSourceWatchTargets(root) {
52892
52961
  if (!f.startsWith(prefix)) continue;
52893
52962
  const rest2 = f.slice(prefix.length);
52894
52963
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
52895
- else targets.add(join25(root, f));
52964
+ else targets.add(join26(root, f));
52896
52965
  }
52897
52966
  for (const c of children) {
52898
- if (IGNORED_PATH.test(join25(root, c) + sep4)) continue;
52967
+ if (IGNORED_PATH.test(join26(root, c) + sep4)) continue;
52899
52968
  if (hasIgnoredChild(c)) addUnder(c);
52900
- else targets.add(join25(root, c));
52969
+ else targets.add(join26(root, c));
52901
52970
  }
52902
52971
  };
52903
52972
  addUnder("");
52904
52973
  if (targets.size > 0) return [...targets];
52905
52974
  } catch {
52906
52975
  }
52907
- 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)));
52976
+ 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)));
52908
52977
  }
52909
52978
  function createWorkspaceEngine(opts) {
52910
52979
  const paths = workspacePaths(opts.workspaceRoot);
@@ -53062,7 +53131,7 @@ function createWorkspaceEngine(opts) {
53062
53131
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
53063
53132
  let episodeId2;
53064
53133
  if (srcPaths.length > 0) {
53065
- const abs = srcPaths.map((p) => join25(opts.workspaceRoot, p));
53134
+ const abs = srcPaths.map((p) => join26(opts.workspaceRoot, p));
53066
53135
  try {
53067
53136
  const r = await runReindexPass(
53068
53137
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -53098,8 +53167,8 @@ function createWorkspaceEngine(opts) {
53098
53167
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
53099
53168
  );
53100
53169
  };
53101
- const gitDir = join25(opts.workspaceRoot, ".git");
53102
- if (existsSync20(gitDir)) {
53170
+ const gitDir = join26(opts.workspaceRoot, ".git");
53171
+ if (existsSync21(gitDir)) {
53103
53172
  stopGit = startGitSensor(gitDir, (ev) => {
53104
53173
  void handleGitEvent(ev).catch((err2) => {
53105
53174
  console.warn("[errata] git event handler failed:", err2);
@@ -53169,10 +53238,10 @@ function createWorkspaceEngine(opts) {
53169
53238
  });
53170
53239
  doneRender?.();
53171
53240
  writeContextFile(opts.workspaceRoot, body2);
53172
- const target = join25(opts.workspaceRoot, "AGENTS.md");
53241
+ const target = join26(opts.workspaceRoot, "AGENTS.md");
53173
53242
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
53174
53243
  if (elicit) {
53175
- writePrimingHandles(join25(paths.configDir, "priming-handles.json"), [
53244
+ writePrimingHandles(join26(paths.configDir, "priming-handles.json"), [
53176
53245
  ...snapshot.recentProblems.map((r) => r.node),
53177
53246
  // Resolved-band handles: the ✓ problem AND its Solution are citable
53178
53247
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -53202,7 +53271,7 @@ function createWorkspaceEngine(opts) {
53202
53271
  remoteInFlight = true;
53203
53272
  lastRemoteAt = now;
53204
53273
  try {
53205
- const norm = (xs) => (xs ?? []).map((t) => t.trim().toLowerCase().replace(/[@\s].*$/, "")).filter(Boolean);
53274
+ const norm = (xs) => (xs ?? []).map((t) => t.trim().toLowerCase().replace(/(?!^)[@\s].*$/, "")).filter(Boolean);
53206
53275
  const seed = store.findNodesByLabel("Problem").sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 6).map((p) => p.id);
53207
53276
  const q = {
53208
53277
  stack: norm(profile.stack),
@@ -53388,7 +53457,7 @@ function createWorkspaceEngine(opts) {
53388
53457
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
53389
53458
  });
53390
53459
  };
53391
- const turnCursorPath = join25(paths.configDir, "turn-cursors.json");
53460
+ const turnCursorPath = join26(paths.configDir, "turn-cursors.json");
53392
53461
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
53393
53462
  const turnOffset = loadTurnOffsets(turnCursorPath);
53394
53463
  const sessionLastProblem = /* @__PURE__ */ new Map();
@@ -53412,7 +53481,7 @@ function createWorkspaceEngine(opts) {
53412
53481
  const t = Date.now();
53413
53482
  let processedTurns = 0;
53414
53483
  const elicit = isEdgeElicitationEnabled();
53415
- const handleMap = elicit ? readPrimingHandles(join25(paths.configDir, "priming-handles.json")) : {};
53484
+ const handleMap = elicit ? readPrimingHandles(join26(paths.configDir, "priming-handles.json")) : {};
53416
53485
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
53417
53486
  const toRel = (abs) => {
53418
53487
  const p = abs.replace(/\\/g, "/");
@@ -53803,6 +53872,15 @@ function createWorkspaceEngine(opts) {
53803
53872
  console.log(
53804
53873
  `[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "")
53805
53874
  );
53875
+ appendWitnessLedger(paths.configDir, {
53876
+ ts: Date.now(),
53877
+ channel,
53878
+ recorded,
53879
+ unmatched,
53880
+ duplicate,
53881
+ selfGated,
53882
+ parked: pendingFor(witnessQueue, channel).length
53883
+ });
53806
53884
  };
53807
53885
  if (typeof cloud.reportContradictions === "function") {
53808
53886
  await sendWitnesses(
@@ -53819,7 +53897,10 @@ function createWorkspaceEngine(opts) {
53819
53897
  if (typeof cloud.reportCorroborations === "function") {
53820
53898
  const emit = plan.corroborations.filter((c) => !mintedHere(c.nodeId));
53821
53899
  const skipped = plan.corroborations.length - emit.length;
53822
- if (skipped > 0) console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
53900
+ if (skipped > 0) {
53901
+ console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
53902
+ appendWitnessLedger(paths.configDir, { ts: Date.now(), channel: "corroborate", clientSelfSkipped: skipped });
53903
+ }
53823
53904
  await sendWitnesses(
53824
53905
  "corroborate",
53825
53906
  emit,
@@ -54021,7 +54102,7 @@ function createWorkspaceEngine(opts) {
54021
54102
  try {
54022
54103
  const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
54023
54104
  emitAndProjectSkills(opts.workspaceRoot, inputs);
54024
- writePrimingHandles(join25(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
54105
+ writePrimingHandles(join26(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
54025
54106
  } catch (err2) {
54026
54107
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
54027
54108
  }
@@ -54208,7 +54289,7 @@ function createWorkspaceEngine(opts) {
54208
54289
  console.log(
54209
54290
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
54210
54291
  );
54211
- const pending = existsSync20(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
54292
+ const pending = existsSync21(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
54212
54293
  return { uploaded: 0, failed: 0, remaining: pending };
54213
54294
  }
54214
54295
  try {
@@ -54295,7 +54376,7 @@ async function startDaemon(opts) {
54295
54376
  reviewUrl: () => webUiUrl + "/review"
54296
54377
  });
54297
54378
  const writeLockFile = (url2) => {
54298
- writeFileSync18(
54379
+ writeFileSync19(
54299
54380
  engine.paths.daemonLock,
54300
54381
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
54301
54382
  "utf8"
@@ -54338,7 +54419,7 @@ async function startDaemon(opts) {
54338
54419
  );
54339
54420
  await engine.stop();
54340
54421
  try {
54341
- if (existsSync21(engine.paths.daemonLock)) {
54422
+ if (existsSync22(engine.paths.daemonLock)) {
54342
54423
  }
54343
54424
  } catch {
54344
54425
  }
@@ -54355,16 +54436,16 @@ async function listenServer(fetchFn, port) {
54355
54436
 
54356
54437
  // src/registry.ts
54357
54438
  init_paths();
54358
- import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
54359
- import { join as join26 } from "node:path";
54439
+ import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "node:fs";
54440
+ import { join as join27 } from "node:path";
54360
54441
  function registryPath() {
54361
- return process.env["ERRATA_REGISTRY_PATH"] ?? join26(globalDir(), "workspaces.json");
54442
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join27(globalDir(), "workspaces.json");
54362
54443
  }
54363
54444
  function read() {
54364
54445
  const p = registryPath();
54365
- if (!existsSync22(p)) return { version: 1, workspaces: {} };
54446
+ if (!existsSync23(p)) return { version: 1, workspaces: {} };
54366
54447
  try {
54367
- const parsed = JSON.parse(readFileSync21(p, "utf8"));
54448
+ const parsed = JSON.parse(readFileSync22(p, "utf8"));
54368
54449
  return { version: 1, workspaces: parsed.workspaces ?? {} };
54369
54450
  } catch {
54370
54451
  return { version: 1, workspaces: {} };
@@ -54372,7 +54453,7 @@ function read() {
54372
54453
  }
54373
54454
  function write(reg) {
54374
54455
  ensureDir(globalDir());
54375
- writeFileSync19(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54456
+ writeFileSync20(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54376
54457
  }
54377
54458
  function registerWorkspace(profile, root, now = Date.now()) {
54378
54459
  const reg = read();
@@ -54389,7 +54470,7 @@ function pruneMissingWorkspaces() {
54389
54470
  const reg = read();
54390
54471
  const removed = [];
54391
54472
  for (const [id, entry] of Object.entries(reg.workspaces)) {
54392
- if (!existsSync22(entry.path)) {
54473
+ if (!existsSync23(entry.path)) {
54393
54474
  removed.push(entry);
54394
54475
  delete reg.workspaces[id];
54395
54476
  }
@@ -54398,13 +54479,13 @@ function pruneMissingWorkspaces() {
54398
54479
  return removed;
54399
54480
  }
54400
54481
  function workspaceStatus(entry) {
54401
- const missing = !existsSync22(entry.path);
54482
+ const missing = !existsSync23(entry.path);
54402
54483
  const lockPath = workspacePaths(entry.path).daemonLock;
54403
54484
  let running = false;
54404
54485
  let webUiUrl = null;
54405
- if (existsSync22(lockPath)) {
54486
+ if (existsSync23(lockPath)) {
54406
54487
  try {
54407
- const lock = JSON.parse(readFileSync21(lockPath, "utf8"));
54488
+ const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
54408
54489
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
54409
54490
  running = true;
54410
54491
  webUiUrl = lock.webUiUrl;
@@ -54432,7 +54513,7 @@ function pidAlive(pid) {
54432
54513
  // src/multi.ts
54433
54514
  init_dist();
54434
54515
  init_src4();
54435
- import { readFileSync as readFileSync24, unlinkSync as unlinkSync3, writeFileSync as writeFileSync20 } from "node:fs";
54516
+ import { readFileSync as readFileSync25, unlinkSync as unlinkSync3, writeFileSync as writeFileSync21 } from "node:fs";
54436
54517
 
54437
54518
  // src/principle-sync.ts
54438
54519
  init_src4();
@@ -54460,8 +54541,8 @@ init_reconcile();
54460
54541
 
54461
54542
  // src/lockfile-auto.ts
54462
54543
  init_src();
54463
- import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
54464
- import { join as join27 } from "node:path";
54544
+ import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
54545
+ import { join as join28 } from "node:path";
54465
54546
 
54466
54547
  // src/package-index.ts
54467
54548
  init_src();
@@ -54610,11 +54691,11 @@ function runLockfilePass(opts) {
54610
54691
  { file: "package-lock.json", parse: parsePackageLockJson }
54611
54692
  ];
54612
54693
  for (const c of candidates) {
54613
- const p = join27(opts.root, c.file);
54614
- if (!existsSync23(p)) continue;
54694
+ const p = join28(opts.root, c.file);
54695
+ if (!existsSync24(p)) continue;
54615
54696
  let sbom;
54616
54697
  try {
54617
- sbom = c.parse(readFileSync22(p, "utf8"));
54698
+ sbom = c.parse(readFileSync23(p, "utf8"));
54618
54699
  } catch {
54619
54700
  continue;
54620
54701
  }
@@ -55062,7 +55143,7 @@ var ConsolidateWorker = class {
55062
55143
  init_paths();
55063
55144
 
55064
55145
  // src/lock.ts
55065
- import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
55146
+ import { existsSync as existsSync25, readFileSync as readFileSync24 } from "node:fs";
55066
55147
  function isProcessAlive(pid) {
55067
55148
  if (!pid || pid <= 0) return false;
55068
55149
  try {
@@ -55073,9 +55154,9 @@ function isProcessAlive(pid) {
55073
55154
  }
55074
55155
  }
55075
55156
  function readDaemonLock(lockPath) {
55076
- if (!existsSync24(lockPath)) return null;
55157
+ if (!existsSync25(lockPath)) return null;
55077
55158
  try {
55078
- const lock = JSON.parse(readFileSync23(lockPath, "utf8"));
55159
+ const lock = JSON.parse(readFileSync24(lockPath, "utf8"));
55079
55160
  return typeof lock.pid === "number" ? lock : null;
55080
55161
  } catch {
55081
55162
  return null;
@@ -55354,12 +55435,12 @@ async function reanchorProject(opts) {
55354
55435
  }
55355
55436
 
55356
55437
  // src/adopt.ts
55357
- import { existsSync as existsSync25 } from "node:fs";
55358
- import { dirname as dirname10, join as join28 } from "node:path";
55438
+ import { existsSync as existsSync26 } from "node:fs";
55439
+ import { dirname as dirname10, join as join29 } from "node:path";
55359
55440
  function findGitRoot(absPath) {
55360
55441
  let dir = absPath;
55361
55442
  for (let depth = 0; depth < 64; depth++) {
55362
- if (existsSync25(join28(dir, ".git"))) return dir;
55443
+ if (existsSync26(join29(dir, ".git"))) return dir;
55363
55444
  const parent = dirname10(dir);
55364
55445
  if (parent === dir) return null;
55365
55446
  dir = parent;
@@ -55608,7 +55689,7 @@ async function startMultiDaemon(opts = {}) {
55608
55689
  void ambientLinkAll();
55609
55690
  app.route(`/ws/${rec.id}`, rec.webApp);
55610
55691
  try {
55611
- writeFileSync20(
55692
+ writeFileSync21(
55612
55693
  rec.engine.paths.daemonLock,
55613
55694
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
55614
55695
  "utf8"
@@ -55797,7 +55878,7 @@ async function startMultiDaemon(opts = {}) {
55797
55878
  baseUrl = `http://127.0.0.1:${port}`;
55798
55879
  try {
55799
55880
  ensureDir(globalDir());
55800
- writeFileSync20(
55881
+ writeFileSync21(
55801
55882
  lockPath,
55802
55883
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
55803
55884
  "utf8"
@@ -55806,7 +55887,7 @@ async function startMultiDaemon(opts = {}) {
55806
55887
  }
55807
55888
  for (const r of records) {
55808
55889
  try {
55809
- writeFileSync20(
55890
+ writeFileSync21(
55810
55891
  r.engine.paths.daemonLock,
55811
55892
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
55812
55893
  "utf8"
@@ -56304,7 +56385,7 @@ async function startMultiDaemon(opts = {}) {
56304
56385
  },
56305
56386
  async stop() {
56306
56387
  try {
56307
- const cur = readFileSync24(lockPath, "utf8");
56388
+ const cur = readFileSync25(lockPath, "utf8");
56308
56389
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
56309
56390
  } catch {
56310
56391
  }
@@ -57321,21 +57402,21 @@ async function cmdInit() {
57321
57402
  if (!skipHooks) {
57322
57403
  console.log("");
57323
57404
  console.log("installing harness hooks...");
57324
- const { existsSync: existsSync27 } = await import("node:fs");
57325
- const { join: join30 } = await import("node:path");
57405
+ const { existsSync: existsSync28 } = await import("node:fs");
57406
+ const { join: join31 } = await import("node:path");
57326
57407
  try {
57327
57408
  await installClaudeHooks(port);
57328
57409
  } catch (err2) {
57329
57410
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
57330
57411
  }
57331
- if (existsSync27(join30(ROOT, ".cursor"))) {
57412
+ if (existsSync28(join31(ROOT, ".cursor"))) {
57332
57413
  try {
57333
57414
  await installCursorMcpConfig();
57334
57415
  } catch (err2) {
57335
57416
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
57336
57417
  }
57337
57418
  }
57338
- if (existsSync27(join30(ROOT, ".codex"))) {
57419
+ if (existsSync28(join31(ROOT, ".codex"))) {
57339
57420
  try {
57340
57421
  await installCodexHooks(port);
57341
57422
  } catch (err2) {
@@ -57492,9 +57573,9 @@ async function cmdStatus() {
57492
57573
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
57493
57574
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
57494
57575
  }
57495
- console.log(` graph db: ${existsSync26(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57496
- console.log(` event log: ${existsSync26(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57497
- if (existsSync26(paths.castalia)) {
57576
+ console.log(` graph db: ${existsSync27(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57577
+ console.log(` event log: ${existsSync27(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57578
+ if (existsSync27(paths.castalia)) {
57498
57579
  try {
57499
57580
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57500
57581
  const store = openGraphStore2({ path: paths.castalia });
@@ -57526,6 +57607,13 @@ async function cmdStatus() {
57526
57607
  } catch {
57527
57608
  }
57528
57609
  }
57610
+ try {
57611
+ const { summarizeWitnessLedger: summarizeWitnessLedger2 } = await Promise.resolve().then(() => (init_witness_ledger(), witness_ledger_exports));
57612
+ const lines = summarizeWitnessLedger2(paths.configDir, Date.now());
57613
+ if (lines.length === 0) console.log(" evidence: no witness activity ledgered yet (corroborate/contradict have never fired here)");
57614
+ else for (const l of lines) console.log(` evidence: ${l}`);
57615
+ } catch {
57616
+ }
57529
57617
  const lockPath = globalDaemonLock();
57530
57618
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
57531
57619
  console.log(
@@ -58168,11 +58256,11 @@ function cmdInstallationProfile(args2) {
58168
58256
  }
58169
58257
  async function cmdReview() {
58170
58258
  const paths = workspacePaths(ROOT);
58171
- if (!existsSync26(paths.reviewQueue)) {
58259
+ if (!existsSync27(paths.reviewQueue)) {
58172
58260
  console.log("(review queue empty)");
58173
58261
  return;
58174
58262
  }
58175
- const queue = JSON.parse(readFileSync25(paths.reviewQueue, "utf8"));
58263
+ const queue = JSON.parse(readFileSync26(paths.reviewQueue, "utf8"));
58176
58264
  if (queue.length === 0) {
58177
58265
  console.log("(review queue empty)");
58178
58266
  return;
@@ -58843,7 +58931,7 @@ async function gatherRepo(store, ws) {
58843
58931
  };
58844
58932
  }
58845
58933
  async function gatherReportData(generatedAt) {
58846
- const { existsSync: existsSync27 } = await import("node:fs");
58934
+ const { existsSync: existsSync28 } = await import("node:fs");
58847
58935
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
58848
58936
  const cfg = loadConfig();
58849
58937
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -58851,7 +58939,7 @@ async function gatherReportData(generatedAt) {
58851
58939
  for (const ws of listWorkspaces()) {
58852
58940
  if (ws.missing) continue;
58853
58941
  const dbPath = workspacePaths(ws.path).castalia;
58854
- if (!existsSync27(dbPath)) continue;
58942
+ if (!existsSync28(dbPath)) continue;
58855
58943
  let store = null;
58856
58944
  try {
58857
58945
  store = openGraphStore2({ path: dbPath });
@@ -58882,7 +58970,7 @@ async function gatherReportData(generatedAt) {
58882
58970
  };
58883
58971
  }
58884
58972
  async function cmdReport(args2) {
58885
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync21 } = await import("node:fs");
58973
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync22 } = await import("node:fs");
58886
58974
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
58887
58975
  const includeFutureVerbs = args2.includes("--future-verbs");
58888
58976
  const now = /* @__PURE__ */ new Date();
@@ -58895,8 +58983,8 @@ async function cmdReport(args2) {
58895
58983
  const outDir = workspacePaths(ROOT).configDir;
58896
58984
  mkdirSync8(outDir, { recursive: true });
58897
58985
  const files = renderReport2(data, { includeFutureVerbs });
58898
- for (const f of files) writeFileSync21(join29(outDir, f.name), f.html, "utf8");
58899
- const indexPath = join29(outDir, "report.html");
58986
+ for (const f of files) writeFileSync22(join30(outDir, f.name), f.html, "utf8");
58987
+ const indexPath = join30(outDir, "report.html");
58900
58988
  console.log(`report \u2192 ${indexPath}`);
58901
58989
  console.log(
58902
58990
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -59014,15 +59102,15 @@ function hookRelayCommand(port, path2) {
59014
59102
  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 '{}'`;
59015
59103
  }
59016
59104
  async function installClaudeHooks(port) {
59017
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
59018
- const { join: join30 } = await import("node:path");
59019
- const dir = join30(ROOT, ".claude");
59020
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59021
- const file2 = join30(dir, "settings.json");
59105
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59106
+ const { join: join31 } = await import("node:path");
59107
+ const dir = join31(ROOT, ".claude");
59108
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59109
+ const file2 = join31(dir, "settings.json");
59022
59110
  let settings = {};
59023
- if (existsSync27(file2)) {
59111
+ if (existsSync28(file2)) {
59024
59112
  try {
59025
- settings = JSON.parse(readFileSync26(file2, "utf8"));
59113
+ settings = JSON.parse(readFileSync27(file2, "utf8"));
59026
59114
  } catch {
59027
59115
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
59028
59116
  process.exit(2);
@@ -59068,10 +59156,10 @@ async function installClaudeHooks(port) {
59068
59156
  dropErrata(list);
59069
59157
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
59070
59158
  }
59071
- writeFileSync21(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
59159
+ writeFileSync22(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
59072
59160
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
59073
59161
  await installClaudeMcpConfig();
59074
- const claudeMd = join30(ROOT, "CLAUDE.md");
59162
+ const claudeMd = join31(ROOT, "CLAUDE.md");
59075
59163
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
59076
59164
  if (recall.kind === "collision") {
59077
59165
  console.warn(
@@ -59083,15 +59171,15 @@ async function installClaudeHooks(port) {
59083
59171
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
59084
59172
  }
59085
59173
  async function installClaudeMcpConfig() {
59086
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
59087
- const { join: join30, dirname: dirname11 } = await import("node:path");
59088
- const file2 = join30(ROOT, ".mcp.json");
59174
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59175
+ const { join: join31, dirname: dirname11 } = await import("node:path");
59176
+ const file2 = join31(ROOT, ".mcp.json");
59089
59177
  const dir = dirname11(file2);
59090
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59178
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59091
59179
  let cfg = {};
59092
- if (existsSync27(file2)) {
59180
+ if (existsSync28(file2)) {
59093
59181
  try {
59094
- cfg = JSON.parse(readFileSync26(file2, "utf8"));
59182
+ cfg = JSON.parse(readFileSync27(file2, "utf8"));
59095
59183
  } catch {
59096
59184
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
59097
59185
  process.exit(2);
@@ -59099,21 +59187,21 @@ async function installClaudeMcpConfig() {
59099
59187
  }
59100
59188
  cfg.mcpServers ??= {};
59101
59189
  cfg.mcpServers["errata"] = errataMcpInvocation();
59102
- writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59190
+ writeFileSync22(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59103
59191
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
59104
59192
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
59105
59193
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
59106
59194
  }
59107
59195
  async function installCursorMcpConfig() {
59108
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
59109
- const { join: join30 } = await import("node:path");
59110
- const dir = join30(ROOT, ".cursor");
59111
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59112
- const file2 = join30(dir, "mcp.json");
59196
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59197
+ const { join: join31 } = await import("node:path");
59198
+ const dir = join31(ROOT, ".cursor");
59199
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59200
+ const file2 = join31(dir, "mcp.json");
59113
59201
  let cfg = {};
59114
- if (existsSync27(file2)) {
59202
+ if (existsSync28(file2)) {
59115
59203
  try {
59116
- cfg = JSON.parse(readFileSync26(file2, "utf8"));
59204
+ cfg = JSON.parse(readFileSync27(file2, "utf8"));
59117
59205
  } catch {
59118
59206
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
59119
59207
  process.exit(2);
@@ -59121,7 +59209,7 @@ async function installCursorMcpConfig() {
59121
59209
  }
59122
59210
  cfg.mcpServers ??= {};
59123
59211
  cfg.mcpServers["errata"] = errataMcpInvocation();
59124
- writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59212
+ writeFileSync22(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59125
59213
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
59126
59214
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
59127
59215
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -59129,16 +59217,16 @@ async function installCursorMcpConfig() {
59129
59217
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
59130
59218
  }
59131
59219
  async function installCodexHooks(port) {
59132
- const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
59133
- const { join: join30 } = await import("node:path");
59134
- const dir = join30(ROOT, ".codex");
59135
- if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59136
- const file2 = join30(dir, "config.toml");
59220
+ const { mkdirSync: mkdirSync8, existsSync: existsSync28, readFileSync: readFileSync27, writeFileSync: writeFileSync22 } = await import("node:fs");
59221
+ const { join: join31 } = await import("node:path");
59222
+ const dir = join31(ROOT, ".codex");
59223
+ if (!existsSync28(dir)) mkdirSync8(dir, { recursive: true });
59224
+ const file2 = join31(dir, "config.toml");
59137
59225
  const BEGIN = `# >>> errata hooks (errata-managed)`;
59138
59226
  const END = `# <<< errata hooks`;
59139
59227
  let existing = "";
59140
- if (existsSync27(file2)) {
59141
- existing = readFileSync26(file2, "utf8");
59228
+ if (existsSync28(file2)) {
59229
+ existing = readFileSync27(file2, "utf8");
59142
59230
  const beginIdx = existing.indexOf(BEGIN);
59143
59231
  const endIdx = existing.indexOf(END);
59144
59232
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -59167,7 +59255,7 @@ ${END}
59167
59255
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
59168
59256
 
59169
59257
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
59170
- writeFileSync21(file2, final, "utf8");
59258
+ writeFileSync22(file2, final, "utf8");
59171
59259
  console.log(`installed Codex hooks \u2192 ${file2}`);
59172
59260
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
59173
59261
  console.log("");
@@ -59481,7 +59569,7 @@ async function cmdDash(args2) {
59481
59569
  await yieldToLoop2();
59482
59570
  try {
59483
59571
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
59484
- const res = bleedRules(join29(r.root, ".claude", "rules"), items);
59572
+ const res = bleedRules(join30(r.root, ".claude", "rules"), items);
59485
59573
  if (res.written || res.pruned) {
59486
59574
  console.log(
59487
59575
  `[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.282",
3
+ "version": "2.0.2-dev.295",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {