@inerrata-corporation/errata 2.0.2-dev.227 → 2.0.2-dev.247

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.
package/errata.mjs CHANGED
@@ -16410,6 +16410,21 @@ var init_src2 = __esm({
16410
16410
  });
16411
16411
 
16412
16412
  // ../../packages/local-graph/src/store.ts
16413
+ function localEdgeViolation(fromLabel, type, toLabel) {
16414
+ if (type in LOCAL_RULE_OVERRIDES) {
16415
+ const rule = LOCAL_RULE_OVERRIDES[type];
16416
+ if (!rule) return null;
16417
+ if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
16418
+ return `${type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
16419
+ }
16420
+ if (toLabel && rule.to && !rule.to.includes(toLabel)) {
16421
+ return `${type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
16422
+ }
16423
+ return null;
16424
+ }
16425
+ const verdict = isValidEdge(fromLabel, type, toLabel);
16426
+ return verdict.ok ? null : verdict.reason ?? "edge rule violation";
16427
+ }
16413
16428
  function encodeEmbedding(emb) {
16414
16429
  if (!emb || emb.length === 0) return null;
16415
16430
  const f = Float32Array.from(emb);
@@ -16484,7 +16499,7 @@ var init_store = __esm({
16484
16499
  REVEALED_BY: null,
16485
16500
  PRODUCED: null
16486
16501
  };
16487
- SCHEMA_VERSION = 5;
16502
+ SCHEMA_VERSION = 6;
16488
16503
  SCHEMA_SQL = `
16489
16504
  CREATE TABLE IF NOT EXISTS schema_version (
16490
16505
  version INTEGER PRIMARY KEY
@@ -16499,6 +16514,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
16499
16514
  value TEXT NOT NULL
16500
16515
  );
16501
16516
 
16517
+ -- Durable ledger of edges the ontology gate REFUSED, keyed by edge type.
16518
+ -- Durable rather than in-memory for one specific reason: the status command runs
16519
+ -- in a SEPARATE process and opens its own store handle, so a counter living on
16520
+ -- the instance reads 0 there forever. That is exactly how a producer rejecting
16521
+ -- 100% of its output stayed invisible for 17 days. Persisting it also survives
16522
+ -- the daemon restart that would otherwise erase the evidence.
16523
+ -- Keyed by type because a systematic producer bug shows up as ONE type
16524
+ -- dominating; sample keeps the latest reason so the count is actionable.
16525
+ CREATE TABLE IF NOT EXISTS edge_rejections (
16526
+ type TEXT PRIMARY KEY,
16527
+ count INTEGER NOT NULL DEFAULT 0,
16528
+ last_at INTEGER NOT NULL,
16529
+ sample TEXT
16530
+ );
16531
+
16502
16532
  CREATE TABLE IF NOT EXISTS nodes (
16503
16533
  id TEXT PRIMARY KEY,
16504
16534
  label TEXT NOT NULL,
@@ -16818,6 +16848,16 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16818
16848
  if (!cols.has(name2)) this.db.exec(ddl);
16819
16849
  }
16820
16850
  }
16851
+ if (from < 6) {
16852
+ this.db.exec(`
16853
+ CREATE TABLE IF NOT EXISTS edge_rejections (
16854
+ type TEXT PRIMARY KEY,
16855
+ count INTEGER NOT NULL DEFAULT 0,
16856
+ last_at INTEGER NOT NULL,
16857
+ sample TEXT
16858
+ )
16859
+ `);
16860
+ }
16821
16861
  this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
16822
16862
  });
16823
16863
  }
@@ -16899,6 +16939,7 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16899
16939
  const violation = this.edgeRuleViolation(edge2);
16900
16940
  if (violation) {
16901
16941
  this.rejectedEdgeCount++;
16942
+ this.recordEdgeRejection(edge2.type, violation, edge2.lastSeenAt || edge2.createdAt || 0);
16902
16943
  console.warn(`[local-graph] rejected edge ${edge2.from}-[:${edge2.type}]->${edge2.to}: ${violation}`);
16903
16944
  return;
16904
16945
  }
@@ -16923,22 +16964,51 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16923
16964
  * overlay consulted first. Returns the reason string on a documented-
16924
16965
  * forbidden combination, else null. Point lookups on the id PK — negligible
16925
16966
  * next to the insert itself. */
16926
- edgeRuleViolation(edge2) {
16927
- const fromLabel = this.getNode(edge2.from)?.label;
16928
- const toLabel = this.getNode(edge2.to)?.label;
16929
- if (edge2.type in LOCAL_RULE_OVERRIDES) {
16930
- const rule = LOCAL_RULE_OVERRIDES[edge2.type];
16931
- if (!rule) return null;
16932
- if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
16933
- return `${edge2.type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
16934
- }
16935
- if (toLabel && rule.to && !rule.to.includes(toLabel)) {
16936
- return `${edge2.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
16937
- }
16938
- return null;
16967
+ /** Upsert one refusal into the durable ledger. Best-effort: a bookkeeping
16968
+ * failure must never turn a refused edge into a thrown write. */
16969
+ recordEdgeRejection(type, reason, at) {
16970
+ try {
16971
+ this.db.prepare(
16972
+ `INSERT INTO edge_rejections (type, count, last_at, sample) VALUES (?, 1, ?, ?)
16973
+ ON CONFLICT(type) DO UPDATE SET
16974
+ count = count + 1, last_at = excluded.last_at, sample = excluded.sample`
16975
+ ).run(type, at, reason.slice(0, 200));
16976
+ } catch {
16977
+ }
16978
+ }
16979
+ /** Refusals recorded by the ontology gate, per edge type, newest activity first.
16980
+ * Durable across restarts and readable from any process (see the table note). */
16981
+ edgeRejections() {
16982
+ try {
16983
+ return this.db.prepare(
16984
+ "SELECT type, count, last_at AS lastAt, sample FROM edge_rejections ORDER BY count DESC, last_at DESC"
16985
+ ).all();
16986
+ } catch {
16987
+ return [];
16988
+ }
16989
+ }
16990
+ /** Drop ledger entries whose last refusal predates `cutoff`. A still-misbehaving
16991
+ * producer keeps refreshing `last_at` and survives; a fixed one fades out. */
16992
+ pruneEdgeRejections(cutoff) {
16993
+ try {
16994
+ this.db.prepare("DELETE FROM edge_rejections WHERE last_at < ?").run(cutoff);
16995
+ } catch {
16996
+ }
16997
+ }
16998
+ /** Clear the ledger outright, whole or per type — operator escape hatch. */
16999
+ clearEdgeRejections(type) {
17000
+ try {
17001
+ if (type) this.db.prepare("DELETE FROM edge_rejections WHERE type = ?").run(type);
17002
+ else this.db.exec("DELETE FROM edge_rejections");
17003
+ } catch {
16939
17004
  }
16940
- const verdict = isValidEdge(fromLabel, edge2.type, toLabel);
16941
- return verdict.ok ? null : verdict.reason ?? "edge rule violation";
17005
+ }
17006
+ edgeRuleViolation(edge2) {
17007
+ return localEdgeViolation(
17008
+ this.getNode(edge2.from)?.label,
17009
+ edge2.type,
17010
+ this.getNode(edge2.to)?.label
17011
+ );
16942
17012
  }
16943
17013
  updateEdge(id, patch) {
16944
17014
  this.stmts.updateEdge.run({
@@ -17075,6 +17145,32 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
17075
17145
  const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
17076
17146
  return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
17077
17147
  }
17148
+ /**
17149
+ * Live edges WITH their endpoint labels and ids, resolved in ONE join.
17150
+ *
17151
+ * The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
17152
+ * Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
17153
+ * 550,000 on this store — and each one deserializes the node's embedding blob.
17154
+ * Measured: the sweep did not finish in 10 minutes. As a single join it is one
17155
+ * query over an index-covered scan. Labels only; nothing here touches embeddings.
17156
+ */
17157
+ scanLiveEdgeRows() {
17158
+ const rows = this.db.prepare(
17159
+ `SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
17160
+ FROM edges e
17161
+ LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
17162
+ LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
17163
+ WHERE e.valid_to IS NULL`
17164
+ ).all();
17165
+ return rows.map((r) => ({
17166
+ id: r.id,
17167
+ from: r.from_id,
17168
+ to: r.to_id,
17169
+ type: r.type,
17170
+ fromLabel: r.from_label ?? void 0,
17171
+ toLabel: r.to_label ?? void 0
17172
+ }));
17173
+ }
17078
17174
  /** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
17079
17175
  * expression index so the incremental reindex fetches only the changed files'
17080
17176
  * symbols instead of scanning every versioned node. */
@@ -20879,6 +20975,7 @@ __export(src_exports2, {
20879
20975
  linkProblemToPackages: () => linkProblemToPackages,
20880
20976
  linkProblemToSymbols: () => linkProblemToSymbols,
20881
20977
  listNeedsRevisit: () => listNeedsRevisit,
20978
+ localEdgeViolation: () => localEdgeViolation,
20882
20979
  markRevisit: () => markRevisit,
20883
20980
  matchLanguagesInText: () => matchLanguagesInText,
20884
20981
  matchPackagesInText: () => matchPackagesInText,
@@ -21643,6 +21740,7 @@ var init_oauth = __esm({
21643
21740
 
21644
21741
  // ../../packages/cloud-client/src/client.ts
21645
21742
  import { randomUUID } from "node:crypto";
21743
+ import { gzipSync } from "node:zlib";
21646
21744
  function wirePerContext(value) {
21647
21745
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
21648
21746
  const out2 = {};
@@ -21840,6 +21938,11 @@ function normalizeSolution(input) {
21840
21938
  validationSource: input.validationSource
21841
21939
  };
21842
21940
  }
21941
+ function isIntermediaryRejection(err2) {
21942
+ if (!(err2 instanceof CloudError)) return false;
21943
+ if (err2.status < 400 || err2.status >= 500) return false;
21944
+ return /<!DOCTYPE html|<html/i.test(err2.message);
21945
+ }
21843
21946
  function chunkArray(items, size) {
21844
21947
  if (items.length <= size) return items.length ? [[...items]] : [];
21845
21948
  const out2 = [];
@@ -21857,7 +21960,7 @@ function provenanceHeaders(provenance) {
21857
21960
  ...provenance.agentModel ? { "x-inerrata-agent-model": provenance.agentModel } : {}
21858
21961
  };
21859
21962
  }
21860
- var asWireCount, INGEST_NODE_CHUNK, CloudClient, CloudError;
21963
+ var asWireCount, INGEST_NODE_CHUNK, COMPRESS_MIN_BYTES, CloudClient, CloudError;
21861
21964
  var init_client = __esm({
21862
21965
  "../../packages/cloud-client/src/client.ts"() {
21863
21966
  "use strict";
@@ -21865,6 +21968,7 @@ var init_client = __esm({
21865
21968
  init_src();
21866
21969
  asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
21867
21970
  INGEST_NODE_CHUNK = 8;
21971
+ COMPRESS_MIN_BYTES = 4096;
21868
21972
  CloudClient = class {
21869
21973
  baseUrl;
21870
21974
  apiKey;
@@ -21879,6 +21983,8 @@ var init_client = __esm({
21879
21983
  daemonVersion;
21880
21984
  daemonChannel;
21881
21985
  provenance;
21986
+ /** Cleared for the process once a server proves it cannot inflate (see `json`). */
21987
+ compressRequests;
21882
21988
  constructor(opts) {
21883
21989
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
21884
21990
  if (opts.apiKey !== void 0) this.apiKey = opts.apiKey;
@@ -21892,6 +21998,7 @@ var init_client = __esm({
21892
21998
  this.timeoutMs = opts.timeoutMs ?? 15e3;
21893
21999
  this.daemonVersion = opts.daemonVersion;
21894
22000
  this.daemonChannel = opts.daemonChannel;
22001
+ this.compressRequests = opts.compressRequests ?? true;
21895
22002
  this.provenance = opts.provenance ?? {
21896
22003
  clientProduct: "inerrata_cloud_client",
21897
22004
  clientKind: "sdk",
@@ -22029,18 +22136,24 @@ var init_client = __esm({
22029
22136
  const startedAt = Date.now();
22030
22137
  const totalChunks = Math.max(1, Math.ceil(batch.nodes.length / INGEST_NODE_CHUNK)) + Math.ceil(Math.max(0, batch.edges.length - MAX_EDGES_PER_PAYLOAD) / MAX_EDGES_PER_PAYLOAD);
22031
22138
  let chunkIndex = 0;
22139
+ const blocked = [];
22032
22140
  const emit = (nodeIds, accepted) => {
22033
22141
  chunkIndex++;
22142
+ const refused = blocked.splice(0, blocked.length);
22143
+ const refusedSet = new Set(refused);
22034
22144
  opts.onChunk?.({
22035
22145
  chunkIndex,
22036
22146
  totalChunks,
22037
- nodeIds,
22147
+ // Watermark only what actually landed — a blocked node never reached the
22148
+ // door, and marking it contributed would lose it permanently and silently.
22149
+ nodeIds: refusedSet.size ? nodeIds.filter((id) => !refusedSet.has(id)) : nodeIds,
22038
22150
  accepted,
22039
- elapsedMs: Date.now() - startedAt
22151
+ elapsedMs: Date.now() - startedAt,
22152
+ ...refused.length ? { blockedNodeIds: refused } : {}
22040
22153
  });
22041
22154
  };
22042
22155
  if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
22043
- const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges);
22156
+ const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges, blocked);
22044
22157
  const summary = summarizeIngestResult(result);
22045
22158
  emit(batch.nodes.map((n) => n.id), summary.accepted);
22046
22159
  return { ...summary, result };
@@ -22055,7 +22168,7 @@ var init_client = __esm({
22055
22168
  const shipped = new Set(inChunk);
22056
22169
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
22057
22170
  }
22058
- const r = await this.ingestWithSplit(batch, nodeChunk, inChunk);
22171
+ const r = await this.ingestWithSplit(batch, nodeChunk, inChunk, blocked);
22059
22172
  emit(nodeChunk.map((n) => n.id), summarizeIngestResult(r).accepted);
22060
22173
  merged.nodes.push(...r.nodes);
22061
22174
  merged.edges.push(...r.edges);
@@ -22098,16 +22211,21 @@ var init_client = __esm({
22098
22211
  * a verdict about the payload — splitting it would just re-send a rejected batch
22099
22212
  * N more times — and a 409/413 has its own handling upstream.
22100
22213
  */
22101
- async ingestWithSplit(batch, nodes, edges) {
22214
+ async ingestWithSplit(batch, nodes, edges, blocked) {
22102
22215
  try {
22103
22216
  return await this.ingestWire(toWirePayload({ ...batch, nodes, edges }, randomUUID()));
22104
22217
  } catch (err2) {
22105
22218
  const status = err2 instanceof CloudError ? err2.status : 0;
22106
22219
  const timedOut = status === 502 || status === 504 || status === 408 || status === 0;
22107
- if (!timedOut || nodes.length <= 1) throw err2;
22220
+ const refusedAtEdge = blocked !== void 0 && isIntermediaryRejection(err2);
22221
+ if (refusedAtEdge && nodes.length === 1 && nodes[0]) {
22222
+ blocked.push(nodes[0].id);
22223
+ return { runId: randomUUID(), nodes: [], edges: [] };
22224
+ }
22225
+ if (!timedOut && !refusedAtEdge || nodes.length <= 1) throw err2;
22108
22226
  const mid = Math.ceil(nodes.length / 2);
22109
- const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges);
22110
- const b = await this.ingestWithSplit(batch, nodes.slice(mid), []);
22227
+ const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges, blocked);
22228
+ const b = await this.ingestWithSplit(batch, nodes.slice(mid), [], blocked);
22111
22229
  return {
22112
22230
  runId: a.runId,
22113
22231
  nodes: [...a.nodes, ...b.nodes],
@@ -22409,15 +22527,43 @@ var init_client = __esm({
22409
22527
  const token = opts?.bearerToken ?? await this.authToken();
22410
22528
  if (token) headers["authorization"] = `Bearer ${token}`;
22411
22529
  }
22530
+ let payload;
22531
+ if (body2 !== void 0) {
22532
+ const raw2 = JSON.stringify(body2);
22533
+ if (this.compressRequests && Buffer.byteLength(raw2) >= COMPRESS_MIN_BYTES) {
22534
+ payload = gzipSync(Buffer.from(raw2));
22535
+ headers["content-encoding"] = "gzip";
22536
+ } else {
22537
+ payload = raw2;
22538
+ }
22539
+ }
22412
22540
  const ac = new AbortController();
22413
22541
  const tid = setTimeout(() => ac.abort(), this.timeoutMs);
22414
22542
  try {
22415
22543
  const res = await this.fetchFn(url2, {
22416
22544
  method,
22417
22545
  headers,
22418
- body: body2 === void 0 ? void 0 : JSON.stringify(body2),
22546
+ body: payload,
22419
22547
  signal: ac.signal
22420
22548
  });
22549
+ if (res.status === 400 && headers["content-encoding"] === "gzip") {
22550
+ delete headers["content-encoding"];
22551
+ const retry = await this.fetchFn(url2, {
22552
+ method,
22553
+ headers,
22554
+ body: JSON.stringify(body2),
22555
+ signal: ac.signal
22556
+ });
22557
+ if (!retry.ok) {
22558
+ const text = await retry.text();
22559
+ throw new CloudError(
22560
+ `${method} ${path2} failed: HTTP ${retry.status} ${text.slice(0, 200)}`,
22561
+ retry.status
22562
+ );
22563
+ }
22564
+ this.compressRequests = false;
22565
+ return await retry.json();
22566
+ }
22421
22567
  if (!res.ok) {
22422
22568
  const text = await res.text();
22423
22569
  throw new CloudError(
@@ -47262,12 +47408,12 @@ var init_report_render = __esm({
47262
47408
 
47263
47409
  // src/cli.ts
47264
47410
  init_src5();
47265
- import { closeSync as closeSync2, existsSync as existsSync25, openSync as openSync2, readFileSync as readFileSync24, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47266
- import { join as join28 } from "node:path";
47411
+ import { closeSync as closeSync2, existsSync as existsSync26, openSync as openSync2, readFileSync as readFileSync25, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47412
+ import { join as join29 } from "node:path";
47267
47413
  import { spawn as spawn3 } from "node:child_process";
47268
47414
 
47269
47415
  // src/daemon.ts
47270
- import { existsSync as existsSync20, writeFileSync as writeFileSync17 } from "node:fs";
47416
+ import { existsSync as existsSync21, writeFileSync as writeFileSync18 } from "node:fs";
47271
47417
 
47272
47418
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
47273
47419
  import { createServer as createServerHTTP } from "http";
@@ -47847,8 +47993,8 @@ init_config();
47847
47993
 
47848
47994
  // src/engine.ts
47849
47995
  import { execFileSync as execFileSync3 } from "node:child_process";
47850
- import { existsSync as existsSync19, statSync as statSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync9, renameSync as renameSync3, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "node:fs";
47851
- import { join as join24, relative as relative6, sep as sep4 } from "node:path";
47996
+ 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";
47997
+ import { join as join25, relative as relative6, sep as sep4 } from "node:path";
47852
47998
 
47853
47999
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
47854
48000
  import { stat as statcb } from "fs";
@@ -50292,6 +50438,10 @@ function tagEdgeCorroborated(targetAnchorIds, sessionTouchedNodeIds, independent
50292
50438
  for (const a of targetAnchorIds) if (sessionTouchedNodeIds.has(a)) return true;
50293
50439
  return false;
50294
50440
  }
50441
+ function citeEdgeType(targetLabel2) {
50442
+ const t = typePriorEdge("Problem", targetLabel2);
50443
+ return t === "SUPERSEDES" ? "RELATES_TO" : t;
50444
+ }
50295
50445
  function typePriorEdge(sourceLabel, targetLabel2, sentence = "") {
50296
50446
  const direct = LABEL_PAIR[`${sourceLabel}>${targetLabel2}`];
50297
50447
  if (direct) return direct;
@@ -50838,6 +50988,90 @@ function backfillConstraintKind(store, opts) {
50838
50988
  return report;
50839
50989
  }
50840
50990
 
50991
+ // src/edge-repair.ts
50992
+ init_src();
50993
+ init_src4();
50994
+ import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "node:fs";
50995
+ import { join as join17 } from "node:path";
50996
+ function citeEdgeId(from, type, to) {
50997
+ return `edge_${digest({ from, type, to })}`.slice(0, 24);
50998
+ }
50999
+ var EDGE_REPAIR_VERSION = 1;
51000
+ var REJECTION_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
51001
+ var EMPTY2 = {
51002
+ skipped: true,
51003
+ scanned: 0,
51004
+ invalid: 0,
51005
+ retyped: 0,
51006
+ closed: 0,
51007
+ byType: {}
51008
+ };
51009
+ function markerPath2(configDir) {
51010
+ return join17(configDir, "edge-repair.json");
51011
+ }
51012
+ function alreadyDone2(configDir) {
51013
+ const p = markerPath2(configDir);
51014
+ if (!existsSync14(p)) return false;
51015
+ try {
51016
+ return JSON.parse(readFileSync12(p, "utf8"))?.version === EDGE_REPAIR_VERSION;
51017
+ } catch {
51018
+ return false;
51019
+ }
51020
+ }
51021
+ function repairInvalidEdges(store, opts) {
51022
+ if (!opts.force && !opts.dryRun && alreadyDone2(opts.configDir)) return EMPTY2;
51023
+ const report = {
51024
+ skipped: false,
51025
+ scanned: 0,
51026
+ invalid: 0,
51027
+ retyped: 0,
51028
+ closed: 0,
51029
+ byType: {}
51030
+ };
51031
+ const work = [];
51032
+ for (const e of store.scanLiveEdgeRows()) {
51033
+ report.scanned++;
51034
+ if (!localEdgeViolation(e.fromLabel, e.type, e.toLabel)) continue;
51035
+ report.invalid++;
51036
+ const candidate = e.toLabel ? citeEdgeType(e.toLabel) : null;
51037
+ const retype = candidate && candidate !== e.type && !localEdgeViolation(e.fromLabel, candidate, e.toLabel) ? candidate : null;
51038
+ work.push({ edge: { id: e.id, from: e.from, to: e.to, type: e.type }, retype });
51039
+ report.byType[e.type] = (report.byType[e.type] ?? 0) + 1;
51040
+ if (retype) report.retyped++;
51041
+ else report.closed++;
51042
+ }
51043
+ if (opts.dryRun) return report;
51044
+ store.transaction(() => {
51045
+ for (const w of work) {
51046
+ const prior = store.getEdge(w.edge.id);
51047
+ store.closeEdge(w.edge.id, opts.now);
51048
+ if (!w.retype || !prior) continue;
51049
+ store.mergeEdge({
51050
+ ...prior,
51051
+ id: citeEdgeId(w.edge.from, w.retype, w.edge.to),
51052
+ type: w.retype,
51053
+ validFrom: opts.now,
51054
+ validTo: null,
51055
+ lastSeenAt: opts.now,
51056
+ // Mark the provenance of the rewrite so a later audit can tell a repaired
51057
+ // edge from one captured natively — without which this sweep would be
51058
+ // indistinguishable from the agent having witnessed it post-fix.
51059
+ attrs: { ...prior.attrs ?? {}, retypedFrom: w.edge.type, retypedBy: `edge-repair:v${EDGE_REPAIR_VERSION}` }
51060
+ });
51061
+ }
51062
+ });
51063
+ store.pruneEdgeRejections(opts.now - REJECTION_RETENTION_MS);
51064
+ try {
51065
+ writeFileSync12(
51066
+ markerPath2(opts.configDir),
51067
+ JSON.stringify({ version: EDGE_REPAIR_VERSION, at: opts.now, ...report }, null, 2),
51068
+ "utf8"
51069
+ );
51070
+ } catch {
51071
+ }
51072
+ return report;
51073
+ }
51074
+
50841
51075
  // src/engine.ts
50842
51076
  init_symbol_summaries();
50843
51077
  init_reconcile();
@@ -51036,11 +51270,11 @@ init_outbox();
51036
51270
  init_src8();
51037
51271
  init_src();
51038
51272
  init_src2();
51039
- import { readFileSync as readFileSync12 } from "node:fs";
51040
- import { join as join17 } from "node:path";
51273
+ import { readFileSync as readFileSync13 } from "node:fs";
51274
+ import { join as join18 } from "node:path";
51041
51275
  function loadClaimIgnorePatterns(workspaceRoot) {
51042
51276
  try {
51043
- return readFileSync12(join17(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
51277
+ return readFileSync13(join18(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
51044
51278
  } catch {
51045
51279
  return [];
51046
51280
  }
@@ -51401,22 +51635,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
51401
51635
  }
51402
51636
 
51403
51637
  // src/git-sensor.ts
51404
- import { existsSync as existsSync14, readFileSync as readFileSync13, watch as fsWatch } from "node:fs";
51405
- import { join as join18 } from "node:path";
51638
+ import { existsSync as existsSync15, readFileSync as readFileSync14, watch as fsWatch } from "node:fs";
51639
+ import { join as join19 } from "node:path";
51406
51640
  function readFirstLine(path2) {
51407
51641
  try {
51408
- return readFileSync13(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51642
+ return readFileSync14(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51409
51643
  } catch {
51410
51644
  return null;
51411
51645
  }
51412
51646
  }
51413
51647
  function readGitRefState(gitDir) {
51414
- const head2 = readFirstLine(join18(gitDir, "HEAD"));
51648
+ const head2 = readFirstLine(join19(gitDir, "HEAD"));
51415
51649
  const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
51416
51650
  const branch = m ? m[1] : null;
51417
51651
  let sha2 = null;
51418
51652
  if (branch) {
51419
- sha2 = readFirstLine(join18(gitDir, "refs", "heads", branch));
51653
+ sha2 = readFirstLine(join19(gitDir, "refs", "heads", branch));
51420
51654
  if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
51421
51655
  } else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
51422
51656
  sha2 = head2;
@@ -51424,13 +51658,13 @@ function readGitRefState(gitDir) {
51424
51658
  return {
51425
51659
  branch,
51426
51660
  sha: sha2,
51427
- mergeHeadExists: existsSync14(join18(gitDir, "MERGE_HEAD")),
51428
- origHeadExists: existsSync14(join18(gitDir, "ORIG_HEAD"))
51661
+ mergeHeadExists: existsSync15(join19(gitDir, "MERGE_HEAD")),
51662
+ origHeadExists: existsSync15(join19(gitDir, "ORIG_HEAD"))
51429
51663
  };
51430
51664
  }
51431
51665
  function shaFromPackedRefs(gitDir, ref) {
51432
51666
  try {
51433
- for (const line of readFileSync13(join18(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
51667
+ for (const line of readFileSync14(join19(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
51434
51668
  const [sha2, name2] = line.split(/\s+/);
51435
51669
  if (name2 === ref && sha2) return sha2;
51436
51670
  }
@@ -51464,7 +51698,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
51464
51698
  const settle = () => {
51465
51699
  if (timer) clearTimeout(timer);
51466
51700
  timer = setTimeout(() => {
51467
- if (existsSync14(join18(gitDir, "index.lock"))) {
51701
+ if (existsSync15(join19(gitDir, "index.lock"))) {
51468
51702
  settle();
51469
51703
  return;
51470
51704
  }
@@ -51475,7 +51709,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
51475
51709
  }, debounceMs);
51476
51710
  };
51477
51711
  for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
51478
- const p = join18(gitDir, sub);
51712
+ const p = join19(gitDir, sub);
51479
51713
  try {
51480
51714
  watchers.push(fsWatch(p, settle));
51481
51715
  } catch {
@@ -51700,21 +51934,21 @@ var TelemetryRecorder = class {
51700
51934
 
51701
51935
  // src/skills.ts
51702
51936
  import {
51703
- existsSync as existsSync15,
51937
+ existsSync as existsSync16,
51704
51938
  mkdirSync as mkdirSync6,
51705
- readFileSync as readFileSync14,
51939
+ readFileSync as readFileSync15,
51706
51940
  readdirSync as readdirSync7,
51707
51941
  unlinkSync as unlinkSync2,
51708
- writeFileSync as writeFileSync12
51942
+ writeFileSync as writeFileSync13
51709
51943
  } from "node:fs";
51710
- import { basename as basename4, join as join19 } from "node:path";
51944
+ import { basename as basename4, join as join20 } from "node:path";
51711
51945
  function skillFileName(id) {
51712
51946
  return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
51713
51947
  }
51714
51948
  function readSkillManifest(manifestPath) {
51715
- if (!existsSync15(manifestPath)) return [];
51949
+ if (!existsSync16(manifestPath)) return [];
51716
51950
  try {
51717
- const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51951
+ const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
51718
51952
  return (parsed.skills ?? []).map((s) => ({
51719
51953
  title: s.title ?? "",
51720
51954
  layer: s.layer ?? "technique",
@@ -51738,7 +51972,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51738
51972
  for (const s of res.skills) {
51739
51973
  const fileName = skillFileName(s.id);
51740
51974
  keep.add(fileName);
51741
- writeFileSync12(join19(paths.skillsDir, fileName), s.markdown, "utf8");
51975
+ writeFileSync13(join20(paths.skillsDir, fileName), s.markdown, "utf8");
51742
51976
  rows.push({
51743
51977
  id: s.id,
51744
51978
  title: s.title,
@@ -51751,7 +51985,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51751
51985
  const fileName = skillFileName(p.id);
51752
51986
  if (keep.has(fileName)) continue;
51753
51987
  keep.add(fileName);
51754
- writeFileSync12(join19(paths.skillsDir, fileName), p.markdown, "utf8");
51988
+ writeFileSync13(join20(paths.skillsDir, fileName), p.markdown, "utf8");
51755
51989
  rows.push({
51756
51990
  id: p.id,
51757
51991
  title: p.title,
@@ -51765,13 +51999,13 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51765
51999
  if (!f.endsWith(".md")) continue;
51766
52000
  if (keep.has(basename4(f))) continue;
51767
52001
  try {
51768
- unlinkSync2(join19(paths.skillsDir, f));
52002
+ unlinkSync2(join20(paths.skillsDir, f));
51769
52003
  pruned++;
51770
52004
  } catch {
51771
52005
  }
51772
52006
  }
51773
52007
  rows.sort((a, b) => a.id.localeCompare(b.id));
51774
- writeFileSync12(
52008
+ writeFileSync13(
51775
52009
  paths.skillsManifest,
51776
52010
  JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
51777
52011
  "utf8"
@@ -51783,22 +52017,22 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51783
52017
  init_src2();
51784
52018
  import {
51785
52019
  cpSync,
51786
- existsSync as existsSync16,
52020
+ existsSync as existsSync17,
51787
52021
  lstatSync,
51788
52022
  mkdirSync as mkdirSync7,
51789
- readFileSync as readFileSync15,
52023
+ readFileSync as readFileSync16,
51790
52024
  readdirSync as readdirSync8,
51791
52025
  rmSync as rmSync2,
51792
52026
  symlinkSync,
51793
- writeFileSync as writeFileSync13
52027
+ writeFileSync as writeFileSync14
51794
52028
  } from "node:fs";
51795
- import { join as join20 } from "node:path";
52029
+ import { join as join21 } from "node:path";
51796
52030
  var SKILL_NS = "errata-";
51797
52031
  var HARNESS_SKILL_DIRS = [
51798
- { configDir: ".claude", skillsDir: join20(".claude", "skills") },
52032
+ { configDir: ".claude", skillsDir: join21(".claude", "skills") },
51799
52033
  // Cursor adopted the standard; its exact project dir is still moving — kept
51800
52034
  // best-effort and gated on `.cursor/` presence so we never create it blind.
51801
- { configDir: ".cursor", skillsDir: join20(".cursor", "skills") }
52035
+ { configDir: ".cursor", skillsDir: join21(".cursor", "skills") }
51802
52036
  ];
51803
52037
  function skillSlug(title, id) {
51804
52038
  const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
@@ -51843,12 +52077,12 @@ function skillCiteHandle(s) {
51843
52077
  return priorHandle({ id: s.id, description: s.title });
51844
52078
  }
51845
52079
  function reconcileNamespaced(dir, keep) {
51846
- if (!existsSync16(dir)) return 0;
52080
+ if (!existsSync17(dir)) return 0;
51847
52081
  let pruned = 0;
51848
52082
  for (const name2 of readdirSync8(dir)) {
51849
52083
  if (!name2.startsWith(SKILL_NS) || keep.has(name2)) continue;
51850
52084
  try {
51851
- rmSync2(join20(dir, name2), { recursive: true, force: true });
52085
+ rmSync2(join21(dir, name2), { recursive: true, force: true });
51852
52086
  pruned++;
51853
52087
  } catch {
51854
52088
  }
@@ -51857,7 +52091,7 @@ function reconcileNamespaced(dir, keep) {
51857
52091
  }
51858
52092
  function linkOrCopy(linkPath, target) {
51859
52093
  try {
51860
- if (existsSync16(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
52094
+ if (existsSync17(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51861
52095
  } catch {
51862
52096
  }
51863
52097
  try {
@@ -51878,7 +52112,7 @@ function safeLstat(p) {
51878
52112
  }
51879
52113
  }
51880
52114
  function emitAndProjectSkills(root, skills) {
51881
- const agentsSkillsDir = join20(root, ".agents", "skills");
52115
+ const agentsSkillsDir = join21(root, ".agents", "skills");
51882
52116
  mkdirSync7(agentsSkillsDir, { recursive: true });
51883
52117
  const slugs = [];
51884
52118
  const keep = /* @__PURE__ */ new Set();
@@ -51886,7 +52120,7 @@ function emitAndProjectSkills(root, skills) {
51886
52120
  for (const s of skills) {
51887
52121
  let body2;
51888
52122
  try {
51889
- body2 = readFileSync15(s.bodyPath, "utf8");
52123
+ body2 = readFileSync16(s.bodyPath, "utf8");
51890
52124
  } catch {
51891
52125
  continue;
51892
52126
  }
@@ -51895,9 +52129,9 @@ function emitAndProjectSkills(root, skills) {
51895
52129
  keep.add(slug2);
51896
52130
  slugs.push(slug2);
51897
52131
  const description = deriveDescription(s.title, s.layer, body2);
51898
- mkdirSync7(join20(agentsSkillsDir, slug2), { recursive: true });
51899
- writeFileSync13(
51900
- join20(agentsSkillsDir, slug2, "SKILL.md"),
52132
+ mkdirSync7(join21(agentsSkillsDir, slug2), { recursive: true });
52133
+ writeFileSync14(
52134
+ join21(agentsSkillsDir, slug2, "SKILL.md"),
51901
52135
  renderSkillMd(slug2, description, body2, skillCiteHandle(s)),
51902
52136
  "utf8"
51903
52137
  );
@@ -51906,11 +52140,11 @@ function emitAndProjectSkills(root, skills) {
51906
52140
  reconcileNamespaced(agentsSkillsDir, keep);
51907
52141
  let projected = 0;
51908
52142
  for (const h of HARNESS_SKILL_DIRS) {
51909
- if (!existsSync16(join20(root, h.configDir))) continue;
51910
- const dir = join20(root, h.skillsDir);
52143
+ if (!existsSync17(join21(root, h.configDir))) continue;
52144
+ const dir = join21(root, h.skillsDir);
51911
52145
  mkdirSync7(dir, { recursive: true });
51912
52146
  for (const slug2 of slugs) {
51913
- linkOrCopy(join20(dir, slug2), join20(agentsSkillsDir, slug2));
52147
+ linkOrCopy(join21(dir, slug2), join21(agentsSkillsDir, slug2));
51914
52148
  projected++;
51915
52149
  }
51916
52150
  reconcileNamespaced(dir, keep);
@@ -51919,15 +52153,15 @@ function emitAndProjectSkills(root, skills) {
51919
52153
  return { slugs, emitted, projected };
51920
52154
  }
51921
52155
  function emitInputsFromManifest(erretaDir, manifestPath) {
51922
- if (!existsSync16(manifestPath)) return [];
52156
+ if (!existsSync17(manifestPath)) return [];
51923
52157
  try {
51924
- const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
52158
+ const parsed = JSON.parse(readFileSync16(manifestPath, "utf8"));
51925
52159
  return (parsed.skills ?? []).filter((s) => Boolean(s.id && s.file)).map((s) => ({
51926
52160
  id: s.id,
51927
52161
  title: s.title ?? s.id,
51928
52162
  layer: s.layer ?? "technique",
51929
52163
  confidence: s.confidence ?? 0,
51930
- bodyPath: join20(erretaDir, s.file)
52164
+ bodyPath: join21(erretaDir, s.file)
51931
52165
  }));
51932
52166
  } catch {
51933
52167
  return [];
@@ -51941,17 +52175,17 @@ var GITIGNORE_LINES = [
51941
52175
  ".cursor/skills/errata-*/"
51942
52176
  ];
51943
52177
  function ensureSkillGitignore(root) {
51944
- const path2 = join20(root, ".gitignore");
52178
+ const path2 = join21(root, ".gitignore");
51945
52179
  let current = "";
51946
52180
  try {
51947
- current = existsSync16(path2) ? readFileSync15(path2, "utf8") : "";
52181
+ current = existsSync17(path2) ? readFileSync16(path2, "utf8") : "";
51948
52182
  } catch {
51949
52183
  return;
51950
52184
  }
51951
52185
  if (current.includes(GITIGNORE_MARK)) return;
51952
52186
  const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
51953
52187
  try {
51954
- writeFileSync13(path2, `${current}${prefix}
52188
+ writeFileSync14(path2, `${current}${prefix}
51955
52189
  ${GITIGNORE_LINES.join("\n")}
51956
52190
  `, "utf8");
51957
52191
  } catch {
@@ -52012,20 +52246,20 @@ init_paths();
52012
52246
  // src/profile.ts
52013
52247
  init_src2();
52014
52248
  init_paths();
52015
- import { existsSync as existsSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
52249
+ import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "node:fs";
52016
52250
  import { createHash as createHash12 } from "node:crypto";
52017
- import { join as join22 } from "node:path";
52251
+ import { join as join23 } from "node:path";
52018
52252
 
52019
52253
  // src/git-remote.ts
52020
52254
  init_src();
52021
- import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
52022
- import { isAbsolute as isAbsolute3, join as join21, resolve as resolve5 } from "node:path";
52255
+ import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync4 } from "node:fs";
52256
+ import { isAbsolute as isAbsolute3, join as join22, resolve as resolve5 } from "node:path";
52023
52257
  function resolveGitDir(root) {
52024
- const dotGit = join21(root, ".git");
52258
+ const dotGit = join22(root, ".git");
52025
52259
  try {
52026
52260
  const st = statSync4(dotGit);
52027
52261
  if (st.isDirectory()) return dotGit;
52028
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync16(dotGit, "utf8"));
52262
+ const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync17(dotGit, "utf8"));
52029
52263
  if (!m) return null;
52030
52264
  const dir = m[1];
52031
52265
  return isAbsolute3(dir) ? dir : resolve5(root, dir);
@@ -52034,22 +52268,22 @@ function resolveGitDir(root) {
52034
52268
  }
52035
52269
  }
52036
52270
  function gitConfigPath(gitDir) {
52037
- const commondirFile = join21(gitDir, "commondir");
52038
- if (existsSync17(commondirFile)) {
52039
- const common = readFileSync16(commondirFile, "utf8").trim();
52271
+ const commondirFile = join22(gitDir, "commondir");
52272
+ if (existsSync18(commondirFile)) {
52273
+ const common = readFileSync17(commondirFile, "utf8").trim();
52040
52274
  const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
52041
- return join21(commonDir, "config");
52275
+ return join22(commonDir, "config");
52042
52276
  }
52043
- return join21(gitDir, "config");
52277
+ return join22(gitDir, "config");
52044
52278
  }
52045
52279
  function readRemotes(root) {
52046
52280
  const gitDir = resolveGitDir(root);
52047
52281
  if (!gitDir) return [];
52048
52282
  const cfgPath = gitConfigPath(gitDir);
52049
- if (!existsSync17(cfgPath)) return [];
52283
+ if (!existsSync18(cfgPath)) return [];
52050
52284
  let txt;
52051
52285
  try {
52052
- txt = readFileSync16(cfgPath, "utf8");
52286
+ txt = readFileSync17(cfgPath, "utf8");
52053
52287
  } catch {
52054
52288
  return [];
52055
52289
  }
@@ -52085,13 +52319,13 @@ function refreshRepoLocator(root, profile) {
52085
52319
  }
52086
52320
  function loadProfile(root) {
52087
52321
  const p = workspacePaths(root);
52088
- if (!existsSync18(p.workspaceJson)) return null;
52089
- return JSON.parse(readFileSync17(p.workspaceJson, "utf8"));
52322
+ if (!existsSync19(p.workspaceJson)) return null;
52323
+ return JSON.parse(readFileSync18(p.workspaceJson, "utf8"));
52090
52324
  }
52091
52325
  function saveProfile(root, profile) {
52092
52326
  const p = workspacePaths(root);
52093
52327
  ensureDir(p.configDir);
52094
- writeFileSync14(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
52328
+ writeFileSync15(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
52095
52329
  }
52096
52330
  function autodetectProfile(root) {
52097
52331
  const id = workspaceId(root);
@@ -52099,10 +52333,10 @@ function autodetectProfile(root) {
52099
52333
  const p = emptyProfile(id, name2);
52100
52334
  const locator = detectRepoLocator(root);
52101
52335
  if (locator) p.repoLocator = locator;
52102
- const pkgPath = join22(root, "package.json");
52103
- if (existsSync18(pkgPath)) {
52336
+ const pkgPath = join23(root, "package.json");
52337
+ if (existsSync19(pkgPath)) {
52104
52338
  try {
52105
- const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
52339
+ const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
52106
52340
  p.languages.push("typescript", "javascript");
52107
52341
  const nodeVer = pkg.engines?.node ?? "node";
52108
52342
  p.stack.push(`node@${nodeVer}`);
@@ -52123,10 +52357,10 @@ function autodetectProfile(root) {
52123
52357
  } catch {
52124
52358
  }
52125
52359
  }
52126
- const pyproject = join22(root, "pyproject.toml");
52127
- if (existsSync18(pyproject)) {
52360
+ const pyproject = join23(root, "pyproject.toml");
52361
+ if (existsSync19(pyproject)) {
52128
52362
  try {
52129
- const txt = readFileSync17(pyproject, "utf8");
52363
+ const txt = readFileSync18(pyproject, "utf8");
52130
52364
  const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
52131
52365
  p.languages.push("python");
52132
52366
  p.stack.push(`python@${py ?? "3"}`);
@@ -52137,16 +52371,16 @@ function autodetectProfile(root) {
52137
52371
  } catch {
52138
52372
  }
52139
52373
  }
52140
- const reqs = join22(root, "requirements.txt");
52141
- if (existsSync18(reqs)) {
52374
+ const reqs = join23(root, "requirements.txt");
52375
+ if (existsSync19(reqs)) {
52142
52376
  if (!p.languages.includes("python")) p.languages.push("python");
52143
52377
  if (!p.stack.includes("python@3")) p.stack.push("python@3");
52144
52378
  }
52145
- if (existsSync18(join22(root, "go.mod"))) {
52379
+ if (existsSync19(join23(root, "go.mod"))) {
52146
52380
  p.languages.push("go");
52147
52381
  p.stack.push("go");
52148
52382
  }
52149
- if (existsSync18(join22(root, "Cargo.toml"))) {
52383
+ if (existsSync19(join23(root, "Cargo.toml"))) {
52150
52384
  p.languages.push("rust");
52151
52385
  p.stack.push("rust");
52152
52386
  }
@@ -52156,17 +52390,17 @@ function autodetectProfile(root) {
52156
52390
  }
52157
52391
 
52158
52392
  // src/witness-queue.ts
52159
- import { readFileSync as readFileSync18, renameSync as renameSync2, writeFileSync as writeFileSync15 } from "node:fs";
52160
- import { dirname as dirname9, join as join23 } from "node:path";
52393
+ import { readFileSync as readFileSync19, renameSync as renameSync2, writeFileSync as writeFileSync16 } from "node:fs";
52394
+ import { dirname as dirname9, join as join24 } from "node:path";
52161
52395
  var WITNESS_QUEUE_CAP = 500;
52162
52396
  var WITNESS_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
52163
52397
  var WITNESS_MAX_ATTEMPTS = 5;
52164
52398
  function witnessQueuePath(workspaceConfigDir) {
52165
- return join23(workspaceConfigDir, "witness-queue.json");
52399
+ return join24(workspaceConfigDir, "witness-queue.json");
52166
52400
  }
52167
52401
  function loadWitnessQueue(path2) {
52168
52402
  try {
52169
- const raw2 = JSON.parse(readFileSync18(path2, "utf8"));
52403
+ const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
52170
52404
  if (!Array.isArray(raw2)) return [];
52171
52405
  return raw2.filter(
52172
52406
  (w) => !!w && typeof w === "object" && typeof w.nodeId === "string" && typeof w.witnessKey === "string"
@@ -52177,8 +52411,8 @@ function loadWitnessQueue(path2) {
52177
52411
  }
52178
52412
  function saveWitnessQueue(path2, queue) {
52179
52413
  try {
52180
- const tmp = join23(dirname9(path2), `.${Date.now()}.witness-queue.tmp`);
52181
- writeFileSync15(tmp, JSON.stringify(queue), "utf8");
52414
+ const tmp = join24(dirname9(path2), `.${Date.now()}.witness-queue.tmp`);
52415
+ writeFileSync16(tmp, JSON.stringify(queue), "utf8");
52182
52416
  renameSync2(tmp, path2);
52183
52417
  } catch {
52184
52418
  }
@@ -52458,7 +52692,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52458
52692
  }
52459
52693
 
52460
52694
  // src/engine.ts
52461
- var DAEMON_VERSION = true ? "2.0.2-dev.227" : "2.0.0-alpha.0";
52695
+ var DAEMON_VERSION = true ? "2.0.2-dev.247" : "2.0.0-alpha.0";
52462
52696
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52463
52697
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52464
52698
  var GIT_OP_MUTE_MS = 4e3;
@@ -52468,7 +52702,7 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
52468
52702
  function appendIdentityAudit(path2, record2, line) {
52469
52703
  if (!record2.accepted && record2.score <= 0) return;
52470
52704
  try {
52471
- if (existsSync19(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52705
+ if (existsSync20(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52472
52706
  renameSync3(path2, `${path2}.1`);
52473
52707
  }
52474
52708
  appendFileSync2(path2, line);
@@ -52478,7 +52712,7 @@ function appendIdentityAudit(path2, record2, line) {
52478
52712
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
52479
52713
  function loadTurnCursors(path2) {
52480
52714
  try {
52481
- const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
52715
+ const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52482
52716
  return new Map(
52483
52717
  Object.entries(raw2).map(([k, v]) => [k, typeof v === "string" ? v : String(v?.uuid ?? "")])
52484
52718
  );
@@ -52488,7 +52722,7 @@ function loadTurnCursors(path2) {
52488
52722
  }
52489
52723
  function loadTurnOffsets(path2) {
52490
52724
  try {
52491
- const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
52725
+ const raw2 = JSON.parse(readFileSync20(path2, "utf8"));
52492
52726
  const out2 = /* @__PURE__ */ new Map();
52493
52727
  for (const [k, v] of Object.entries(raw2)) {
52494
52728
  const off = typeof v === "object" && v !== null ? v.offset : void 0;
@@ -52504,7 +52738,7 @@ function saveTurnCursors(path2, cursors, offsets) {
52504
52738
  const merged = {};
52505
52739
  for (const [k, uuid3] of cursors) merged[k] = { uuid: uuid3, offset: offsets.get(k) ?? 0 };
52506
52740
  for (const [k, offset] of offsets) if (!merged[k]) merged[k] = { uuid: "", offset };
52507
- writeFileSync16(path2, JSON.stringify(merged), "utf8");
52741
+ writeFileSync17(path2, JSON.stringify(merged), "utf8");
52508
52742
  } catch {
52509
52743
  }
52510
52744
  }
@@ -52526,7 +52760,7 @@ function gitSourceWatchTargets(root) {
52526
52760
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
52527
52761
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
52528
52762
  );
52529
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join24(root, d) + sep4));
52763
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join25(root, d) + sep4));
52530
52764
  } catch {
52531
52765
  }
52532
52766
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -52538,19 +52772,19 @@ function gitSourceWatchTargets(root) {
52538
52772
  if (!f.startsWith(prefix)) continue;
52539
52773
  const rest2 = f.slice(prefix.length);
52540
52774
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
52541
- else targets.add(join24(root, f));
52775
+ else targets.add(join25(root, f));
52542
52776
  }
52543
52777
  for (const c of children) {
52544
- if (IGNORED_PATH.test(join24(root, c) + sep4)) continue;
52778
+ if (IGNORED_PATH.test(join25(root, c) + sep4)) continue;
52545
52779
  if (hasIgnoredChild(c)) addUnder(c);
52546
- else targets.add(join24(root, c));
52780
+ else targets.add(join25(root, c));
52547
52781
  }
52548
52782
  };
52549
52783
  addUnder("");
52550
52784
  if (targets.size > 0) return [...targets];
52551
52785
  } catch {
52552
52786
  }
52553
- return readdirSync9(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join24(root, String(e.name)) + sep4)).map((e) => join24(root, String(e.name)));
52787
+ 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)));
52554
52788
  }
52555
52789
  function createWorkspaceEngine(opts) {
52556
52790
  const paths = workspacePaths(opts.workspaceRoot);
@@ -52708,7 +52942,7 @@ function createWorkspaceEngine(opts) {
52708
52942
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
52709
52943
  let episodeId2;
52710
52944
  if (srcPaths.length > 0) {
52711
- const abs = srcPaths.map((p) => join24(opts.workspaceRoot, p));
52945
+ const abs = srcPaths.map((p) => join25(opts.workspaceRoot, p));
52712
52946
  try {
52713
52947
  const r = await runReindexPass(
52714
52948
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -52744,8 +52978,8 @@ function createWorkspaceEngine(opts) {
52744
52978
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
52745
52979
  );
52746
52980
  };
52747
- const gitDir = join24(opts.workspaceRoot, ".git");
52748
- if (existsSync19(gitDir)) {
52981
+ const gitDir = join25(opts.workspaceRoot, ".git");
52982
+ if (existsSync20(gitDir)) {
52749
52983
  stopGit = startGitSensor(gitDir, (ev) => {
52750
52984
  void handleGitEvent(ev).catch((err2) => {
52751
52985
  console.warn("[errata] git event handler failed:", err2);
@@ -52815,10 +53049,10 @@ function createWorkspaceEngine(opts) {
52815
53049
  });
52816
53050
  doneRender?.();
52817
53051
  writeContextFile(opts.workspaceRoot, body2);
52818
- const target = join24(opts.workspaceRoot, "AGENTS.md");
53052
+ const target = join25(opts.workspaceRoot, "AGENTS.md");
52819
53053
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
52820
53054
  if (elicit) {
52821
- writePrimingHandles(join24(paths.configDir, "priming-handles.json"), [
53055
+ writePrimingHandles(join25(paths.configDir, "priming-handles.json"), [
52822
53056
  ...snapshot.recentProblems.map((r) => r.node),
52823
53057
  // Resolved-band handles: the ✓ problem AND its Solution are citable
52824
53058
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -52909,6 +53143,17 @@ function createWorkspaceEngine(opts) {
52909
53143
  } catch (err2) {
52910
53144
  console.warn("[errata] anchor backfill failed:", err2);
52911
53145
  }
53146
+ try {
53147
+ const r = repairInvalidEdges(store, { configDir: paths.configDir, now: Date.now() });
53148
+ if (!r.skipped && r.invalid > 0) {
53149
+ console.log(
53150
+ `[errata] edge repair: ${r.retyped} edge(s) retyped, ${r.closed} closed (${Object.entries(r.byType).map(([t, n]) => `${t}x${n}`).join(", ")})`
53151
+ );
53152
+ refreshContextNow();
53153
+ }
53154
+ } catch (err2) {
53155
+ console.warn("[errata] edge repair failed:", err2);
53156
+ }
52912
53157
  try {
52913
53158
  const c = backfillConstraintKind(store, {
52914
53159
  root: opts.workspaceRoot,
@@ -53023,7 +53268,7 @@ function createWorkspaceEngine(opts) {
53023
53268
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
53024
53269
  });
53025
53270
  };
53026
- const turnCursorPath = join24(paths.configDir, "turn-cursors.json");
53271
+ const turnCursorPath = join25(paths.configDir, "turn-cursors.json");
53027
53272
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
53028
53273
  const turnOffset = loadTurnOffsets(turnCursorPath);
53029
53274
  const sessionLastProblem = /* @__PURE__ */ new Map();
@@ -53047,7 +53292,7 @@ function createWorkspaceEngine(opts) {
53047
53292
  const t = Date.now();
53048
53293
  let processedTurns = 0;
53049
53294
  const elicit = isEdgeElicitationEnabled();
53050
- const handleMap = elicit ? readPrimingHandles(join24(paths.configDir, "priming-handles.json")) : {};
53295
+ const handleMap = elicit ? readPrimingHandles(join25(paths.configDir, "priming-handles.json")) : {};
53051
53296
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
53052
53297
  const toRel = (abs) => {
53053
53298
  const p = abs.replace(/\\/g, "/");
@@ -53356,9 +53601,7 @@ function createWorkspaceEngine(opts) {
53356
53601
  if (targetId === pid) continue;
53357
53602
  const target = store.getNode(targetId);
53358
53603
  if (!target) continue;
53359
- const fallback = typePriorEdge("Problem", target.label);
53360
- const type = target.label === "Pattern" ? "INSTANCE_OF" : target.label === "Problem" ? "MATCHES" : fallback === "SUPERSEDES" ? "RELATES_TO" : fallback;
53361
- mintCiteEdge(pid, targetId, type, inst.evidence === "witnessed" ? 0.4 : 0.3, { instanceCite: true, evidence: inst.evidence });
53604
+ mintCiteEdge(pid, targetId, citeEdgeType(target.label), inst.evidence === "witnessed" ? 0.4 : 0.3, { instanceCite: true, evidence: inst.evidence });
53362
53605
  }
53363
53606
  }
53364
53607
  for (const tf of plan.transfers) {
@@ -53658,7 +53901,7 @@ function createWorkspaceEngine(opts) {
53658
53901
  try {
53659
53902
  const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
53660
53903
  emitAndProjectSkills(opts.workspaceRoot, inputs);
53661
- writePrimingHandles(join24(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
53904
+ writePrimingHandles(join25(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
53662
53905
  } catch (err2) {
53663
53906
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
53664
53907
  }
@@ -53845,7 +54088,7 @@ function createWorkspaceEngine(opts) {
53845
54088
  console.log(
53846
54089
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
53847
54090
  );
53848
- const pending = existsSync19(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
54091
+ const pending = existsSync20(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
53849
54092
  return { uploaded: 0, failed: 0, remaining: pending };
53850
54093
  }
53851
54094
  try {
@@ -53932,7 +54175,7 @@ async function startDaemon(opts) {
53932
54175
  reviewUrl: () => webUiUrl + "/review"
53933
54176
  });
53934
54177
  const writeLockFile = (url2) => {
53935
- writeFileSync17(
54178
+ writeFileSync18(
53936
54179
  engine.paths.daemonLock,
53937
54180
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
53938
54181
  "utf8"
@@ -53975,7 +54218,7 @@ async function startDaemon(opts) {
53975
54218
  );
53976
54219
  await engine.stop();
53977
54220
  try {
53978
- if (existsSync20(engine.paths.daemonLock)) {
54221
+ if (existsSync21(engine.paths.daemonLock)) {
53979
54222
  }
53980
54223
  } catch {
53981
54224
  }
@@ -53992,16 +54235,16 @@ async function listenServer(fetchFn, port) {
53992
54235
 
53993
54236
  // src/registry.ts
53994
54237
  init_paths();
53995
- import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "node:fs";
53996
- import { join as join25 } from "node:path";
54238
+ import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
54239
+ import { join as join26 } from "node:path";
53997
54240
  function registryPath() {
53998
- return process.env["ERRATA_REGISTRY_PATH"] ?? join25(globalDir(), "workspaces.json");
54241
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join26(globalDir(), "workspaces.json");
53999
54242
  }
54000
54243
  function read() {
54001
54244
  const p = registryPath();
54002
- if (!existsSync21(p)) return { version: 1, workspaces: {} };
54245
+ if (!existsSync22(p)) return { version: 1, workspaces: {} };
54003
54246
  try {
54004
- const parsed = JSON.parse(readFileSync20(p, "utf8"));
54247
+ const parsed = JSON.parse(readFileSync21(p, "utf8"));
54005
54248
  return { version: 1, workspaces: parsed.workspaces ?? {} };
54006
54249
  } catch {
54007
54250
  return { version: 1, workspaces: {} };
@@ -54009,7 +54252,7 @@ function read() {
54009
54252
  }
54010
54253
  function write(reg) {
54011
54254
  ensureDir(globalDir());
54012
- writeFileSync18(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54255
+ writeFileSync19(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54013
54256
  }
54014
54257
  function registerWorkspace(profile, root, now = Date.now()) {
54015
54258
  const reg = read();
@@ -54026,7 +54269,7 @@ function pruneMissingWorkspaces() {
54026
54269
  const reg = read();
54027
54270
  const removed = [];
54028
54271
  for (const [id, entry] of Object.entries(reg.workspaces)) {
54029
- if (!existsSync21(entry.path)) {
54272
+ if (!existsSync22(entry.path)) {
54030
54273
  removed.push(entry);
54031
54274
  delete reg.workspaces[id];
54032
54275
  }
@@ -54035,13 +54278,13 @@ function pruneMissingWorkspaces() {
54035
54278
  return removed;
54036
54279
  }
54037
54280
  function workspaceStatus(entry) {
54038
- const missing = !existsSync21(entry.path);
54281
+ const missing = !existsSync22(entry.path);
54039
54282
  const lockPath = workspacePaths(entry.path).daemonLock;
54040
54283
  let running = false;
54041
54284
  let webUiUrl = null;
54042
- if (existsSync21(lockPath)) {
54285
+ if (existsSync22(lockPath)) {
54043
54286
  try {
54044
- const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
54287
+ const lock = JSON.parse(readFileSync21(lockPath, "utf8"));
54045
54288
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
54046
54289
  running = true;
54047
54290
  webUiUrl = lock.webUiUrl;
@@ -54069,7 +54312,7 @@ function pidAlive(pid) {
54069
54312
  // src/multi.ts
54070
54313
  init_dist();
54071
54314
  init_src4();
54072
- import { readFileSync as readFileSync23, unlinkSync as unlinkSync3, writeFileSync as writeFileSync19 } from "node:fs";
54315
+ import { readFileSync as readFileSync24, unlinkSync as unlinkSync3, writeFileSync as writeFileSync20 } from "node:fs";
54073
54316
 
54074
54317
  // src/principle-sync.ts
54075
54318
  init_src4();
@@ -54097,8 +54340,8 @@ init_reconcile();
54097
54340
 
54098
54341
  // src/lockfile-auto.ts
54099
54342
  init_src();
54100
- import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
54101
- import { join as join26 } from "node:path";
54343
+ import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
54344
+ import { join as join27 } from "node:path";
54102
54345
 
54103
54346
  // src/package-index.ts
54104
54347
  init_src();
@@ -54247,11 +54490,11 @@ function runLockfilePass(opts) {
54247
54490
  { file: "package-lock.json", parse: parsePackageLockJson }
54248
54491
  ];
54249
54492
  for (const c of candidates) {
54250
- const p = join26(opts.root, c.file);
54251
- if (!existsSync22(p)) continue;
54493
+ const p = join27(opts.root, c.file);
54494
+ if (!existsSync23(p)) continue;
54252
54495
  let sbom;
54253
54496
  try {
54254
- sbom = c.parse(readFileSync21(p, "utf8"));
54497
+ sbom = c.parse(readFileSync22(p, "utf8"));
54255
54498
  } catch {
54256
54499
  continue;
54257
54500
  }
@@ -54699,7 +54942,7 @@ var ConsolidateWorker = class {
54699
54942
  init_paths();
54700
54943
 
54701
54944
  // src/lock.ts
54702
- import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
54945
+ import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
54703
54946
  function isProcessAlive(pid) {
54704
54947
  if (!pid || pid <= 0) return false;
54705
54948
  try {
@@ -54710,9 +54953,9 @@ function isProcessAlive(pid) {
54710
54953
  }
54711
54954
  }
54712
54955
  function readDaemonLock(lockPath) {
54713
- if (!existsSync23(lockPath)) return null;
54956
+ if (!existsSync24(lockPath)) return null;
54714
54957
  try {
54715
- const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
54958
+ const lock = JSON.parse(readFileSync23(lockPath, "utf8"));
54716
54959
  return typeof lock.pid === "number" ? lock : null;
54717
54960
  } catch {
54718
54961
  return null;
@@ -54729,6 +54972,39 @@ init_config();
54729
54972
  // src/update.ts
54730
54973
  import { spawn as spawn2 } from "node:child_process";
54731
54974
  var PACKAGE_NAME = "@inerrata-corporation/errata";
54975
+ function parse3(v) {
54976
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?/.exec(v.trim());
54977
+ if (!m) return null;
54978
+ return {
54979
+ base: [Number(m[1]), Number(m[2]), Number(m[3])],
54980
+ pre: m[4] === void 0 ? null : m[4].split(/[.-]/).map((p) => /^\d+$/.test(p) ? Number(p) : p)
54981
+ };
54982
+ }
54983
+ function compareVersions(a, b) {
54984
+ const pa = parse3(a);
54985
+ const pb = parse3(b);
54986
+ if (!pa || !pb) return 0;
54987
+ const [aMaj, aMin, aPatch] = pa.base;
54988
+ const [bMaj, bMin, bPatch] = pb.base;
54989
+ if (aMaj !== bMaj) return aMaj - bMaj;
54990
+ if (aMin !== bMin) return aMin - bMin;
54991
+ if (aPatch !== bPatch) return aPatch - bPatch;
54992
+ if (pa.pre === null && pb.pre === null) return 0;
54993
+ if (pa.pre === null) return 1;
54994
+ if (pb.pre === null) return -1;
54995
+ for (let i2 = 0; i2 < Math.max(pa.pre.length, pb.pre.length); i2++) {
54996
+ const x = pa.pre[i2];
54997
+ const y = pb.pre[i2];
54998
+ if (x === void 0) return -1;
54999
+ if (y === void 0) return 1;
55000
+ if (x === y) continue;
55001
+ if (typeof x === "number" && typeof y === "number") return x - y;
55002
+ if (typeof x === "number") return -1;
55003
+ if (typeof y === "number") return 1;
55004
+ return x < y ? -1 : 1;
55005
+ }
55006
+ return 0;
55007
+ }
54732
55008
  async function latestPublished(channel, timeoutMs = 4e3) {
54733
55009
  const ctrl = new AbortController();
54734
55010
  const timer = setTimeout(() => ctrl.abort(), timeoutMs);
@@ -54748,10 +55024,14 @@ async function latestPublished(channel, timeoutMs = 4e3) {
54748
55024
  }
54749
55025
  async function checkForUpdate(channel) {
54750
55026
  const latest = await latestPublished(channel);
55027
+ const direction = latest === null ? 0 : compareVersions(latest, DAEMON_VERSION);
54751
55028
  return {
54752
55029
  current: DAEMON_VERSION,
54753
55030
  latest,
54754
- updateAvailable: latest !== null && latest !== DAEMON_VERSION
55031
+ // `compareVersions` returns 0 for unparseable input, so an unrecognizable tag
55032
+ // is treated as "nothing to do" rather than gambling on a direction.
55033
+ updateAvailable: latest !== null && direction > 0,
55034
+ downgradeAvailable: latest !== null && direction < 0
54755
55035
  };
54756
55036
  }
54757
55037
  var POLL_INTERVAL_MS = {
@@ -54954,12 +55234,12 @@ async function reanchorProject(opts) {
54954
55234
  }
54955
55235
 
54956
55236
  // src/adopt.ts
54957
- import { existsSync as existsSync24 } from "node:fs";
54958
- import { dirname as dirname10, join as join27 } from "node:path";
55237
+ import { existsSync as existsSync25 } from "node:fs";
55238
+ import { dirname as dirname10, join as join28 } from "node:path";
54959
55239
  function findGitRoot(absPath) {
54960
55240
  let dir = absPath;
54961
55241
  for (let depth = 0; depth < 64; depth++) {
54962
- if (existsSync24(join27(dir, ".git"))) return dir;
55242
+ if (existsSync25(join28(dir, ".git"))) return dir;
54963
55243
  const parent = dirname10(dir);
54964
55244
  if (parent === dir) return null;
54965
55245
  dir = parent;
@@ -55208,7 +55488,7 @@ async function startMultiDaemon(opts = {}) {
55208
55488
  void ambientLinkAll();
55209
55489
  app.route(`/ws/${rec.id}`, rec.webApp);
55210
55490
  try {
55211
- writeFileSync19(
55491
+ writeFileSync20(
55212
55492
  rec.engine.paths.daemonLock,
55213
55493
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
55214
55494
  "utf8"
@@ -55397,7 +55677,7 @@ async function startMultiDaemon(opts = {}) {
55397
55677
  baseUrl = `http://127.0.0.1:${port}`;
55398
55678
  try {
55399
55679
  ensureDir(globalDir());
55400
- writeFileSync19(
55680
+ writeFileSync20(
55401
55681
  lockPath,
55402
55682
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
55403
55683
  "utf8"
@@ -55406,7 +55686,7 @@ async function startMultiDaemon(opts = {}) {
55406
55686
  }
55407
55687
  for (const r of records) {
55408
55688
  try {
55409
- writeFileSync19(
55689
+ writeFileSync20(
55410
55690
  r.engine.paths.daemonLock,
55411
55691
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
55412
55692
  "utf8"
@@ -55733,6 +56013,11 @@ async function startMultiDaemon(opts = {}) {
55733
56013
  if (!local) continue;
55734
56014
  store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
55735
56015
  }
56016
+ if (p.blockedNodeIds?.length) {
56017
+ console.log(
56018
+ `[sync\u2192cloud] [${projectName}] context: ${p.blockedNodeIds.length} node(s) refused by an edge/CDN filter on content \u2014 left pending, will retry`
56019
+ );
56020
+ }
55736
56021
  noteFlushProgress(p.accepted, p.nodeIds.length, `context ${p.chunkIndex}/${p.totalChunks}`);
55737
56022
  }
55738
56023
  });
@@ -55762,9 +56047,18 @@ async function startMultiDaemon(opts = {}) {
55762
56047
  if (instances) {
55763
56048
  if (project) instances.projectId = project.projectId;
55764
56049
  await lane("instances", projectName, async () => {
56050
+ const blocked = /* @__PURE__ */ new Set();
55765
56051
  const res = await client.ingest(instances, {
55766
- onChunk: (p) => noteFlushProgress(p.accepted, p.nodeIds.length, `instances ${p.chunkIndex}/${p.totalChunks}`)
56052
+ onChunk: (p) => {
56053
+ for (const id of p.blockedNodeIds ?? []) blocked.add(id);
56054
+ noteFlushProgress(p.accepted, p.nodeIds.length, `instances ${p.chunkIndex}/${p.totalChunks}`);
56055
+ }
55767
56056
  });
56057
+ if (blocked.size > 0) {
56058
+ console.log(
56059
+ `[sync\u2192cloud] [${projectName}] instances: ${blocked.size} node(s) refused by an edge/CDN filter on content \u2014 left pending, will retry. First: ${store.getNode([...blocked][0])?.description.slice(0, 120) ?? [...blocked][0]}`
56060
+ );
56061
+ }
55768
56062
  uploaded += res.accepted;
55769
56063
  const seq = store.currentIngestSeq();
55770
56064
  const cloudIdByLocal = new Map(
@@ -55773,6 +56067,7 @@ async function startMultiDaemon(opts = {}) {
55773
56067
  for (const n of instances.nodes) {
55774
56068
  const local = store.getNode(n.id);
55775
56069
  if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
56070
+ if (blocked.has(n.id)) continue;
55776
56071
  const cloudNodeId = cloudIdByLocal.get(n.id);
55777
56072
  store.updateNode(n.id, {
55778
56073
  attrs: {
@@ -55889,7 +56184,7 @@ async function startMultiDaemon(opts = {}) {
55889
56184
  },
55890
56185
  async stop() {
55891
56186
  try {
55892
- const cur = readFileSync23(lockPath, "utf8");
56187
+ const cur = readFileSync24(lockPath, "utf8");
55893
56188
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
55894
56189
  } catch {
55895
56190
  }
@@ -56906,21 +57201,21 @@ async function cmdInit() {
56906
57201
  if (!skipHooks) {
56907
57202
  console.log("");
56908
57203
  console.log("installing harness hooks...");
56909
- const { existsSync: existsSync26 } = await import("node:fs");
56910
- const { join: join29 } = await import("node:path");
57204
+ const { existsSync: existsSync27 } = await import("node:fs");
57205
+ const { join: join30 } = await import("node:path");
56911
57206
  try {
56912
57207
  await installClaudeHooks(port);
56913
57208
  } catch (err2) {
56914
57209
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
56915
57210
  }
56916
- if (existsSync26(join29(ROOT, ".cursor"))) {
57211
+ if (existsSync27(join30(ROOT, ".cursor"))) {
56917
57212
  try {
56918
57213
  await installCursorMcpConfig();
56919
57214
  } catch (err2) {
56920
57215
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
56921
57216
  }
56922
57217
  }
56923
- if (existsSync26(join29(ROOT, ".codex"))) {
57218
+ if (existsSync27(join30(ROOT, ".codex"))) {
56924
57219
  try {
56925
57220
  await installCodexHooks(port);
56926
57221
  } catch (err2) {
@@ -57077,9 +57372,9 @@ async function cmdStatus() {
57077
57372
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
57078
57373
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
57079
57374
  }
57080
- console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57081
- console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57082
- if (existsSync25(paths.castalia)) {
57375
+ console.log(` graph db: ${existsSync26(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57376
+ console.log(` event log: ${existsSync26(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57377
+ if (existsSync26(paths.castalia)) {
57083
57378
  try {
57084
57379
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57085
57380
  const store = openGraphStore2({ path: paths.castalia });
@@ -57095,6 +57390,16 @@ async function cmdStatus() {
57095
57390
  }
57096
57391
  }
57097
57392
  console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
57393
+ const rejections = store.edgeRejections();
57394
+ if (rejections.length > 0) {
57395
+ const refused = rejections.reduce((n, r) => n + r.count, 0);
57396
+ console.log(` edges: ${refused} refused by the ontology gate \u2014 a producer is emitting invalid edges`);
57397
+ for (const r of rejections.slice(0, 3)) {
57398
+ const mins = Math.max(0, Math.round((Date.now() - r.lastAt) / 6e4));
57399
+ const age = mins < 60 ? `${mins}m ago` : mins < 1440 ? `${Math.round(mins / 60)}h ago` : `${Math.round(mins / 1440)}d ago`;
57400
+ console.log(` ${r.count}x ${r.type} (last ${age})${r.sample ? ` \u2014 ${r.sample}` : ""}`);
57401
+ }
57402
+ }
57098
57403
  } finally {
57099
57404
  store.close();
57100
57405
  }
@@ -57214,9 +57519,11 @@ async function cmdUpdate(args2) {
57214
57519
  const cfg = loadConfig();
57215
57520
  let channel = cfg.updateChannel;
57216
57521
  let checkOnly = false;
57522
+ let allowDowngrade = false;
57217
57523
  for (let i2 = 0; i2 < args2.length; i2++) {
57218
57524
  const a = args2[i2];
57219
57525
  if (a === "--check") checkOnly = true;
57526
+ else if (a === "--allow-downgrade") allowDowngrade = true;
57220
57527
  else if (a === "--channel") {
57221
57528
  const v = args2[++i2];
57222
57529
  if (v === "dev" || v === "latest") channel = v;
@@ -57237,8 +57544,19 @@ async function cmdUpdate(args2) {
57237
57544
  return;
57238
57545
  }
57239
57546
  if (!status.updateAvailable) {
57240
- console.log(`errata is up to date \u2014 ${status.current} (channel: ${channel})`);
57241
- return;
57547
+ if (status.downgradeAvailable) {
57548
+ console.log(
57549
+ `channel '${channel}' points at ${status.latest}, which is OLDER than the running ${status.current}.`
57550
+ );
57551
+ console.log(` not installing \u2014 a downgrade past a store migration cannot open its own graph.`);
57552
+ console.log(` if this is a deliberate rollback: errata update --allow-downgrade`);
57553
+ if (!allowDowngrade) return;
57554
+ console.log(`
57555
+ --allow-downgrade given; proceeding.`);
57556
+ } else {
57557
+ console.log(`errata is up to date \u2014 ${status.current} (channel: ${channel})`);
57558
+ return;
57559
+ }
57242
57560
  }
57243
57561
  console.log(`update available: ${status.current} \u2192 ${status.latest} (channel: ${channel})`);
57244
57562
  if (checkOnly) {
@@ -57730,11 +58048,11 @@ function cmdInstallationProfile(args2) {
57730
58048
  }
57731
58049
  async function cmdReview() {
57732
58050
  const paths = workspacePaths(ROOT);
57733
- if (!existsSync25(paths.reviewQueue)) {
58051
+ if (!existsSync26(paths.reviewQueue)) {
57734
58052
  console.log("(review queue empty)");
57735
58053
  return;
57736
58054
  }
57737
- const queue = JSON.parse(readFileSync24(paths.reviewQueue, "utf8"));
58055
+ const queue = JSON.parse(readFileSync25(paths.reviewQueue, "utf8"));
57738
58056
  if (queue.length === 0) {
57739
58057
  console.log("(review queue empty)");
57740
58058
  return;
@@ -58405,7 +58723,7 @@ async function gatherRepo(store, ws) {
58405
58723
  };
58406
58724
  }
58407
58725
  async function gatherReportData(generatedAt) {
58408
- const { existsSync: existsSync26 } = await import("node:fs");
58726
+ const { existsSync: existsSync27 } = await import("node:fs");
58409
58727
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
58410
58728
  const cfg = loadConfig();
58411
58729
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -58413,7 +58731,7 @@ async function gatherReportData(generatedAt) {
58413
58731
  for (const ws of listWorkspaces()) {
58414
58732
  if (ws.missing) continue;
58415
58733
  const dbPath = workspacePaths(ws.path).castalia;
58416
- if (!existsSync26(dbPath)) continue;
58734
+ if (!existsSync27(dbPath)) continue;
58417
58735
  let store = null;
58418
58736
  try {
58419
58737
  store = openGraphStore2({ path: dbPath });
@@ -58444,7 +58762,7 @@ async function gatherReportData(generatedAt) {
58444
58762
  };
58445
58763
  }
58446
58764
  async function cmdReport(args2) {
58447
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync20 } = await import("node:fs");
58765
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync21 } = await import("node:fs");
58448
58766
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
58449
58767
  const includeFutureVerbs = args2.includes("--future-verbs");
58450
58768
  const now = /* @__PURE__ */ new Date();
@@ -58457,8 +58775,8 @@ async function cmdReport(args2) {
58457
58775
  const outDir = workspacePaths(ROOT).configDir;
58458
58776
  mkdirSync8(outDir, { recursive: true });
58459
58777
  const files = renderReport2(data, { includeFutureVerbs });
58460
- for (const f of files) writeFileSync20(join28(outDir, f.name), f.html, "utf8");
58461
- const indexPath = join28(outDir, "report.html");
58778
+ for (const f of files) writeFileSync21(join29(outDir, f.name), f.html, "utf8");
58779
+ const indexPath = join29(outDir, "report.html");
58462
58780
  console.log(`report \u2192 ${indexPath}`);
58463
58781
  console.log(
58464
58782
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -58576,15 +58894,15 @@ function hookRelayCommand(port, path2) {
58576
58894
  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 '{}'`;
58577
58895
  }
58578
58896
  async function installClaudeHooks(port) {
58579
- const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58580
- const { join: join29 } = await import("node:path");
58581
- const dir = join29(ROOT, ".claude");
58582
- if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58583
- const file2 = join29(dir, "settings.json");
58897
+ const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58898
+ const { join: join30 } = await import("node:path");
58899
+ const dir = join30(ROOT, ".claude");
58900
+ if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58901
+ const file2 = join30(dir, "settings.json");
58584
58902
  let settings = {};
58585
- if (existsSync26(file2)) {
58903
+ if (existsSync27(file2)) {
58586
58904
  try {
58587
- settings = JSON.parse(readFileSync25(file2, "utf8"));
58905
+ settings = JSON.parse(readFileSync26(file2, "utf8"));
58588
58906
  } catch {
58589
58907
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58590
58908
  process.exit(2);
@@ -58630,10 +58948,10 @@ async function installClaudeHooks(port) {
58630
58948
  dropErrata(list);
58631
58949
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
58632
58950
  }
58633
- writeFileSync20(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58951
+ writeFileSync21(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58634
58952
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
58635
58953
  await installClaudeMcpConfig();
58636
- const claudeMd = join29(ROOT, "CLAUDE.md");
58954
+ const claudeMd = join30(ROOT, "CLAUDE.md");
58637
58955
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
58638
58956
  if (recall.kind === "collision") {
58639
58957
  console.warn(
@@ -58645,15 +58963,15 @@ async function installClaudeHooks(port) {
58645
58963
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
58646
58964
  }
58647
58965
  async function installClaudeMcpConfig() {
58648
- const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58649
- const { join: join29, dirname: dirname11 } = await import("node:path");
58650
- const file2 = join29(ROOT, ".mcp.json");
58966
+ const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58967
+ const { join: join30, dirname: dirname11 } = await import("node:path");
58968
+ const file2 = join30(ROOT, ".mcp.json");
58651
58969
  const dir = dirname11(file2);
58652
- if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58970
+ if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58653
58971
  let cfg = {};
58654
- if (existsSync26(file2)) {
58972
+ if (existsSync27(file2)) {
58655
58973
  try {
58656
- cfg = JSON.parse(readFileSync25(file2, "utf8"));
58974
+ cfg = JSON.parse(readFileSync26(file2, "utf8"));
58657
58975
  } catch {
58658
58976
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58659
58977
  process.exit(2);
@@ -58661,21 +58979,21 @@ async function installClaudeMcpConfig() {
58661
58979
  }
58662
58980
  cfg.mcpServers ??= {};
58663
58981
  cfg.mcpServers["errata"] = errataMcpInvocation();
58664
- writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58982
+ writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58665
58983
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
58666
58984
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
58667
58985
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
58668
58986
  }
58669
58987
  async function installCursorMcpConfig() {
58670
- const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58671
- const { join: join29 } = await import("node:path");
58672
- const dir = join29(ROOT, ".cursor");
58673
- if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58674
- const file2 = join29(dir, "mcp.json");
58988
+ const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
58989
+ const { join: join30 } = await import("node:path");
58990
+ const dir = join30(ROOT, ".cursor");
58991
+ if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
58992
+ const file2 = join30(dir, "mcp.json");
58675
58993
  let cfg = {};
58676
- if (existsSync26(file2)) {
58994
+ if (existsSync27(file2)) {
58677
58995
  try {
58678
- cfg = JSON.parse(readFileSync25(file2, "utf8"));
58996
+ cfg = JSON.parse(readFileSync26(file2, "utf8"));
58679
58997
  } catch {
58680
58998
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58681
58999
  process.exit(2);
@@ -58683,7 +59001,7 @@ async function installCursorMcpConfig() {
58683
59001
  }
58684
59002
  cfg.mcpServers ??= {};
58685
59003
  cfg.mcpServers["errata"] = errataMcpInvocation();
58686
- writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
59004
+ writeFileSync21(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58687
59005
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
58688
59006
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
58689
59007
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -58691,16 +59009,16 @@ async function installCursorMcpConfig() {
58691
59009
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
58692
59010
  }
58693
59011
  async function installCodexHooks(port) {
58694
- const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58695
- const { join: join29 } = await import("node:path");
58696
- const dir = join29(ROOT, ".codex");
58697
- if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58698
- const file2 = join29(dir, "config.toml");
59012
+ const { mkdirSync: mkdirSync8, existsSync: existsSync27, readFileSync: readFileSync26, writeFileSync: writeFileSync21 } = await import("node:fs");
59013
+ const { join: join30 } = await import("node:path");
59014
+ const dir = join30(ROOT, ".codex");
59015
+ if (!existsSync27(dir)) mkdirSync8(dir, { recursive: true });
59016
+ const file2 = join30(dir, "config.toml");
58699
59017
  const BEGIN = `# >>> errata hooks (errata-managed)`;
58700
59018
  const END = `# <<< errata hooks`;
58701
59019
  let existing = "";
58702
- if (existsSync26(file2)) {
58703
- existing = readFileSync25(file2, "utf8");
59020
+ if (existsSync27(file2)) {
59021
+ existing = readFileSync26(file2, "utf8");
58704
59022
  const beginIdx = existing.indexOf(BEGIN);
58705
59023
  const endIdx = existing.indexOf(END);
58706
59024
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -58729,7 +59047,7 @@ ${END}
58729
59047
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
58730
59048
 
58731
59049
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
58732
- writeFileSync20(file2, final, "utf8");
59050
+ writeFileSync21(file2, final, "utf8");
58733
59051
  console.log(`installed Codex hooks \u2192 ${file2}`);
58734
59052
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
58735
59053
  console.log("");
@@ -59043,7 +59361,7 @@ async function cmdDash(args2) {
59043
59361
  await yieldToLoop2();
59044
59362
  try {
59045
59363
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
59046
- const res = bleedRules(join28(r.root, ".claude", "rules"), items);
59364
+ const res = bleedRules(join29(r.root, ".claude", "rules"), items);
59047
59365
  if (res.written || res.pruned) {
59048
59366
  console.log(
59049
59367
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")