@inerrata-corporation/errata 2.0.2-dev.125 → 2.0.2-dev.133

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
@@ -183,7 +183,14 @@ var init_castalia = __esm({
183
183
  "SOLVED_BY",
184
184
  "MITIGATES",
185
185
  "REPORTED_FAILURE",
186
- "CONTRADICTS"
186
+ "CONTRADICTS",
187
+ // Motif twin-faces (KN-twin-faces): failure-face motif → remedy-face motif
188
+ // across layers (AntiPattern/Weakness ↔ Technique/Pattern). Same
189
+ // problem→fix direction as FIXED_BY/SOLVED_BY. Minted by the nightly LLM
190
+ // twin-face pass — these are the near-identical cross-layer pairs the
191
+ // polarity gate (finding 5) correctly refuses to FUSE; the link carries
192
+ // what fusion can't.
193
+ "REMEDIED_BY"
187
194
  ];
188
195
  CONCEPTUAL_EDGES = [
189
196
  "INSTANCE_OF",
@@ -313,6 +320,10 @@ var init_castalia = __esm({
313
320
  CAUSED_BY: 3,
314
321
  FIXED_BY: 3,
315
322
  SOLVED_BY: 3,
323
+ // Twin-face link (failure motif → remedy motif, KN-twin-faces) — causal-grade
324
+ // but a notch below witnessed FIXED_BY/SOLVED_BY: the pairing is an LLM
325
+ // judgment over descriptions, not an agent-witnessed resolution.
326
+ REMEDIED_BY: 2.5,
316
327
  MANIFESTS_AS: 2,
317
328
  ESCALATES_TO: 1.5,
318
329
  AFFECTS: 1.2,
@@ -15605,6 +15616,15 @@ var init_wire = __esm({
15605
15616
  /** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
15606
15617
  description: external_exports.string().min(1).max(4e3),
15607
15618
  attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
15619
+ /** One-way origin key of the SESSION that minted this node (`ws_…`, a
15620
+ * truncated digest — never the raw session id). Stamped onto the created
15621
+ * node as `authoringSession`, which is the independence unit for the
15622
+ * evidence channels: the session that authored a claim may not corroborate
15623
+ * or refute it, while a DIFFERENT session on the same checkout may (alyssa,
15624
+ * 2026-07-31 — the workspace key made every witness on a single-checkout
15625
+ * deployment a self-corroboration). Optional + additive: absent leaves the
15626
+ * gate fail-open for that node, exactly today's behaviour. */
15627
+ originSession: external_exports.string().min(3).max(64).optional(),
15608
15628
  extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
15609
15629
  validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
15610
15630
  /** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
@@ -15720,6 +15740,9 @@ function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
15720
15740
  const path2 = normalizeSymbolPath(relPath);
15721
15741
  return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
15722
15742
  }
15743
+ function isMembraneSaltedId(id) {
15744
+ return typeof id === "string" && /^(orgn_|teamn_|projn_)/.test(id);
15745
+ }
15723
15746
  var init_project_symbol = __esm({
15724
15747
  "../../packages/shared/src/project-symbol.ts"() {
15725
15748
  "use strict";
@@ -15876,7 +15899,7 @@ var init_canonicalize = __esm({
15876
15899
 
15877
15900
  // ../../packages/shared/src/nlp/triage-canon.ts
15878
15901
  function canonicalizeToken(t) {
15879
- const base = t.trim().toLowerCase().replace(/[@\s].*$/, "");
15902
+ const base = t.trim().toLowerCase().replace(/(?!^)@.*$|\s.*$/, "");
15880
15903
  if (!base) return "";
15881
15904
  return resolveCanonicalId(base) ?? base;
15882
15905
  }
@@ -15969,6 +15992,7 @@ __export(src_exports, {
15969
15992
  isCodeLabel: () => isCodeLabel,
15970
15993
  isContextLabel: () => isContextLabel,
15971
15994
  isInferredSource: () => isInferredSource,
15995
+ isMembraneSaltedId: () => isMembraneSaltedId,
15972
15996
  isProjectOnlyEdgeType: () => isProjectOnlyEdgeType,
15973
15997
  isProjectOnlyNodeLabel: () => isProjectOnlyNodeLabel,
15974
15998
  isQuarantinedExtractionSource: () => isQuarantinedExtractionSource,
@@ -16264,6 +16288,9 @@ var init_profile = __esm({
16264
16288
  });
16265
16289
 
16266
16290
  // ../../packages/local-shared/src/daemon-wire.ts
16291
+ function formatDisposition(d) {
16292
+ return `${d.recorded} recorded \xB7 ${d.unmatched} unmatched (queued) \xB7 ${d.duplicate} dup \xB7 ${d.selfGated} self`;
16293
+ }
16267
16294
  var init_daemon_wire = __esm({
16268
16295
  "../../packages/local-shared/src/daemon-wire.ts"() {
16269
16296
  "use strict";
@@ -18168,6 +18195,9 @@ var init_problem_package_link = __esm({
18168
18195
  });
18169
18196
 
18170
18197
  // ../../packages/local-graph/src/design-problem.ts
18198
+ function isConstraintProblem(node2) {
18199
+ return node2.attrs["kind"] === "constraint";
18200
+ }
18171
18201
  function isPlaceholderStatement(statement) {
18172
18202
  const s = statement.trim();
18173
18203
  if (s.length < 8) return true;
@@ -18417,7 +18447,7 @@ function tokenJaccard(a, b) {
18417
18447
  for (const t of sa) if (sb.has(t)) inter++;
18418
18448
  return inter / (sa.size + sb.size - inter);
18419
18449
  }
18420
- function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts) {
18450
+ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
18421
18451
  const stmt = statement.trim();
18422
18452
  if (!ANCHORABLE_CODE.test(relPath)) return null;
18423
18453
  const file2 = resolveFileNode(store, relPath, workspaceId2);
@@ -18428,6 +18458,8 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
18428
18458
  const cand = store.getNode(e.from);
18429
18459
  if (!cand || cand.label !== "Problem" || cand.attrs["resolvedAt"]) continue;
18430
18460
  if (cand.id === selfId) continue;
18461
+ const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
18462
+ if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
18431
18463
  const score2 = tokenJaccard(stmt, cand.description);
18432
18464
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
18433
18465
  }
@@ -18462,6 +18494,7 @@ function priorsForFile(store, relPath) {
18462
18494
  }
18463
18495
  if (!file2) return null;
18464
18496
  const openProblems = [];
18497
+ const constraints = [];
18465
18498
  const seenProblem = /* @__PURE__ */ new Set();
18466
18499
  const related = /* @__PURE__ */ new Map();
18467
18500
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
@@ -18470,14 +18503,14 @@ function priorsForFile(store, relPath) {
18470
18503
  if (n.label === "Problem") {
18471
18504
  if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
18472
18505
  seenProblem.add(n.id);
18473
- openProblems.push(n);
18506
+ (isConstraintProblem(n) ? constraints : openProblems).push(n);
18474
18507
  }
18475
18508
  } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
18476
18509
  related.set(n.id, n);
18477
18510
  }
18478
18511
  }
18479
18512
  const solutionsByProblem = /* @__PURE__ */ new Map();
18480
- for (const p of openProblems) {
18513
+ for (const p of [...openProblems, ...constraints]) {
18481
18514
  for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
18482
18515
  const n = store.getNode(e.to);
18483
18516
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
@@ -18488,9 +18521,11 @@ function priorsForFile(store, relPath) {
18488
18521
  }
18489
18522
  }
18490
18523
  }
18491
- if (openProblems.length === 0 && related.size === 0) return null;
18492
- openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
18493
- return { file: file2, openProblems, related: [...related.values()], solutionsByProblem };
18524
+ if (openProblems.length === 0 && constraints.length === 0 && related.size === 0) return null;
18525
+ const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
18526
+ openProblems.sort(recentFirst);
18527
+ constraints.sort(recentFirst);
18528
+ return { file: file2, openProblems, constraints, related: [...related.values()], solutionsByProblem };
18494
18529
  }
18495
18530
  function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
18496
18531
  return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
@@ -18561,7 +18596,8 @@ function resolveDesignProblemById(store, id, fixNote, t) {
18561
18596
  }
18562
18597
  function closeDesignProblem(store, problemId, fixNote, t) {
18563
18598
  const p = store.getNode(problemId);
18564
- if (!p || p.label !== "Problem" || p.attrs["resolvedAt"]) return false;
18599
+ if (!p || p.label !== "Problem") return false;
18600
+ if (p.attrs["resolvedAs"]) return false;
18565
18601
  if (fixNote?.trim() && store.outEdges(problemId, ["SOLVED_BY"]).length === 0) {
18566
18602
  const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
18567
18603
  store.mergeNode(
@@ -18569,6 +18605,7 @@ function closeDesignProblem(store, problemId, fixNote, t) {
18569
18605
  );
18570
18606
  mergeEdge(store, problemId, solId, "SOLVED_BY", t);
18571
18607
  }
18608
+ if (p.attrs["resolvedAt"]) return false;
18572
18609
  store.updateNode(problemId, {
18573
18610
  attrs: { ...p.attrs, provisional: false, resolvedAt: t },
18574
18611
  lastUpdatedAt: t
@@ -18609,6 +18646,7 @@ function resolveDesignProblems(store, t) {
18609
18646
  for (const p of store.findNodesByLabel("Problem")) {
18610
18647
  if (!p.id.startsWith("dprob_")) continue;
18611
18648
  if (p.attrs["resolvedAt"]) continue;
18649
+ if (isConstraintProblem(p)) continue;
18612
18650
  let symName = "";
18613
18651
  let symRelPath;
18614
18652
  let edited = false;
@@ -18626,7 +18664,7 @@ function resolveDesignProblems(store, t) {
18626
18664
  if (store.outEdges(p.id, ["SOLVED_BY"]).length === 0) {
18627
18665
  const solId = `dfix_${digest({ problemId: p.id, edit: t })}`.slice(0, 56);
18628
18666
  store.mergeNode(
18629
- buildNode(solId, "Solution", `addressed by an edit to ${symName}`, t, {
18667
+ buildNode(solId, "Solution", `${AUTO_MINT_PREFIX}${symName}`, t, {
18630
18668
  source: "convo",
18631
18669
  provisional: false,
18632
18670
  // AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
@@ -18647,7 +18685,7 @@ function resolveDesignProblems(store, t) {
18647
18685
  }
18648
18686
  return resolved;
18649
18687
  }
18650
- var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES;
18688
+ var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
18651
18689
  var init_design_problem = __esm({
18652
18690
  "../../packages/local-graph/src/design-problem.ts"() {
18653
18691
  "use strict";
@@ -18689,6 +18727,7 @@ var init_design_problem = __esm({
18689
18727
  ]);
18690
18728
  ANCHORABLE_CODE = /\.(ts|tsx|js|mjs|cjs|py|go|rs|java|rb|c|cpp|h)$/;
18691
18729
  MAX_ANCHOR_FILES = 5;
18730
+ AUTO_MINT_PREFIX = "addressed by an edit to ";
18692
18731
  }
18693
18732
  });
18694
18733
 
@@ -18753,13 +18792,11 @@ function backfillLegacyAnchors(store, ts) {
18753
18792
  }
18754
18793
  return report;
18755
18794
  }
18756
- var AUTO_MINT_PREFIX;
18757
18795
  var init_anchor_backfill = __esm({
18758
18796
  "../../packages/local-graph/src/anchor-backfill.ts"() {
18759
18797
  "use strict";
18760
18798
  init_justification();
18761
18799
  init_design_problem();
18762
- AUTO_MINT_PREFIX = "addressed by an edit to ";
18763
18800
  }
18764
18801
  });
18765
18802
 
@@ -20520,6 +20557,7 @@ function mergeDuplicateProblems(store, opts) {
20520
20557
  if (consumed.has(b.id)) continue;
20521
20558
  const tb = tokens.get(b.id);
20522
20559
  if (Math.min(ta.size, tb.size) < minTokens) continue;
20560
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
20523
20561
  if (overlap(ta, tb) >= minOverlap) {
20524
20562
  cluster.push(b);
20525
20563
  consumed.add(b.id);
@@ -20576,6 +20614,7 @@ var init_problem_dedup = __esm({
20576
20614
  "use strict";
20577
20615
  init_src();
20578
20616
  init_src();
20617
+ init_design_problem();
20579
20618
  REDIRECT_EDGES = [
20580
20619
  "CAUSED_BY",
20581
20620
  "SOLVED_BY",
@@ -20782,6 +20821,7 @@ var init_principle_sync = __esm({
20782
20821
  // ../../packages/local-graph/src/index.ts
20783
20822
  var src_exports2 = {};
20784
20823
  __export(src_exports2, {
20824
+ AUTO_MINT_PREFIX: () => AUTO_MINT_PREFIX,
20785
20825
  CAUSAL_FAMILY: () => CAUSAL_FAMILY,
20786
20826
  CITABLE_PRIOR_LABELS: () => CITABLE_PRIOR_LABELS,
20787
20827
  CODE_REACH_EDGES: () => CODE_REACH_EDGES,
@@ -20833,6 +20873,7 @@ __export(src_exports2, {
20833
20873
  induceAbstractions: () => induceAbstractions,
20834
20874
  induceTriage: () => induceTriage,
20835
20875
  ingestDesignProblem: () => ingestDesignProblem,
20876
+ isConstraintProblem: () => isConstraintProblem,
20836
20877
  isPlaceholderStatement: () => isPlaceholderStatement,
20837
20878
  linkProblemToLanguages: () => linkProblemToLanguages,
20838
20879
  linkProblemToPackages: () => linkProblemToPackages,
@@ -20918,12 +20959,14 @@ function isPassiveInjectable(node2) {
20918
20959
  }
20919
20960
  function buildSnapshot(opts) {
20920
20961
  const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
20921
- const allProblems = problems.filter((p) => p.attrs["resolvedAt"] == null);
20962
+ const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
20963
+ const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
20922
20964
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
20923
20965
  const recent = allProblems.slice(0, 8).map((p) => ({
20924
20966
  node: p,
20925
20967
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
20926
20968
  }));
20969
+ const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
20927
20970
  const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
20928
20971
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
20929
20972
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
@@ -20951,10 +20994,13 @@ function buildSnapshot(opts) {
20951
20994
  episodeId: episodeId2,
20952
20995
  problems: problems2
20953
20996
  }));
20954
- const needsRevisit = listNeedsRevisit(
20955
- opts.store,
20956
- (opts.now ?? /* @__PURE__ */ new Date()).getTime()
20957
- ).slice(0, 10);
20997
+ const revisitSeen = /* @__PURE__ */ new Set();
20998
+ const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
20999
+ const key = `${r.label}:${r.description.trim().toLowerCase()}`;
21000
+ if (revisitSeen.has(key)) return false;
21001
+ revisitSeen.add(key);
21002
+ return true;
21003
+ }).slice(0, 5);
20958
21004
  const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
20959
21005
  const pkgBase = (x) => {
20960
21006
  const at = x.lastIndexOf("@");
@@ -20990,6 +21036,7 @@ function buildSnapshot(opts) {
20990
21036
  profileContext,
20991
21037
  recentProblems: recent,
20992
21038
  recentResolved,
21039
+ recentConstraints,
20993
21040
  ...causalNudge ? { causalNudge } : {},
20994
21041
  ...domainNudge ? { domainNudge } : {},
20995
21042
  motifs,
@@ -21042,6 +21089,7 @@ function sliceForFile(store, relPath) {
21042
21089
  const fp = priorsForFile(store, relPath);
21043
21090
  const priors = fp ? {
21044
21091
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
21092
+ constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
21045
21093
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
21046
21094
  solutionsByProblem: Object.fromEntries(
21047
21095
  fp.openProblems.slice(0, 3).map((p) => [
@@ -21136,7 +21184,7 @@ function renderSnapshot(s) {
21136
21184
  }
21137
21185
  const tagOf = (n) => s.edgeElicitation ? ` \`[${priorHandle(n)}]\`` : "";
21138
21186
  lines.push("### Recently observed problems in this workspace");
21139
- if (s.recentProblems.length === 0 && s.recentResolved.length === 0) {
21187
+ if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
21140
21188
  lines.push("- _none yet \u2014 errata is still building its model_");
21141
21189
  } else {
21142
21190
  if (s.recentProblems.length > 0) {
@@ -21158,6 +21206,12 @@ function renderSnapshot(s) {
21158
21206
  );
21159
21207
  }
21160
21208
  }
21209
+ if (s.recentConstraints.length > 0) {
21210
+ lines.push("_Design tensions \u2014 constraints this work is shaped around, not defects to fix:_");
21211
+ for (const c of s.recentConstraints) {
21212
+ lines.push(`- \u2696 **${c.description}** \u2014 \`${c.id}\`${tagOf(c)}`);
21213
+ }
21214
+ }
21161
21215
  if (s.recentResolved.length > 0) {
21162
21216
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
21163
21217
  for (const r of s.recentResolved) {
@@ -21225,6 +21279,14 @@ function renderSnapshot(s) {
21225
21279
  }
21226
21280
  }
21227
21281
  }
21282
+ if (w.priors?.constraints.length) {
21283
+ lines.push(
21284
+ "- **Design tensions here** \u2014 standing constraints this code is shaped around. They are NOT open work and a change does not resolve one, so there is no `(fix:)` token; cite one you worked within with `([id])`:"
21285
+ );
21286
+ for (const c of w.priors.constraints) {
21287
+ lines.push(` - ${c.description} \u2192 \`([${c.id}])\``);
21288
+ }
21289
+ }
21228
21290
  if (w.priors?.related.length) {
21229
21291
  lines.push("- **Related priors** (cite with `([id])` where you lean on one):");
21230
21292
  for (const n of w.priors.related) {
@@ -21262,6 +21324,9 @@ function dropLowestUnit(s) {
21262
21324
  case "pendingEnrichment":
21263
21325
  if (s.pendingEnrichment.length) return s.pendingEnrichment.pop(), true;
21264
21326
  break;
21327
+ case "recentConstraints":
21328
+ if (s.recentConstraints.length) return s.recentConstraints.pop(), true;
21329
+ break;
21265
21330
  case "recentProblems":
21266
21331
  if (s.recentProblems.length) return s.recentProblems.pop(), true;
21267
21332
  break;
@@ -21333,8 +21398,19 @@ ${RECALL_FIRST_BODY}`;
21333
21398
  // budget they drop before anything open/actionable.
21334
21399
  "recentResolved",
21335
21400
  "pendingEnrichment",
21336
- "recentProblems",
21337
- "needsRevisit"
21401
+ // A standing design tension outranks an enrichment nudge (it prevents a wrong
21402
+ // decision) but yields to a live defect (which is actionable now).
21403
+ "recentConstraints",
21404
+ // `needsRevisit` used to sit LAST — the most protected band in the block. That
21405
+ // inverted the block's whole purpose once the link-elicitation instruction (4,942
21406
+ // chars, 55% of the budget, non-evictable) started competing for room: the
21407
+ // budgeter drained all 8 Problems and all 4 Solutions and kept nine stale
21408
+ // auto-close notices, so the agent was handed an instruction to cite `[handles]`
21409
+ // with zero problem handles left to cite. A revisit flag is a maintenance nudge
21410
+ // about something ALREADY closed; a live prior is the substrate every link verb
21411
+ // needs. Problems outrank it now.
21412
+ "needsRevisit",
21413
+ "recentProblems"
21338
21414
  ];
21339
21415
  DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
21340
21416
  }
@@ -21567,6 +21643,7 @@ var init_oauth = __esm({
21567
21643
 
21568
21644
  // ../../packages/cloud-client/src/client.ts
21569
21645
  import { randomUUID } from "node:crypto";
21646
+ import { gzipSync } from "node:zlib";
21570
21647
  function wirePerContext(value) {
21571
21648
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
21572
21649
  const out2 = {};
@@ -21764,6 +21841,11 @@ function normalizeSolution(input) {
21764
21841
  validationSource: input.validationSource
21765
21842
  };
21766
21843
  }
21844
+ function isIntermediaryRejection(err2) {
21845
+ if (!(err2 instanceof CloudError)) return false;
21846
+ if (err2.status < 400 || err2.status >= 500) return false;
21847
+ return /<!DOCTYPE html|<html/i.test(err2.message);
21848
+ }
21767
21849
  function chunkArray(items, size) {
21768
21850
  if (items.length <= size) return items.length ? [[...items]] : [];
21769
21851
  const out2 = [];
@@ -21781,14 +21863,15 @@ function provenanceHeaders(provenance) {
21781
21863
  ...provenance.agentModel ? { "x-inerrata-agent-model": provenance.agentModel } : {}
21782
21864
  };
21783
21865
  }
21784
- var asWireCount, INGEST_NODE_CHUNK, CloudClient, CloudError;
21866
+ var asWireCount, INGEST_NODE_CHUNK, COMPRESS_MIN_BYTES, CloudClient, CloudError;
21785
21867
  var init_client = __esm({
21786
21868
  "../../packages/cloud-client/src/client.ts"() {
21787
21869
  "use strict";
21788
21870
  init_oauth();
21789
21871
  init_src();
21790
21872
  asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
21791
- INGEST_NODE_CHUNK = 25;
21873
+ INGEST_NODE_CHUNK = 8;
21874
+ COMPRESS_MIN_BYTES = 4096;
21792
21875
  CloudClient = class {
21793
21876
  baseUrl;
21794
21877
  apiKey;
@@ -21803,6 +21886,8 @@ var init_client = __esm({
21803
21886
  daemonVersion;
21804
21887
  daemonChannel;
21805
21888
  provenance;
21889
+ /** Cleared for the process once a server proves it cannot inflate (see `json`). */
21890
+ compressRequests;
21806
21891
  constructor(opts) {
21807
21892
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
21808
21893
  if (opts.apiKey !== void 0) this.apiKey = opts.apiKey;
@@ -21816,6 +21901,7 @@ var init_client = __esm({
21816
21901
  this.timeoutMs = opts.timeoutMs ?? 15e3;
21817
21902
  this.daemonVersion = opts.daemonVersion;
21818
21903
  this.daemonChannel = opts.daemonChannel;
21904
+ this.compressRequests = opts.compressRequests ?? true;
21819
21905
  this.provenance = opts.provenance ?? {
21820
21906
  clientProduct: "inerrata_cloud_client",
21821
21907
  clientKind: "sdk",
@@ -21948,11 +22034,32 @@ var init_client = __esm({
21948
22034
  * drain (endpoint-label validation is deferred to the service when an endpoint
21949
22035
  * isn't in-payload), so the split never orphans an edge. Sub-results concat into
21950
22036
  * one. (fix: an un-chunked >200-node backlog 413'd forever and never drained.) */
21951
- async ingest(batch) {
22037
+ async ingest(batch, opts = {}) {
21952
22038
  const runId = randomUUID();
22039
+ const startedAt = Date.now();
22040
+ 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);
22041
+ let chunkIndex = 0;
22042
+ const blocked = [];
22043
+ const emit = (nodeIds, accepted) => {
22044
+ chunkIndex++;
22045
+ const refused = blocked.splice(0, blocked.length);
22046
+ const refusedSet = new Set(refused);
22047
+ opts.onChunk?.({
22048
+ chunkIndex,
22049
+ totalChunks,
22050
+ // Watermark only what actually landed — a blocked node never reached the
22051
+ // door, and marking it contributed would lose it permanently and silently.
22052
+ nodeIds: refusedSet.size ? nodeIds.filter((id) => !refusedSet.has(id)) : nodeIds,
22053
+ accepted,
22054
+ elapsedMs: Date.now() - startedAt,
22055
+ ...refused.length ? { blockedNodeIds: refused } : {}
22056
+ });
22057
+ };
21953
22058
  if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
21954
- const result = await this.ingestWire(toWirePayload(batch, runId));
21955
- return { ...summarizeIngestResult(result), result };
22059
+ const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges, blocked);
22060
+ const summary = summarizeIngestResult(result);
22061
+ emit(batch.nodes.map((n) => n.id), summary.accepted);
22062
+ return { ...summary, result };
21956
22063
  }
21957
22064
  const merged = { runId, nodes: [], edges: [] };
21958
22065
  let pendingEdges = [...batch.edges];
@@ -21964,11 +22071,14 @@ var init_client = __esm({
21964
22071
  const shipped = new Set(inChunk);
21965
22072
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
21966
22073
  }
21967
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, randomUUID()));
22074
+ const r = await this.ingestWithSplit(batch, nodeChunk, inChunk, blocked);
22075
+ emit(nodeChunk.map((n) => n.id), summarizeIngestResult(r).accepted);
21968
22076
  merged.nodes.push(...r.nodes);
21969
22077
  merged.edges.push(...r.edges);
21970
22078
  for (const rn of r.nodes) {
21971
- if (rn.nodeId && rn.nodeId !== rn.canonicalId) echoedCloudId.set(rn.canonicalId, rn.nodeId);
22079
+ if (rn.nodeId && rn.nodeId !== rn.canonicalId && isMembraneSaltedId(rn.nodeId)) {
22080
+ echoedCloudId.set(rn.canonicalId, rn.nodeId);
22081
+ }
21972
22082
  }
21973
22083
  if (r.patternReconciliation) {
21974
22084
  merged.patternReconciliation = { ...merged.patternReconciliation, ...r.patternReconciliation };
@@ -21981,6 +22091,7 @@ var init_client = __esm({
21981
22091
  to: echoedCloudId.get(e.to) ?? e.to
21982
22092
  }));
21983
22093
  const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
22094
+ emit([], summarizeIngestResult(r).accepted);
21984
22095
  merged.edges.push(...r.edges);
21985
22096
  }
21986
22097
  return { ...summarizeIngestResult(merged), result: merged };
@@ -21989,6 +22100,43 @@ var init_client = __esm({
21989
22100
  async ingestWire(payload) {
21990
22101
  return this.json("POST", "/v2/ingest", payload);
21991
22102
  }
22103
+ /**
22104
+ * Ship a node chunk, and on an EDGE TIMEOUT split it and retry the halves.
22105
+ *
22106
+ * `INGEST_NODE_CHUNK` is a guess about how long the server takes per node, and
22107
+ * that number moves with the size of the graph — so any fixed value eventually
22108
+ * drifts into the proxy's timeout and the lane dies silently (the 2026-07-31
22109
+ * three-day instances-lane outage). This makes the client self-correcting
22110
+ * instead: a chunk that times out is halved and retried, down to a single node,
22111
+ * so the drain degrades to slower rather than to stopped.
22112
+ *
22113
+ * ONLY on timeout-shaped failures (502/504/408, or a transport abort). A 4xx is
22114
+ * a verdict about the payload — splitting it would just re-send a rejected batch
22115
+ * N more times — and a 409/413 has its own handling upstream.
22116
+ */
22117
+ async ingestWithSplit(batch, nodes, edges, blocked) {
22118
+ try {
22119
+ return await this.ingestWire(toWirePayload({ ...batch, nodes, edges }, randomUUID()));
22120
+ } catch (err2) {
22121
+ const status = err2 instanceof CloudError ? err2.status : 0;
22122
+ const timedOut = status === 502 || status === 504 || status === 408 || status === 0;
22123
+ const refusedAtEdge = blocked !== void 0 && isIntermediaryRejection(err2);
22124
+ if (refusedAtEdge && nodes.length === 1 && nodes[0]) {
22125
+ blocked.push(nodes[0].id);
22126
+ return { runId: randomUUID(), nodes: [], edges: [] };
22127
+ }
22128
+ if (!timedOut && !refusedAtEdge || nodes.length <= 1) throw err2;
22129
+ const mid = Math.ceil(nodes.length / 2);
22130
+ const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges, blocked);
22131
+ const b = await this.ingestWithSplit(batch, nodes.slice(mid), [], blocked);
22132
+ return {
22133
+ runId: a.runId,
22134
+ nodes: [...a.nodes, ...b.nodes],
22135
+ edges: [...a.edges, ...b.edges],
22136
+ ...a.patternReconciliation || b.patternReconciliation ? { patternReconciliation: { ...a.patternReconciliation, ...b.patternReconciliation } } : {}
22137
+ };
22138
+ }
22139
+ }
21992
22140
  /**
21993
22141
  * SDK v2 graph-native write. It remembers a typed diagnostic spine through
21994
22142
  * `/v2/ingest` instead of the legacy forum question/answer routes:
@@ -22190,10 +22338,13 @@ var init_client = __esm({
22190
22338
  }
22191
22339
  /** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
22192
22340
  * canonical ids of the agent's recent problems — the cloud ranks the skills whose
22193
- * motifs those problems match first (the rest are baseline). */
22194
- async getSkills(context = "daemon skill sync", seed) {
22341
+ * motifs those problems match first (the rest are baseline). `techSeed`
22342
+ * (CL-tech-bridge) is the workspace's spine tech ids (`lang:<slug>`, purls)
22343
+ * a secondary relevance signal that also covers the zero-problem cold start. */
22344
+ async getSkills(context = "daemon skill sync", seed, techSeed) {
22195
22345
  const qs = new URLSearchParams({ context });
22196
22346
  if (seed && seed.length > 0) qs.set("seed", seed.join(","));
22347
+ if (techSeed && techSeed.length > 0) qs.set("techSeed", techSeed.join(","));
22197
22348
  if (this.daemonVersion) qs.set("daemonVersion", this.daemonVersion);
22198
22349
  if (this.daemonChannel) qs.set("daemonChannel", this.daemonChannel);
22199
22350
  return this.json("GET", `/v2/skills?${qs.toString()}`);
@@ -22279,15 +22430,43 @@ var init_client = __esm({
22279
22430
  const token = opts?.bearerToken ?? await this.authToken();
22280
22431
  if (token) headers["authorization"] = `Bearer ${token}`;
22281
22432
  }
22433
+ let payload;
22434
+ if (body2 !== void 0) {
22435
+ const raw2 = JSON.stringify(body2);
22436
+ if (this.compressRequests && Buffer.byteLength(raw2) >= COMPRESS_MIN_BYTES) {
22437
+ payload = gzipSync(Buffer.from(raw2));
22438
+ headers["content-encoding"] = "gzip";
22439
+ } else {
22440
+ payload = raw2;
22441
+ }
22442
+ }
22282
22443
  const ac = new AbortController();
22283
22444
  const tid = setTimeout(() => ac.abort(), this.timeoutMs);
22284
22445
  try {
22285
22446
  const res = await this.fetchFn(url2, {
22286
22447
  method,
22287
22448
  headers,
22288
- body: body2 === void 0 ? void 0 : JSON.stringify(body2),
22449
+ body: payload,
22289
22450
  signal: ac.signal
22290
22451
  });
22452
+ if (res.status === 400 && headers["content-encoding"] === "gzip") {
22453
+ delete headers["content-encoding"];
22454
+ const retry = await this.fetchFn(url2, {
22455
+ method,
22456
+ headers,
22457
+ body: JSON.stringify(body2),
22458
+ signal: ac.signal
22459
+ });
22460
+ if (!retry.ok) {
22461
+ const text = await retry.text();
22462
+ throw new CloudError(
22463
+ `${method} ${path2} failed: HTTP ${retry.status} ${text.slice(0, 200)}`,
22464
+ retry.status
22465
+ );
22466
+ }
22467
+ this.compressRequests = false;
22468
+ return await retry.json();
22469
+ }
22291
22470
  if (!res.ok) {
22292
22471
  const text = await res.text();
22293
22472
  throw new CloudError(
@@ -25201,15 +25380,17 @@ function triageBullet(ref) {
25201
25380
  }
25202
25381
  function linkBullet(ref) {
25203
25382
  return [
25204
- " \u2022 a problem you flagged resembles prior knowledge \u2014 LINK it to the canonical priors, not just one:",
25383
+ // Lead-in states the trigger without the old "resembles prior knowledge"
25384
+ // conditional (which gated the whole bullet on recognising a prior). Kept to ONE
25385
+ // line on purpose: Cycle 13 measured this instruction at 55% of the 9,000-char
25386
+ // passive-context budget, and every char here is paid for by evicting a prior the
25387
+ // agent could have cited — so unproven framing is a net loss, however good it reads.
25388
+ " \u2022 you flagged a problem \u2014 LINK it, ESPECIALLY when the finding feels novel to here:",
25205
25389
  ` \xB7 (instance:[${ref}],[another-prior],\u2026) \u2014 the priors this problem is an instance of.`,
25206
25390
  " Aim for ~3 DIFFERENT relevant priors; one link is weak, three triangulate it.",
25207
25391
  ` \xB7 ${TAG_EXAMPLE.pattern()} \u2014 name the general shape it instantiates (e.g. (pattern: unbounded`,
25208
25392
  " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
25209
25393
  " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
25210
- ` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
25211
- " (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
25212
- " an invisible island: the Domain is the topic other problems in the area cluster on. Title Case.",
25213
25394
  ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC PACKAGE the problem is about, even when this`,
25214
25395
  " workspace doesn't depend on it (e.g. (package: chokidar), (package: pypi/requests)) \u2014 a",
25215
25396
  " public-registry anchor lets the knowledge cross to the collective; internal names stay private.",
@@ -25233,9 +25414,9 @@ function attemptBullet() {
25233
25414
  ].join("\n");
25234
25415
  }
25235
25416
  function buildAgentInstruction(opts = {}) {
25236
- const signals = opts.signals ?? ["prior", "problem", "fix", "constraint", "triage", "attempt", "link"];
25417
+ const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
25237
25418
  const ref = opts.referent ?? "its-handle";
25238
- const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "link" ? TAG_EXAMPLE.pattern() : TAG_EXAMPLE[s](ref);
25419
+ 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);
25239
25420
  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:`;
25240
25421
  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.";
25241
25422
  return [
@@ -25301,6 +25482,22 @@ var init_agent_signals = __esm({
25301
25482
  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)",
25302
25483
  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",
25303
25484
  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",
25485
+ // DOMAIN — promoted to a FIRST-CLASS signal (was nested inside linkBullet, whose
25486
+ // lead-in gates on "a problem you flagged resembles prior knowledge"). Naming the
25487
+ // area a problem is about is unconditional and has nothing to do with recognizing
25488
+ // a prior, so that gate silently suppressed it: field census 2026-07-30 across 387
25489
+ // sessions — `domain` in 10/194 tag-emitting sessions (5%) against `problem` in
25490
+ // 124/194 (64%), and bimodal (the 10 that do emit average ~7 each), i.e. the
25491
+ // syntax is fine and the trigger was never reached.
25492
+ //
25493
+ // The gloss carries the anti-performance framing Cycles 9–11 proved is the actual
25494
+ // lever (the tag alone scored 0 = control; naming the bias and inverting it is
25495
+ // what lifted capture). The bias here is the mirror of the constraint one: an
25496
+ // agent that has just named a problem *precisely* feels the precision IS the
25497
+ // contribution, so stating the general area reads as vague restatement — exactly
25498
+ // when it matters most, because precise wording is what makes a problem
25499
+ // unfindable to anyone who doesn't already share it.
25500
+ 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",
25304
25501
  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"
25305
25502
  };
25306
25503
  }
@@ -25903,6 +26100,33 @@ function federationHint(status) {
25903
26100
  return void 0;
25904
26101
  }
25905
26102
  }
26103
+ function hasCloudNodeShape(id) {
26104
+ return CANONICAL_KNOWLEDGE_ID.test(id) || UUID_ID.test(id) || id.startsWith("pkg:");
26105
+ }
26106
+ async function collectiveDrilldown(cloud, seedId, limit) {
26107
+ try {
26108
+ const res = await cloud.search({ stack: [], domains: [], kinds: [], seed: [seedId], limit });
26109
+ if (res.nodes.length === 0) return null;
26110
+ return {
26111
+ seed: seedId,
26112
+ nodes: res.nodes.map((n) => ({
26113
+ id: n.id,
26114
+ label: n.label,
26115
+ name: n.description,
26116
+ score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
26117
+ hops: 1,
26118
+ provenance: "collective"
26119
+ })),
26120
+ edges: res.edges.map((e) => ({ from: e.from, to: e.to, type: e.type }))
26121
+ };
26122
+ } catch (err2) {
26123
+ console.error(
26124
+ "[errata] collectiveDrilldown: cloud fallback failed \u2014",
26125
+ err2 instanceof Error ? err2.message : err2
26126
+ );
26127
+ return null;
26128
+ }
26129
+ }
25906
26130
  function formatDualNodes(merged) {
25907
26131
  return merged.nodes.map((n) => ({
25908
26132
  id: n.id,
@@ -25922,7 +26146,7 @@ function formatDualNodes(merged) {
25922
26146
  } : {}
25923
26147
  }));
25924
26148
  }
25925
- var BRIDGE_LABELS;
26149
+ var BRIDGE_LABELS, CANONICAL_KNOWLEDGE_ID, UUID_ID;
25926
26150
  var init_dual_augment = __esm({
25927
26151
  "src/dual-augment.ts"() {
25928
26152
  "use strict";
@@ -25930,6 +26154,8 @@ var init_dual_augment = __esm({
25930
26154
  init_generalize_graph();
25931
26155
  init_dual_burst();
25932
26156
  BRIDGE_LABELS = new Set(SEMANTIC_NODE_LABELS);
26157
+ CANONICAL_KNOWLEDGE_ID = /^[a-z][a-z0-9]*_[0-9a-f]{8,}$/;
26158
+ UUID_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
25933
26159
  }
25934
26160
  });
25935
26161
 
@@ -26424,6 +26650,7 @@ function mergeProblemsByEmbedding(store, opts) {
26424
26650
  if (b.id === a.id || consumed.has(b.id)) continue;
26425
26651
  if (b.embedding.length !== a.embedding.length) continue;
26426
26652
  if ((a.attrs["embeddingVersion"] ?? "") !== (b.attrs["embeddingVersion"] ?? "")) continue;
26653
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
26427
26654
  if (cosine(a.embedding, b.embedding) >= minCosine) {
26428
26655
  cluster.push(b);
26429
26656
  consumed.add(b.id);
@@ -36845,6 +37072,12 @@ function cosine3(a, b) {
36845
37072
  for (let i2 = 0; i2 < a.length; i2++) dot += a[i2] * b[i2];
36846
37073
  return dot;
36847
37074
  }
37075
+ async function collectiveSeedFallback(args2, ctx, defaultLimit) {
37076
+ const seedId = typeof args2["nodeId"] === "string" ? args2["nodeId"] : null;
37077
+ if (!seedId || !ctx.cloud || !hasCloudNodeShape(seedId)) return null;
37078
+ const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? defaultLimit)));
37079
+ return collectiveDrilldown(ctx.cloud, seedId, limit);
37080
+ }
36848
37081
  function resolveSymbol2(store, args2) {
36849
37082
  const id = args2["nodeId"];
36850
37083
  if (typeof id === "string" && store.getNode(id)) return id;
@@ -37195,7 +37428,7 @@ var init_mcp = __esm({
37195
37428
  },
37196
37429
  {
37197
37430
  name: "errata.neighbors",
37198
- description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph.",
37431
+ description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective neighborhood, tagged provenance:collective.",
37199
37432
  inputSchema: {
37200
37433
  type: "object",
37201
37434
  properties: {
@@ -37204,9 +37437,27 @@ var init_mcp = __esm({
37204
37437
  limit: { type: "number", description: "Max edges per direction (default 30)" }
37205
37438
  }
37206
37439
  },
37207
- handler: (args2, store) => {
37440
+ handler: async (args2, store, ctx) => {
37208
37441
  const id = resolveSymbol2(store, args2);
37209
- if (!id) return unresolved(args2);
37442
+ if (!id) {
37443
+ const cn = await collectiveSeedFallback(args2, ctx, 30);
37444
+ if (cn) {
37445
+ return {
37446
+ found: true,
37447
+ node: { id: cn.seed, provenance: "collective" },
37448
+ collectiveReachable: true,
37449
+ collectiveStatus: "cloud-seed",
37450
+ // Seed-touching edge types aren't on the wire (the server's seed
37451
+ // burst excludes the seed itself, and edges to nodes outside the
37452
+ // result set are dropped), so the collective fallback reports the
37453
+ // 1-hop neighborhood + its internal topology instead of the local
37454
+ // contract's typed out/in lists.
37455
+ nearby: cn.nodes,
37456
+ edges: cn.edges
37457
+ };
37458
+ }
37459
+ return unresolved(args2);
37460
+ }
37210
37461
  const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 30)));
37211
37462
  const node2 = store.getNode(id);
37212
37463
  if (!node2) return { found: false, reason: "not-found" };
@@ -37253,32 +37504,17 @@ var init_mcp = __esm({
37253
37504
  handler: async (args2, store, ctx) => {
37254
37505
  const id = resolveSymbol2(store, args2);
37255
37506
  if (!id) {
37256
- const seedId = typeof args2["nodeId"] === "string" ? args2["nodeId"] : null;
37257
- if (seedId && ctx.cloud) {
37258
- const res = await ctx.cloud.search({
37259
- stack: [],
37260
- domains: [],
37261
- kinds: [],
37262
- seed: [seedId],
37263
- limit: args2["limit"] != null ? Number(args2["limit"]) : 20
37264
- }).catch(() => null);
37265
- if (res && res.nodes.length > 0) {
37266
- return {
37267
- seed: seedId,
37268
- seedProvenance: "collective",
37269
- collectiveReachable: true,
37270
- collectiveStatus: "cloud-seed",
37271
- count: res.nodes.length,
37272
- results: res.nodes.map((n) => ({
37273
- id: n.id,
37274
- label: n.label,
37275
- name: n.description,
37276
- score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
37277
- hops: 1,
37278
- provenance: "collective"
37279
- }))
37280
- };
37281
- }
37507
+ const cn = await collectiveSeedFallback(args2, ctx, 20);
37508
+ if (cn) {
37509
+ return {
37510
+ seed: cn.seed,
37511
+ seedProvenance: "collective",
37512
+ collectiveReachable: true,
37513
+ collectiveStatus: "cloud-seed",
37514
+ count: cn.nodes.length,
37515
+ results: cn.nodes,
37516
+ edges: cn.edges
37517
+ };
37282
37518
  }
37283
37519
  return unresolved(args2);
37284
37520
  }
@@ -37435,7 +37671,7 @@ var init_mcp = __esm({
37435
37671
  },
37436
37672
  {
37437
37673
  name: "errata.why",
37438
- description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Merges the machine-wide shared store's triage layer (cause chains from inline `<-` diagnoses land there, keyed by the same content-derived problem id). Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}.",
37674
+ description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Merges the machine-wide shared store's triage layer (cause chains from inline `<-` diagnoses land there, keyed by the same content-derived problem id). Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective causal neighborhood, tagged provenance:collective.",
37439
37675
  inputSchema: {
37440
37676
  type: "object",
37441
37677
  properties: {
@@ -37445,9 +37681,30 @@ var init_mcp = __esm({
37445
37681
  limit: { type: "number", description: "Default 30" }
37446
37682
  }
37447
37683
  },
37448
- handler: (args2, store) => {
37684
+ handler: async (args2, store, ctx) => {
37449
37685
  const id = resolveSymbol2(store, args2);
37450
- if (!id) return unresolved(args2);
37686
+ if (!id) {
37687
+ const cn = await collectiveSeedFallback(args2, ctx, 30);
37688
+ if (cn) {
37689
+ return {
37690
+ found: true,
37691
+ seedId: cn.seed,
37692
+ seedProvenance: "collective",
37693
+ collectiveReachable: true,
37694
+ collectiveStatus: "cloud-seed",
37695
+ nodes: cn.nodes.map((n) => ({
37696
+ id: n.id,
37697
+ label: n.label,
37698
+ description: n.name,
37699
+ hops: n.hops,
37700
+ relevance: n.score,
37701
+ provenance: n.provenance
37702
+ })),
37703
+ edges: cn.edges
37704
+ };
37705
+ }
37706
+ return unresolved(args2);
37707
+ }
37451
37708
  const maxHops = Math.max(1, Math.min(8, Number(args2["maxHops"] ?? 4)));
37452
37709
  const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? 30)));
37453
37710
  const local = causalChain(store, { seedId: id, direction: "both", maxHops, limit });
@@ -37630,11 +37887,11 @@ var init_mcp = __esm({
37630
37887
  },
37631
37888
  {
37632
37889
  name: "errata.problems",
37633
- description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
37890
+ description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
37634
37891
  inputSchema: {
37635
37892
  type: "object",
37636
37893
  properties: {
37637
- status: { type: "string", description: "open | resolved | retracted | all (default open)" },
37894
+ status: { type: "string", description: "open | resolved | retracted | constraint | all (default open)" },
37638
37895
  limit: { type: "number" }
37639
37896
  }
37640
37897
  },
@@ -37644,7 +37901,7 @@ var init_mcp = __esm({
37644
37901
  const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
37645
37902
  const rows = store.findAllVersionsByLabel("Problem").map((p) => {
37646
37903
  const ra = p.attrs["resolvedAs"];
37647
- const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : "open";
37904
+ const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
37648
37905
  const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
37649
37906
  const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
37650
37907
  return {
@@ -37652,17 +37909,21 @@ var init_mcp = __esm({
37652
37909
  problem: p.description,
37653
37910
  status,
37654
37911
  createdAt: p.createdAt,
37912
+ // Surfaced even when `status` is resolved/retracted, so a caller can tell
37913
+ // an explicitly-discharged TENSION from a fixed defect.
37914
+ ...isConstraintProblem(p) ? { kind: "constraint" } : {},
37655
37915
  ...p.attrs["provisional"] ? { provisional: true } : {},
37656
37916
  ...p.attrs["resolvedReason"] ? { reason: String(p.attrs["resolvedReason"]) } : {},
37657
37917
  ...anchor ? { anchor } : {}
37658
37918
  };
37659
37919
  });
37660
- const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : s === "open";
37920
+ const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : want === "constraint" ? s === "constraint" : s === "open";
37661
37921
  const items = rows.filter((r) => inScope(r.status)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
37662
37922
  const countBy = (s) => rows.filter((r) => r.status === s).length;
37663
37923
  return {
37664
37924
  open: countBy("open"),
37665
37925
  resolved: countBy("resolved"),
37926
+ constraints: countBy("constraint"),
37666
37927
  retracted: {
37667
37928
  falsePositive: countBy("false_positive"),
37668
37929
  dissolution: countBy("invalid"),
@@ -37751,7 +38012,8 @@ var init_mcp = __esm({
37751
38012
  inputSchema: { type: "object", properties: {} },
37752
38013
  handler: (_args, store) => {
37753
38014
  const problems = store.findNodesByLabel("Problem");
37754
- const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null);
38015
+ const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null && !isConstraintProblem(p));
38016
+ const constraints = problems.filter((p) => p.attrs["resolvedAt"] == null && isConstraintProblem(p));
37755
38017
  const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
37756
38018
  const allProblems = store.findAllVersionsByLabel("Problem");
37757
38019
  const ungroundedDerived = store.findNodesByLabel("Claim").filter((c) => c.attrs["crystallized"] === "derived" && Number(c.attrs["groundedSupport"] ?? 0) === 0);
@@ -37761,6 +38023,7 @@ var init_mcp = __esm({
37761
38023
  strandedSymbols: findStrandedSymbols(store).length,
37762
38024
  openProblems: open2.length,
37763
38025
  openProblemsWithoutFix: noFix.length,
38026
+ designConstraints: constraints.length,
37764
38027
  retractedFalsePositive: allProblems.filter((p) => p.attrs["resolvedAs"] === "false_positive").length,
37765
38028
  retractedDissolution: allProblems.filter((p) => p.attrs["resolvedAs"] === "invalid").length,
37766
38029
  ungroundedDerivedClaims: ungroundedDerived.length,
@@ -38332,7 +38595,7 @@ function formatProblemsMd(r) {
38332
38595
  const lines = [head("problems")];
38333
38596
  const ret = r.retracted ?? { falsePositive: 0, dissolution: 0, duplicate: 0 };
38334
38597
  lines.push(
38335
- `open ${r.open} \xB7 resolved ${r.resolved} \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
38598
+ `open ${r.open} \xB7 resolved ${r.resolved}` + (r.constraints ? ` \xB7 constraints ${r.constraints}` : "") + ` \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
38336
38599
  );
38337
38600
  lines.push(`showing ${r.count}`);
38338
38601
  lines.push("");
@@ -46070,10 +46333,13 @@ function recallForFile(store, relPath) {
46070
46333
  recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
46071
46334
  );
46072
46335
  }
46336
+ for (const c of priors.constraints.slice(0, 2)) {
46337
+ lines.push(recallLine(c, `design tension \u2014 hold it, don't "fix" it \xB7 ${TAG_EXAMPLE.prior(c.id)} to cite`));
46338
+ }
46073
46339
  for (const n of priors.related.slice(0, 4)) {
46074
46340
  lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
46075
46341
  }
46076
- const total = priors.openProblems.length + priors.related.length;
46342
+ const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
46077
46343
  return `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
46078
46344
  ${lines.join("\n")}
46079
46345
  ` + buildFileRecallInstruction();
@@ -47045,12 +47311,12 @@ var init_report_render = __esm({
47045
47311
 
47046
47312
  // src/cli.ts
47047
47313
  init_src5();
47048
- import { closeSync as closeSync2, existsSync as existsSync24, openSync as openSync2, readFileSync as readFileSync22, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
47049
- import { join as join26 } from "node:path";
47314
+ import { closeSync as closeSync2, existsSync as existsSync25, openSync as openSync2, readFileSync as readFileSync24, renameSync as renameSync4, statSync as statSync6 } from "node:fs";
47315
+ import { join as join28 } from "node:path";
47050
47316
  import { spawn as spawn3 } from "node:child_process";
47051
47317
 
47052
47318
  // src/daemon.ts
47053
- import { existsSync as existsSync19, writeFileSync as writeFileSync15 } from "node:fs";
47319
+ import { existsSync as existsSync20, writeFileSync as writeFileSync17 } from "node:fs";
47054
47320
 
47055
47321
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
47056
47322
  import { createServer as createServerHTTP } from "http";
@@ -47630,8 +47896,8 @@ init_config();
47630
47896
 
47631
47897
  // src/engine.ts
47632
47898
  import { execFileSync as execFileSync3 } from "node:child_process";
47633
- import { existsSync as existsSync18, statSync as statSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync8, renameSync as renameSync2, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
47634
- import { join as join22, relative as relative6, sep as sep4 } from "node:path";
47899
+ 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";
47900
+ import { join as join24, relative as relative6, sep as sep4 } from "node:path";
47635
47901
 
47636
47902
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
47637
47903
  import { stat as statcb } from "fs";
@@ -48362,9 +48628,9 @@ var NodeFsHandler = class {
48362
48628
  if (this.fsw.closed) {
48363
48629
  return;
48364
48630
  }
48365
- const dirname10 = sysPath.dirname(file2);
48631
+ const dirname11 = sysPath.dirname(file2);
48366
48632
  const basename5 = sysPath.basename(file2);
48367
- const parent = this.fsw._getWatchedDir(dirname10);
48633
+ const parent = this.fsw._getWatchedDir(dirname11);
48368
48634
  let prevStats = stats;
48369
48635
  if (parent.has(basename5))
48370
48636
  return;
@@ -48391,7 +48657,7 @@ var NodeFsHandler = class {
48391
48657
  prevStats = newStats2;
48392
48658
  }
48393
48659
  } catch (error48) {
48394
- this.fsw._remove(dirname10, basename5);
48660
+ this.fsw._remove(dirname11, basename5);
48395
48661
  }
48396
48662
  } else if (parent.has(basename5)) {
48397
48663
  const at = newStats.atimeMs;
@@ -49496,6 +49762,39 @@ init_review2();
49496
49762
  import { closeSync, existsSync as existsSync12, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
49497
49763
  import { basename as basename3, dirname as dirname8, join as join15 } from "node:path";
49498
49764
  import { homedir as homedir3 } from "node:os";
49765
+ function readFrom(path2, fromByte) {
49766
+ let fd;
49767
+ try {
49768
+ fd = openSync(path2, "r");
49769
+ } catch {
49770
+ return { text: "", nextOffset: fromByte };
49771
+ }
49772
+ try {
49773
+ const size = fstatSync(fd).size;
49774
+ const start2 = fromByte > 0 && fromByte <= size ? fromByte : 0;
49775
+ const len = size - start2;
49776
+ if (len <= 0) return { text: "", nextOffset: size };
49777
+ const buf = Buffer.allocUnsafe(len);
49778
+ readSync(fd, buf, 0, len, start2);
49779
+ return { text: buf.toString("utf8"), nextOffset: size };
49780
+ } catch {
49781
+ return { text: "", nextOffset: fromByte };
49782
+ } finally {
49783
+ closeSync(fd);
49784
+ }
49785
+ }
49786
+ function transcriptSize(path2) {
49787
+ try {
49788
+ const fd = openSync(path2, "r");
49789
+ try {
49790
+ return fstatSync(fd).size;
49791
+ } finally {
49792
+ closeSync(fd);
49793
+ }
49794
+ } catch {
49795
+ return 0;
49796
+ }
49797
+ }
49499
49798
  function readTail(path2, maxBytes) {
49500
49799
  let fd;
49501
49800
  try {
@@ -49573,7 +49872,13 @@ function isUserTurnBoundary(obj) {
49573
49872
  return blocks.some((b) => b["type"] === "text");
49574
49873
  }
49575
49874
  function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2e6) {
49576
- const raw2 = readTail(transcriptPath, maxBytes);
49875
+ return parseAssistantTurns(readTail(transcriptPath, maxBytes), includeThinking);
49876
+ }
49877
+ function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true) {
49878
+ const { text, nextOffset } = readFrom(transcriptPath, fromByte);
49879
+ return { turns: parseAssistantTurns(text, includeThinking), nextOffset };
49880
+ }
49881
+ function parseAssistantTurns(raw2, includeThinking) {
49577
49882
  if (!raw2) return [];
49578
49883
  const turns = [];
49579
49884
  const lines = raw2.split(/\r?\n/);
@@ -49696,10 +50001,13 @@ init_src4();
49696
50001
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
49697
50002
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
49698
50003
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
49699
- var HANDLE_RE = /\[([a-z0-9][\w-]{0,40})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
50004
+ var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
49700
50005
  var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
49701
50006
  var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
49702
50007
  var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
50008
+ var NEST2_UNIT = String.raw`(?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))`;
50009
+ var FIX_LONG_RE = new RegExp(String.raw`\(\s*fix:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{12,}?)\s*\)`, "gi");
50010
+ var CAUSE_LONG_RE = new RegExp(String.raw`\(\s*cause:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{8,}?)\s*\)`, "gi");
49703
50011
  var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
49704
50012
  var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
49705
50013
  var AIDS_PREFIX = /^\s*aids:(?:#([a-z][\w-]{0,63}))?\s*/i;
@@ -49712,7 +50020,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
49712
50020
  var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
49713
50021
  var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
49714
50022
  var CAUSE_TEXT_MIN = 8;
49715
- var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
50023
+ var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n]){8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49716
50024
  var CONSTRAINT_MIN = 8;
49717
50025
  function clauseBefore(sentence, idx) {
49718
50026
  const win = sentence.slice(Math.max(0, idx - 80), idx);
@@ -49727,6 +50035,17 @@ function fixRationaleBefore(sentence, idx) {
49727
50035
  const clause = m ? win.slice(m.index + 1) : win;
49728
50036
  return clause.replace(/\s+/g, " ").trim();
49729
50037
  }
50038
+ function fixRationaleAfter(sentence, endIdx) {
50039
+ const win = sentence.slice(endIdx, endIdx + FIX_RATIONALE_WINDOW).replace(new RegExp(CITE_GROUP_RE.source, "g"), " ").replace(new RegExp(FLAG_RE.source, "g"), " ");
50040
+ const stripped = win.replace(/^[\s—:–,-]+/, "");
50041
+ const m = /^[^.;!?—]*/.exec(stripped);
50042
+ return (m ? m[0] : stripped).replace(/\s+/g, " ").trim();
50043
+ }
50044
+ function deriveFixRationale(sentence, startIdx, endIdx) {
50045
+ const before = fixRationaleBefore(sentence, startIdx);
50046
+ if (before.length >= FIX_RATIONALE_MIN) return before;
50047
+ return fixRationaleAfter(sentence, endIdx);
50048
+ }
49730
50049
  function parseInlineTags(text) {
49731
50050
  const out2 = [];
49732
50051
  const deFenced = text.replace(/```[\s\S]*?```/g, " ");
@@ -49740,9 +50059,11 @@ function parseInlineTags(text) {
49740
50059
  (m) => /\[[!?]|\((?:fix|cause|constraint|tried|failed):|\(\[/.test(m) ? " " : m
49741
50060
  );
49742
50061
  const sfield = sentence.replace(/\s+/g, " ").trim().slice(0, 160);
50062
+ const citeMatched = /* @__PURE__ */ new Set();
49743
50063
  CITE_GROUP_RE.lastIndex = 0;
49744
50064
  let g;
49745
50065
  while ((g = CITE_GROUP_RE.exec(sentence)) !== null) {
50066
+ citeMatched.add(g.index);
49746
50067
  let content = g[1];
49747
50068
  if (/^\s*pattern:/i.test(content)) continue;
49748
50069
  const fixM = FIX_PREFIX.exec(content);
@@ -49796,7 +50117,7 @@ function parseInlineTags(text) {
49796
50117
  if (!isFix && NEG.test(clauseBefore(sentence, g.index))) continue;
49797
50118
  let fixRationale;
49798
50119
  if (isFix) {
49799
- const r = fixRationaleBefore(sentence, g.index);
50120
+ const r = deriveFixRationale(sentence, g.index, g.index + g[0].length);
49800
50121
  if (r.length >= FIX_RATIONALE_MIN) fixRationale = r;
49801
50122
  }
49802
50123
  HANDLE_RE.lastIndex = 0;
@@ -49821,6 +50142,55 @@ function parseInlineTags(text) {
49821
50142
  out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49822
50143
  }
49823
50144
  }
50145
+ for (const pass of [
50146
+ { re: FIX_LONG_RE, isFix: true },
50147
+ { re: CAUSE_LONG_RE, isFix: false }
50148
+ ]) {
50149
+ pass.re.lastIndex = 0;
50150
+ let lm;
50151
+ while ((lm = pass.re.exec(sentence)) !== null) {
50152
+ if (citeMatched.has(lm.index)) continue;
50153
+ const verbThread = lm[1] ?? void 0;
50154
+ const content = lm[2];
50155
+ HANDLE_RE.lastIndex = 0;
50156
+ const h = HANDLE_RE.exec(content);
50157
+ const handle2 = h ? h[1] ?? h[2] : void 0;
50158
+ if (!pass.isFix) {
50159
+ if (handle2) {
50160
+ out2.push({ kind: "triage", handle: handle2, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
50161
+ } else {
50162
+ const causeText = content.replace(/\s+/g, " ").trim();
50163
+ if (causeText.length >= CAUSE_TEXT_MIN)
50164
+ out2.push({ kind: "triage", causeText, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
50165
+ }
50166
+ continue;
50167
+ }
50168
+ if (handle2) {
50169
+ const inGroup = content.replace(new RegExp(HANDLE_RE.source, "gi"), " ").replace(/\s+/g, " ").trim();
50170
+ const r = inGroup.length >= FIX_RATIONALE_MIN ? inGroup : deriveFixRationale(sentence, lm.index, lm.index + lm[0].length);
50171
+ let emitted = false;
50172
+ HANDLE_RE.lastIndex = 0;
50173
+ let hh;
50174
+ while ((hh = HANDLE_RE.exec(content)) !== null) {
50175
+ const hcap = hh[1] ?? hh[2];
50176
+ if (hcap) {
50177
+ emitted = true;
50178
+ out2.push({
50179
+ kind: "fix",
50180
+ handle: hcap,
50181
+ sentence: sfield,
50182
+ ...r.length >= FIX_RATIONALE_MIN ? { fixRationale: r } : {},
50183
+ ...verbThread ? { threadId: verbThread } : {}
50184
+ });
50185
+ }
50186
+ }
50187
+ if (emitted) continue;
50188
+ }
50189
+ const note = content.replace(/\s+/g, " ").trim();
50190
+ if (note.length >= FIX_RATIONALE_MIN)
50191
+ out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
50192
+ }
50193
+ }
49824
50194
  FLAG_RE.lastIndex = 0;
49825
50195
  let f;
49826
50196
  while ((f = FLAG_RE.exec(sentence)) !== null) {
@@ -50058,7 +50428,7 @@ function harvestInlineTags(store, text, opts) {
50058
50428
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
50059
50429
  const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
50060
50430
  const tags = parseInlineTags(text);
50061
- const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo" || t.kind === "constraint") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
50431
+ const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
50062
50432
  const bindSymptom = (seq, threadId) => {
50063
50433
  if (threadId) {
50064
50434
  const hit = symptomSeqs.find((s) => s.threadId === threadId);
@@ -50088,7 +50458,7 @@ function harvestInlineTags(store, text, opts) {
50088
50458
  ...tag.threadId ? { threadId: tag.threadId } : {}
50089
50459
  });
50090
50460
  } else if (tag.kind === "constraint") {
50091
- plan.problems.push({ statement: tag.statement, kind: "problem" });
50461
+ plan.problems.push({ statement: tag.statement, kind: "constraint" });
50092
50462
  } else if (tag.kind === "fix") {
50093
50463
  if (tag.handle) {
50094
50464
  const problemId = resolveHandle(store, tag.handle, opts.handleMap);
@@ -50249,7 +50619,15 @@ function harvestInlineTags(store, text, opts) {
50249
50619
  }
50250
50620
  return plan;
50251
50621
  }
50252
- var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
50622
+ var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
50623
+ "Problem",
50624
+ "Solution",
50625
+ "RootCause",
50626
+ "Pattern",
50627
+ "AntiPattern",
50628
+ "Technique",
50629
+ "Weakness"
50630
+ ]);
50253
50631
  function stampWitnessOrigin(items, origin) {
50254
50632
  if (!origin) return [...items];
50255
50633
  const o = digest({ p: origin }).slice(0, 12);
@@ -50300,7 +50678,10 @@ function parseRollupJson(text) {
50300
50678
  ...str("cause") ? { cause: str("cause") } : {},
50301
50679
  ...str("fix") ? { fix: str("fix") } : {},
50302
50680
  ...str("anchor") ? { anchor: str("anchor") } : {},
50303
- kind: o["kind"] === "todo" ? "todo" : "problem"
50681
+ // Preserve `constraint` (GH-constraint-kind) collapsing it to `problem`
50682
+ // here would re-arm the auto-close and the inferred fix-binding on a design
50683
+ // tension that arrived through the rollup path instead of the inline tag.
50684
+ kind: o["kind"] === "todo" ? "todo" : o["kind"] === "constraint" ? "constraint" : "problem"
50304
50685
  });
50305
50686
  }
50306
50687
  return flags2.slice(0, 12);
@@ -50338,6 +50719,174 @@ ${conversation}` }]
50338
50719
  };
50339
50720
  }
50340
50721
 
50722
+ // src/constraint-backfill.ts
50723
+ init_src4();
50724
+ init_src2();
50725
+ import { existsSync as existsSync13, readFileSync as readFileSync11, readdirSync as readdirSync6, writeFileSync as writeFileSync11 } from "node:fs";
50726
+ import { join as join16 } from "node:path";
50727
+ import { homedir as homedir4 } from "node:os";
50728
+ var BACKFILL_VERSION = 1;
50729
+ var EMPTY = {
50730
+ skipped: true,
50731
+ stamped: 0,
50732
+ detached: 0,
50733
+ reopened: 0,
50734
+ witnessed: 0,
50735
+ cloudTwins: [],
50736
+ reminted: 0,
50737
+ recoverable: 0
50738
+ };
50739
+ function markerPath(configDir) {
50740
+ return join16(configDir, "constraint-backfill.json");
50741
+ }
50742
+ function alreadyDone(configDir) {
50743
+ const p = markerPath(configDir);
50744
+ if (!existsSync13(p)) return false;
50745
+ try {
50746
+ const raw2 = JSON.parse(readFileSync11(p, "utf8"));
50747
+ return raw2?.version === BACKFILL_VERSION;
50748
+ } catch {
50749
+ return false;
50750
+ }
50751
+ }
50752
+ function replay(root) {
50753
+ const statements = /* @__PURE__ */ new Set();
50754
+ const citedByFix = /* @__PURE__ */ new Set();
50755
+ const dir = claudeProjectDir(root, homedir4());
50756
+ if (!existsSync13(dir)) return { statements, citedByFix };
50757
+ let names;
50758
+ try {
50759
+ names = readdirSync6(dir).filter((n) => n.endsWith(".jsonl"));
50760
+ } catch {
50761
+ return { statements, citedByFix };
50762
+ }
50763
+ for (const f of names) {
50764
+ let lines;
50765
+ try {
50766
+ lines = readFileSync11(join16(dir, f), "utf8").split("\n");
50767
+ } catch {
50768
+ continue;
50769
+ }
50770
+ for (const line of lines) {
50771
+ if (!line.trim()) continue;
50772
+ let rec;
50773
+ try {
50774
+ rec = JSON.parse(line);
50775
+ } catch {
50776
+ continue;
50777
+ }
50778
+ if (rec.type !== "assistant" || !Array.isArray(rec.message?.content)) continue;
50779
+ for (const b of rec.message.content) {
50780
+ if (b?.type !== "text" || typeof b.text !== "string") continue;
50781
+ for (const tag of parseInlineTags(b.text)) {
50782
+ if (tag.kind === "constraint" && tag.statement) statements.add(tag.statement);
50783
+ if (tag.kind === "fix") {
50784
+ const t = tag;
50785
+ if (t.handle) citedByFix.add(t.handle);
50786
+ if (t.threadId) citedByFix.add(t.threadId);
50787
+ }
50788
+ }
50789
+ }
50790
+ }
50791
+ }
50792
+ return { statements, citedByFix };
50793
+ }
50794
+ function backfillConstraintKind(store, opts) {
50795
+ if (!opts.force && !opts.dryRun && alreadyDone(opts.configDir)) return EMPTY;
50796
+ const { statements, citedByFix } = replay(opts.root);
50797
+ const report = {
50798
+ skipped: false,
50799
+ stamped: 0,
50800
+ detached: 0,
50801
+ reopened: 0,
50802
+ witnessed: 0,
50803
+ cloudTwins: [],
50804
+ reminted: 0,
50805
+ recoverable: 0
50806
+ };
50807
+ const missing = [];
50808
+ const seen = /* @__PURE__ */ new Set();
50809
+ const work = [];
50810
+ for (const statement of statements) {
50811
+ const id = designProblemId(statement);
50812
+ if (seen.has(id)) continue;
50813
+ seen.add(id);
50814
+ const node2 = store.getNode(id);
50815
+ if (!node2 || node2.label !== "Problem") {
50816
+ missing.push(statement);
50817
+ continue;
50818
+ }
50819
+ const isWitnessed = citedByFix.has(priorHandle(node2)) || citedByFix.has(id) || [...citedByFix].some((c) => c && id.startsWith(c));
50820
+ const fabricated = [];
50821
+ for (const e of store.outEdges(id, ["SOLVED_BY"])) {
50822
+ const sol = store.getNode(e.to);
50823
+ if (!sol) continue;
50824
+ const auto = sol.description.startsWith(AUTO_MINT_PREFIX);
50825
+ if (!auto && isWitnessed) report.witnessed++;
50826
+ else fabricated.push(sol.id);
50827
+ }
50828
+ const stamp = node2.attrs["kind"] !== "constraint";
50829
+ if (stamp) report.stamped++;
50830
+ if (fabricated.length > 0) {
50831
+ report.detached += fabricated.length;
50832
+ report.reopened++;
50833
+ }
50834
+ const cloudNodeId = node2.attrs["cloudNodeId"];
50835
+ if (typeof cloudNodeId === "string" && (stamp || fabricated.length > 0)) {
50836
+ report.cloudTwins.push(cloudNodeId);
50837
+ }
50838
+ if (stamp || fabricated.length > 0) work.push({ id, fabricated, stamp });
50839
+ }
50840
+ report.recoverable = opts.remint ? 0 : missing.length;
50841
+ if (opts.dryRun) {
50842
+ report.reminted = opts.remint ? missing.length : 0;
50843
+ return report;
50844
+ }
50845
+ if (opts.remint && missing.length > 0) {
50846
+ store.transaction(() => {
50847
+ for (const statement of missing) {
50848
+ try {
50849
+ const r = ingestDesignProblem(
50850
+ store,
50851
+ { problem: statement, kind: "constraint" },
50852
+ { workspaceId: "", source: `constraint-remint:v${BACKFILL_VERSION}`, ts: opts.now }
50853
+ );
50854
+ if (r.created) report.reminted++;
50855
+ } catch {
50856
+ }
50857
+ }
50858
+ });
50859
+ }
50860
+ store.transaction(() => {
50861
+ for (const w of work) {
50862
+ const node2 = store.getNode(w.id);
50863
+ if (!node2) continue;
50864
+ const attrs = { ...node2.attrs, kind: "constraint" };
50865
+ if (w.fabricated.length > 0) {
50866
+ delete attrs["resolvedAt"];
50867
+ delete attrs["resolvedReason"];
50868
+ }
50869
+ store.updateNode(w.id, { attrs, lastUpdatedAt: opts.now });
50870
+ for (const solId of w.fabricated) {
50871
+ for (const e of store.outEdges(w.id, ["SOLVED_BY"])) {
50872
+ if (e.to === solId) store.closeEdge(e.id, opts.now);
50873
+ }
50874
+ const sol = store.getNode(solId);
50875
+ if (sol?.description.startsWith(AUTO_MINT_PREFIX)) store.closeNode(solId, opts.now);
50876
+ }
50877
+ }
50878
+ });
50879
+ try {
50880
+ writeFileSync11(
50881
+ markerPath(opts.configDir),
50882
+ JSON.stringify({ version: BACKFILL_VERSION, at: opts.now, ...report, cloudTwins: report.cloudTwins.length }, null, 2),
50883
+ "utf8"
50884
+ );
50885
+ } catch {
50886
+ }
50887
+ return report;
50888
+ }
50889
+
50341
50890
  // src/engine.ts
50342
50891
  init_symbol_summaries();
50343
50892
  init_reconcile();
@@ -50536,11 +51085,11 @@ init_outbox();
50536
51085
  init_src8();
50537
51086
  init_src();
50538
51087
  init_src2();
50539
- import { readFileSync as readFileSync11 } from "node:fs";
50540
- import { join as join16 } from "node:path";
51088
+ import { readFileSync as readFileSync12 } from "node:fs";
51089
+ import { join as join17 } from "node:path";
50541
51090
  function loadClaimIgnorePatterns(workspaceRoot) {
50542
51091
  try {
50543
- return readFileSync11(join16(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
51092
+ return readFileSync12(join17(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
50544
51093
  } catch {
50545
51094
  return [];
50546
51095
  }
@@ -50672,14 +51221,16 @@ function generalizePrincipleForSync(principle, level = 2) {
50672
51221
  delete node2.logicalId;
50673
51222
  return node2;
50674
51223
  }
50675
- function buildPrincipleIngest(sharedStore, daemonVersion, ignorePatterns = [], level = 2) {
51224
+ function buildPrincipleIngest(sharedStore, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
51225
+ const lang = opts.primaryLanguage?.trim().toLowerCase();
51226
+ const claim = lang ? { anchorVisibility: "public", anchor: `lang:${lang}` } : {};
50676
51227
  const selected = sharedStore.findNodesByLabel("Claim").filter((n) => isSyncablePrinciple(n, ignorePatterns));
50677
51228
  for (const n of selected) {
50678
51229
  if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) !== ABSTRACTION_LEVEL.PRINCIPLE || n.attrs["provisional"] !== false) {
50679
51230
  throw new Error(`buildPrincipleIngest: refusing to sync non-canonical-principle ${n.id} \u2014 C7 invariant violated`);
50680
51231
  }
50681
51232
  }
50682
- const nodes = selected.map((n) => generalizePrincipleForSync(n, level));
51233
+ const nodes = selected.map((n) => ({ ...generalizePrincipleForSync(n, level), ...claim }));
50683
51234
  if (nodes.length === 0) return null;
50684
51235
  const base = {
50685
51236
  daemonVersion,
@@ -50732,11 +51283,17 @@ function isSyncableRoute(edge2, shared, triage, ignorePatterns = []) {
50732
51283
  ].join(" ").toLowerCase();
50733
51284
  return !ignorePatterns.some((p) => blob.includes(p));
50734
51285
  }
50735
- function generalizeRouteForSync(edge2, level) {
51286
+ var isScopedBucket = (bucket) => bucket.startsWith("stack:@");
51287
+ function filterScopedBucketKeys(rec, includeScopedBuckets) {
51288
+ if (includeScopedBuckets) return rec;
51289
+ return Object.fromEntries(Object.entries(rec).filter(([k]) => !isScopedBucket(k)));
51290
+ }
51291
+ function generalizeRouteForSync(edge2, level, includeScopedBuckets = false) {
50736
51292
  const disc = edge2.attrs["discriminator"];
50737
51293
  const local = edge2.attrs["perContext"] ?? {};
50738
51294
  const perContext = {};
50739
51295
  for (const [b, c] of Object.entries(local)) {
51296
+ if (!includeScopedBuckets && isScopedBucket(b)) continue;
50740
51297
  const independent = c.independent ?? c.contributors?.length;
50741
51298
  const inferredIndependent = c.inferredIndependent ?? c.inferredContributors?.length;
50742
51299
  perContext[b] = {
@@ -50757,13 +51314,52 @@ function generalizeRouteForSync(edge2, level) {
50757
51314
  navFailures: 0
50758
51315
  };
50759
51316
  }
51317
+ function buildBucketTokenAnchors(languages, packages = []) {
51318
+ const anchors = {};
51319
+ const langTokens = /* @__PURE__ */ new Set();
51320
+ for (const l of languages) {
51321
+ const t = canonicalizeToken(String(l));
51322
+ if (!t || t.includes(":") && !t.startsWith("lang:")) continue;
51323
+ langTokens.add(t);
51324
+ anchors[t] = t.startsWith("lang:") ? t : `lang:${t}`;
51325
+ }
51326
+ const versionless = (purl) => purl.replace(/@[^/@]+$/, "");
51327
+ const conflicted = /* @__PURE__ */ new Set();
51328
+ for (const p of packages) {
51329
+ const t = canonicalizeToken(p.name);
51330
+ if (!t || !p.purl || langTokens.has(t) || conflicted.has(t)) continue;
51331
+ const existing = anchors[t];
51332
+ if (existing === void 0) {
51333
+ anchors[t] = p.purl;
51334
+ } else if (versionless(existing) !== versionless(p.purl)) {
51335
+ delete anchors[t];
51336
+ conflicted.add(t);
51337
+ }
51338
+ }
51339
+ return anchors;
51340
+ }
51341
+ function witnessedAnchor(routes, tokenAnchors) {
51342
+ const weight = /* @__PURE__ */ new Map();
51343
+ for (const e of routes) {
51344
+ const perContext = e.attrs["perContext"] ?? {};
51345
+ for (const [bucket, c] of Object.entries(perContext)) {
51346
+ if (!bucket.startsWith("stack:") || !c || !(c.confirmed > 0)) continue;
51347
+ const anchor = tokenAnchors[bucket.slice("stack:".length)];
51348
+ if (anchor) weight.set(anchor, (weight.get(anchor) ?? 0) + c.confirmed);
51349
+ }
51350
+ }
51351
+ return [...weight.entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1))[0]?.[0];
51352
+ }
50760
51353
  function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
50761
51354
  const lang = opts.primaryLanguage?.trim().toLowerCase();
50762
- const claim = lang ? { anchorVisibility: "public", anchor: `lang:${lang}` } : {};
51355
+ const fallbackAnchor = lang ? `lang:${lang}` : void 0;
51356
+ const tokenAnchors = opts.tokenAnchors ?? {};
51357
+ const includeScopedBuckets = opts.includeScopedBuckets === true;
51358
+ const claimOf = (anchor) => anchor ? { anchorVisibility: "public", anchor } : {};
50763
51359
  const nodes = [];
50764
51360
  const edges = [];
50765
51361
  const seenIds = /* @__PURE__ */ new Set();
50766
- const shareEndpoint = (id) => {
51362
+ const shareEndpoint = (id, anchor) => {
50767
51363
  if (seenIds.has(id)) return;
50768
51364
  const n = shared.getNode(id);
50769
51365
  if (!n) return;
@@ -50773,12 +51369,13 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50773
51369
  description: generalize(n.description, { level }).text,
50774
51370
  embedding: [],
50775
51371
  attrs: { scope: {} },
50776
- ...claim
51372
+ ...claimOf(anchor ?? fallbackAnchor)
50777
51373
  });
50778
51374
  };
50779
51375
  for (const tri of shared.findNodesByLabel("Triage")) {
50780
51376
  const routes = shared.outEdges(tri.id, ["CONFIRMS", "INDICATES", "ROUTES_TO"]).filter((e) => isSyncableRoute(e, shared, tri, ignorePatterns));
50781
51377
  if (routes.length === 0) continue;
51378
+ const triAnchor = witnessedAnchor(routes, tokenAnchors);
50782
51379
  if (!seenIds.has(tri.id)) {
50783
51380
  seenIds.add(tri.id);
50784
51381
  const statement = String(tri.attrs["statement"] ?? "");
@@ -50788,21 +51385,26 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50788
51385
  embedding: [],
50789
51386
  attrs: {
50790
51387
  ...statement ? { statement: generalize(statement, { level }).text } : {},
50791
- ...tri.attrs["perContextSeen"] ? { perContextSeen: tri.attrs["perContextSeen"] } : {}
51388
+ ...tri.attrs["perContextSeen"] ? {
51389
+ perContextSeen: filterScopedBucketKeys(
51390
+ tri.attrs["perContextSeen"],
51391
+ includeScopedBuckets
51392
+ )
51393
+ } : {}
50792
51394
  },
50793
- ...claim
51395
+ ...claimOf(triAnchor ?? fallbackAnchor)
50794
51396
  });
50795
51397
  }
50796
51398
  for (const tb of shared.inEdges(tri.id, ["TRIAGED_BY"])) {
50797
- shareEndpoint(tb.from);
51399
+ shareEndpoint(tb.from, triAnchor);
50798
51400
  edges.push({ ...tb, navSuccesses: 0, navFailures: 0 });
50799
51401
  }
50800
51402
  for (const e of routes) {
50801
51403
  if (!(e.type === "CONFIRMS" || e.type === "ROUTES_TO" && e.attrs["provisional"] === false)) {
50802
51404
  throw new Error(`buildTriageIngest: refusing to sync non-router route ${e.id} \u2014 C7 invariant violated`);
50803
51405
  }
50804
- shareEndpoint(e.to);
50805
- edges.push(generalizeRouteForSync(e, level));
51406
+ shareEndpoint(e.to, witnessedAnchor([e], tokenAnchors));
51407
+ edges.push(generalizeRouteForSync(e, level, includeScopedBuckets));
50806
51408
  }
50807
51409
  }
50808
51410
  if (edges.length === 0) return null;
@@ -50848,22 +51450,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
50848
51450
  }
50849
51451
 
50850
51452
  // src/git-sensor.ts
50851
- import { existsSync as existsSync13, readFileSync as readFileSync12, watch as fsWatch } from "node:fs";
50852
- import { join as join17 } from "node:path";
51453
+ import { existsSync as existsSync14, readFileSync as readFileSync13, watch as fsWatch } from "node:fs";
51454
+ import { join as join18 } from "node:path";
50853
51455
  function readFirstLine(path2) {
50854
51456
  try {
50855
- return readFileSync12(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51457
+ return readFileSync13(path2, "utf8").split(/\r?\n/, 1)[0].trim();
50856
51458
  } catch {
50857
51459
  return null;
50858
51460
  }
50859
51461
  }
50860
51462
  function readGitRefState(gitDir) {
50861
- const head2 = readFirstLine(join17(gitDir, "HEAD"));
51463
+ const head2 = readFirstLine(join18(gitDir, "HEAD"));
50862
51464
  const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
50863
51465
  const branch = m ? m[1] : null;
50864
51466
  let sha2 = null;
50865
51467
  if (branch) {
50866
- sha2 = readFirstLine(join17(gitDir, "refs", "heads", branch));
51468
+ sha2 = readFirstLine(join18(gitDir, "refs", "heads", branch));
50867
51469
  if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
50868
51470
  } else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
50869
51471
  sha2 = head2;
@@ -50871,13 +51473,13 @@ function readGitRefState(gitDir) {
50871
51473
  return {
50872
51474
  branch,
50873
51475
  sha: sha2,
50874
- mergeHeadExists: existsSync13(join17(gitDir, "MERGE_HEAD")),
50875
- origHeadExists: existsSync13(join17(gitDir, "ORIG_HEAD"))
51476
+ mergeHeadExists: existsSync14(join18(gitDir, "MERGE_HEAD")),
51477
+ origHeadExists: existsSync14(join18(gitDir, "ORIG_HEAD"))
50876
51478
  };
50877
51479
  }
50878
51480
  function shaFromPackedRefs(gitDir, ref) {
50879
51481
  try {
50880
- for (const line of readFileSync12(join17(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
51482
+ for (const line of readFileSync13(join18(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
50881
51483
  const [sha2, name2] = line.split(/\s+/);
50882
51484
  if (name2 === ref && sha2) return sha2;
50883
51485
  }
@@ -50911,7 +51513,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
50911
51513
  const settle = () => {
50912
51514
  if (timer) clearTimeout(timer);
50913
51515
  timer = setTimeout(() => {
50914
- if (existsSync13(join17(gitDir, "index.lock"))) {
51516
+ if (existsSync14(join18(gitDir, "index.lock"))) {
50915
51517
  settle();
50916
51518
  return;
50917
51519
  }
@@ -50922,7 +51524,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
50922
51524
  }, debounceMs);
50923
51525
  };
50924
51526
  for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
50925
- const p = join17(gitDir, sub);
51527
+ const p = join18(gitDir, sub);
50926
51528
  try {
50927
51529
  watchers.push(fsWatch(p, settle));
50928
51530
  } catch {
@@ -51147,21 +51749,21 @@ var TelemetryRecorder = class {
51147
51749
 
51148
51750
  // src/skills.ts
51149
51751
  import {
51150
- existsSync as existsSync14,
51752
+ existsSync as existsSync15,
51151
51753
  mkdirSync as mkdirSync6,
51152
- readFileSync as readFileSync13,
51153
- readdirSync as readdirSync6,
51754
+ readFileSync as readFileSync14,
51755
+ readdirSync as readdirSync7,
51154
51756
  unlinkSync as unlinkSync2,
51155
- writeFileSync as writeFileSync11
51757
+ writeFileSync as writeFileSync12
51156
51758
  } from "node:fs";
51157
- import { basename as basename4, join as join18 } from "node:path";
51759
+ import { basename as basename4, join as join19 } from "node:path";
51158
51760
  function skillFileName(id) {
51159
51761
  return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
51160
51762
  }
51161
51763
  function readSkillManifest(manifestPath) {
51162
- if (!existsSync14(manifestPath)) return [];
51764
+ if (!existsSync15(manifestPath)) return [];
51163
51765
  try {
51164
- const parsed = JSON.parse(readFileSync13(manifestPath, "utf8"));
51766
+ const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51165
51767
  return (parsed.skills ?? []).map((s) => ({
51166
51768
  title: s.title ?? "",
51167
51769
  layer: s.layer ?? "technique",
@@ -51172,12 +51774,12 @@ function readSkillManifest(manifestPath) {
51172
51774
  return [];
51173
51775
  }
51174
51776
  }
51175
- async function syncSkills(paths, client, seed, pins = []) {
51176
- const res = await client.getSkills(void 0, seed);
51777
+ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51778
+ const res = await client.getSkills(void 0, seed, techSeed);
51177
51779
  const staleDaemon = res.staleDaemon && res.latestDaemonVersion ? { latest: res.latestDaemonVersion } : void 0;
51178
51780
  mkdirSync6(paths.skillsDir, { recursive: true });
51179
51781
  if (res.skills.length === 0 && pins.length === 0) {
51180
- const existing = readdirSync6(paths.skillsDir).filter((f) => f.endsWith(".md"));
51782
+ const existing = readdirSync7(paths.skillsDir).filter((f) => f.endsWith(".md"));
51181
51783
  if (existing.length > 0) return { written: 0, pruned: 0, ...staleDaemon ? { staleDaemon } : {} };
51182
51784
  }
51183
51785
  const rows = [];
@@ -51185,7 +51787,7 @@ async function syncSkills(paths, client, seed, pins = []) {
51185
51787
  for (const s of res.skills) {
51186
51788
  const fileName = skillFileName(s.id);
51187
51789
  keep.add(fileName);
51188
- writeFileSync11(join18(paths.skillsDir, fileName), s.markdown, "utf8");
51790
+ writeFileSync12(join19(paths.skillsDir, fileName), s.markdown, "utf8");
51189
51791
  rows.push({
51190
51792
  id: s.id,
51191
51793
  title: s.title,
@@ -51198,7 +51800,7 @@ async function syncSkills(paths, client, seed, pins = []) {
51198
51800
  const fileName = skillFileName(p.id);
51199
51801
  if (keep.has(fileName)) continue;
51200
51802
  keep.add(fileName);
51201
- writeFileSync11(join18(paths.skillsDir, fileName), p.markdown, "utf8");
51803
+ writeFileSync12(join19(paths.skillsDir, fileName), p.markdown, "utf8");
51202
51804
  rows.push({
51203
51805
  id: p.id,
51204
51806
  title: p.title,
@@ -51208,17 +51810,17 @@ async function syncSkills(paths, client, seed, pins = []) {
51208
51810
  });
51209
51811
  }
51210
51812
  let pruned = 0;
51211
- for (const f of readdirSync6(paths.skillsDir)) {
51813
+ for (const f of readdirSync7(paths.skillsDir)) {
51212
51814
  if (!f.endsWith(".md")) continue;
51213
51815
  if (keep.has(basename4(f))) continue;
51214
51816
  try {
51215
- unlinkSync2(join18(paths.skillsDir, f));
51817
+ unlinkSync2(join19(paths.skillsDir, f));
51216
51818
  pruned++;
51217
51819
  } catch {
51218
51820
  }
51219
51821
  }
51220
51822
  rows.sort((a, b) => a.id.localeCompare(b.id));
51221
- writeFileSync11(
51823
+ writeFileSync12(
51222
51824
  paths.skillsManifest,
51223
51825
  JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
51224
51826
  "utf8"
@@ -51227,24 +51829,25 @@ async function syncSkills(paths, client, seed, pins = []) {
51227
51829
  }
51228
51830
 
51229
51831
  // src/agent-skills.ts
51832
+ init_src2();
51230
51833
  import {
51231
51834
  cpSync,
51232
- existsSync as existsSync15,
51835
+ existsSync as existsSync16,
51233
51836
  lstatSync,
51234
51837
  mkdirSync as mkdirSync7,
51235
- readFileSync as readFileSync14,
51236
- readdirSync as readdirSync7,
51838
+ readFileSync as readFileSync15,
51839
+ readdirSync as readdirSync8,
51237
51840
  rmSync as rmSync2,
51238
51841
  symlinkSync,
51239
- writeFileSync as writeFileSync12
51842
+ writeFileSync as writeFileSync13
51240
51843
  } from "node:fs";
51241
- import { join as join19 } from "node:path";
51844
+ import { join as join20 } from "node:path";
51242
51845
  var SKILL_NS = "errata-";
51243
51846
  var HARNESS_SKILL_DIRS = [
51244
- { configDir: ".claude", skillsDir: join19(".claude", "skills") },
51847
+ { configDir: ".claude", skillsDir: join20(".claude", "skills") },
51245
51848
  // Cursor adopted the standard; its exact project dir is still moving — kept
51246
51849
  // best-effort and gated on `.cursor/` presence so we never create it blind.
51247
- { configDir: ".cursor", skillsDir: join19(".cursor", "skills") }
51850
+ { configDir: ".cursor", skillsDir: join20(".cursor", "skills") }
51248
51851
  ];
51249
51852
  function skillSlug(title, id) {
51250
51853
  const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
@@ -51259,22 +51862,42 @@ function deriveDescription(title, layer, body2) {
51259
51862
  const when = ` Apply when working on ${layer === "antipattern" ? "code that risks this pitfall" : "problems in this area"}.`;
51260
51863
  return `${lead}${lead.endsWith(".") ? "" : "."}${when}`.slice(0, 1024);
51261
51864
  }
51262
- function renderSkillMd(name2, description, body2) {
51865
+ function renderSkillMd(name2, description, body2, citeHandle) {
51866
+ const footer = citeHandle ? `
51867
+
51868
+ ---
51869
+ When this skill actually helps you, witness it inline in your reply: \`([${citeHandle}])\` \u2014 the citation is recorded and feeds this skill's reliability, so it keeps being served. Untagged help is invisible.
51870
+ ` : "\n";
51263
51871
  return `---
51264
51872
  name: ${name2}
51265
51873
  description: ${yamlScalar(description)}
51266
51874
  ---
51267
51875
 
51268
- ${body2.trimEnd()}
51269
- `;
51876
+ ${body2.trimEnd()}${footer}`;
51877
+ }
51878
+ var LAYER_TO_LABEL = {
51879
+ pattern: "Pattern",
51880
+ antipattern: "AntiPattern",
51881
+ technique: "Technique",
51882
+ weakness: "Weakness"
51883
+ };
51884
+ function skillHandleNodes(skills) {
51885
+ return skills.map((s) => ({
51886
+ id: s.id,
51887
+ label: LAYER_TO_LABEL[s.layer.toLowerCase()] ?? "Pattern",
51888
+ description: s.title
51889
+ }));
51890
+ }
51891
+ function skillCiteHandle(s) {
51892
+ return priorHandle({ id: s.id, description: s.title });
51270
51893
  }
51271
51894
  function reconcileNamespaced(dir, keep) {
51272
- if (!existsSync15(dir)) return 0;
51895
+ if (!existsSync16(dir)) return 0;
51273
51896
  let pruned = 0;
51274
- for (const name2 of readdirSync7(dir)) {
51897
+ for (const name2 of readdirSync8(dir)) {
51275
51898
  if (!name2.startsWith(SKILL_NS) || keep.has(name2)) continue;
51276
51899
  try {
51277
- rmSync2(join19(dir, name2), { recursive: true, force: true });
51900
+ rmSync2(join20(dir, name2), { recursive: true, force: true });
51278
51901
  pruned++;
51279
51902
  } catch {
51280
51903
  }
@@ -51283,7 +51906,7 @@ function reconcileNamespaced(dir, keep) {
51283
51906
  }
51284
51907
  function linkOrCopy(linkPath, target) {
51285
51908
  try {
51286
- if (existsSync15(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51909
+ if (existsSync16(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51287
51910
  } catch {
51288
51911
  }
51289
51912
  try {
@@ -51304,7 +51927,7 @@ function safeLstat(p) {
51304
51927
  }
51305
51928
  }
51306
51929
  function emitAndProjectSkills(root, skills) {
51307
- const agentsSkillsDir = join19(root, ".agents", "skills");
51930
+ const agentsSkillsDir = join20(root, ".agents", "skills");
51308
51931
  mkdirSync7(agentsSkillsDir, { recursive: true });
51309
51932
  const slugs = [];
51310
51933
  const keep = /* @__PURE__ */ new Set();
@@ -51312,7 +51935,7 @@ function emitAndProjectSkills(root, skills) {
51312
51935
  for (const s of skills) {
51313
51936
  let body2;
51314
51937
  try {
51315
- body2 = readFileSync14(s.bodyPath, "utf8");
51938
+ body2 = readFileSync15(s.bodyPath, "utf8");
51316
51939
  } catch {
51317
51940
  continue;
51318
51941
  }
@@ -51321,10 +51944,10 @@ function emitAndProjectSkills(root, skills) {
51321
51944
  keep.add(slug2);
51322
51945
  slugs.push(slug2);
51323
51946
  const description = deriveDescription(s.title, s.layer, body2);
51324
- mkdirSync7(join19(agentsSkillsDir, slug2), { recursive: true });
51325
- writeFileSync12(
51326
- join19(agentsSkillsDir, slug2, "SKILL.md"),
51327
- renderSkillMd(slug2, description, body2),
51947
+ mkdirSync7(join20(agentsSkillsDir, slug2), { recursive: true });
51948
+ writeFileSync13(
51949
+ join20(agentsSkillsDir, slug2, "SKILL.md"),
51950
+ renderSkillMd(slug2, description, body2, skillCiteHandle(s)),
51328
51951
  "utf8"
51329
51952
  );
51330
51953
  emitted++;
@@ -51332,11 +51955,11 @@ function emitAndProjectSkills(root, skills) {
51332
51955
  reconcileNamespaced(agentsSkillsDir, keep);
51333
51956
  let projected = 0;
51334
51957
  for (const h of HARNESS_SKILL_DIRS) {
51335
- if (!existsSync15(join19(root, h.configDir))) continue;
51336
- const dir = join19(root, h.skillsDir);
51958
+ if (!existsSync16(join20(root, h.configDir))) continue;
51959
+ const dir = join20(root, h.skillsDir);
51337
51960
  mkdirSync7(dir, { recursive: true });
51338
51961
  for (const slug2 of slugs) {
51339
- linkOrCopy(join19(dir, slug2), join19(agentsSkillsDir, slug2));
51962
+ linkOrCopy(join20(dir, slug2), join20(agentsSkillsDir, slug2));
51340
51963
  projected++;
51341
51964
  }
51342
51965
  reconcileNamespaced(dir, keep);
@@ -51345,15 +51968,15 @@ function emitAndProjectSkills(root, skills) {
51345
51968
  return { slugs, emitted, projected };
51346
51969
  }
51347
51970
  function emitInputsFromManifest(erretaDir, manifestPath) {
51348
- if (!existsSync15(manifestPath)) return [];
51971
+ if (!existsSync16(manifestPath)) return [];
51349
51972
  try {
51350
- const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51973
+ const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
51351
51974
  return (parsed.skills ?? []).filter((s) => Boolean(s.id && s.file)).map((s) => ({
51352
51975
  id: s.id,
51353
51976
  title: s.title ?? s.id,
51354
51977
  layer: s.layer ?? "technique",
51355
51978
  confidence: s.confidence ?? 0,
51356
- bodyPath: join19(erretaDir, s.file)
51979
+ bodyPath: join20(erretaDir, s.file)
51357
51980
  }));
51358
51981
  } catch {
51359
51982
  return [];
@@ -51367,17 +51990,17 @@ var GITIGNORE_LINES = [
51367
51990
  ".cursor/skills/errata-*/"
51368
51991
  ];
51369
51992
  function ensureSkillGitignore(root) {
51370
- const path2 = join19(root, ".gitignore");
51993
+ const path2 = join20(root, ".gitignore");
51371
51994
  let current = "";
51372
51995
  try {
51373
- current = existsSync15(path2) ? readFileSync14(path2, "utf8") : "";
51996
+ current = existsSync16(path2) ? readFileSync15(path2, "utf8") : "";
51374
51997
  } catch {
51375
51998
  return;
51376
51999
  }
51377
52000
  if (current.includes(GITIGNORE_MARK)) return;
51378
52001
  const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
51379
52002
  try {
51380
- writeFileSync12(path2, `${current}${prefix}
52003
+ writeFileSync13(path2, `${current}${prefix}
51381
52004
  ${GITIGNORE_LINES.join("\n")}
51382
52005
  `, "utf8");
51383
52006
  } catch {
@@ -51435,126 +52058,23 @@ function createToolRunState() {
51435
52058
  // src/engine.ts
51436
52059
  init_paths();
51437
52060
 
51438
- // src/causal.ts
51439
- var SUPPRESSORS = [
51440
- { kind: "ts-nocheck", re: /@ts-nocheck\b/g },
51441
- { kind: "ts-ignore", re: /@ts-(?:ignore|expect-error)\b/g },
51442
- { kind: "eslint-disable", re: /eslint-disable(?:-next-line|-line)?\b/g },
51443
- { kind: "py-ignore", re: /#\s*(?:noqa|type:\s*ignore|pyright:\s*ignore|mypy:\s*ignore|nosec|pragma:\s*no\s*cover)\b/gi },
51444
- { kind: "test-skip", re: /\bx(?:it|describe|test)\s*\(|\.(?:skip|todo)\s*\(|@(?:pytest\.mark\.skip|unittest\.skip)|\bxfail\b|\bt\.Skip\s*\(/g },
51445
- { kind: "lint-allow", re: /\/\/\s*nolint\b|#\[allow\(|istanbul\s+ignore|c8\s+ignore/g },
51446
- { kind: "config-loosen", re: /"strict"\s*:\s*false|"skipLibCheck"\s*:\s*true|ignore_errors\s*=\s*True/gi }
51447
- ];
51448
- function detectSuppression(oldStr, newStr) {
51449
- if (!newStr) return null;
51450
- const old = oldStr ?? "";
51451
- for (const { kind, re } of SUPPRESSORS) {
51452
- const inNew = (newStr.match(re) ?? []).length;
51453
- const inOld = (old.match(re) ?? []).length;
51454
- if (inNew > inOld) return kind;
51455
- }
51456
- return null;
51457
- }
51458
- var DECL_RE = /\b(?:function|class|const|let|var|def|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
51459
- function deriveIntentHints(tool, oldStr, newStr) {
51460
- const hints = /* @__PURE__ */ new Set();
51461
- if (tool === "Write" && (oldStr == null || oldStr === "")) hints.add("add");
51462
- if (detectSuppression(oldStr, newStr)) hints.add("suppress");
51463
- if (oldStr != null && newStr != null) {
51464
- const oldNames = new Set([...oldStr.matchAll(DECL_RE)].map((m) => m[1]));
51465
- const newNames = new Set([...newStr.matchAll(DECL_RE)].map((m) => m[1]));
51466
- const removed = [...oldNames].filter((n) => !newNames.has(n));
51467
- const added = [...newNames].filter((n) => !oldNames.has(n));
51468
- if (removed.length > 0 && added.length > 0) hints.add("rename");
51469
- else if (added.length > 0) hints.add("add");
51470
- else if (removed.length > 0) hints.add("delete");
51471
- }
51472
- return [...hints];
51473
- }
51474
- function deriveDeclaredRenames(oldStr, newStr) {
51475
- if (oldStr == null || newStr == null) return [];
51476
- const oldNames = [...oldStr.matchAll(DECL_RE)].map((m) => m[1]);
51477
- const newNames = [...newStr.matchAll(DECL_RE)].map((m) => m[1]);
51478
- const oldSet = new Set(oldNames);
51479
- const newSet = new Set(newNames);
51480
- const removed = [...new Set(oldNames.filter((n) => !newSet.has(n)))];
51481
- const added = [...new Set(newNames.filter((n) => !oldSet.has(n)))];
51482
- if (removed.length === 1 && added.length === 1) {
51483
- return [{ from: removed[0], to: added[0] }];
51484
- }
51485
- return [];
51486
- }
51487
- var CausalBuffer = class {
51488
- constructor(windowMs = 5e3, maxAgeMs = 3e4) {
51489
- this.windowMs = windowMs;
51490
- this.maxAgeMs = maxAgeMs;
51491
- }
51492
- events = [];
51493
- record(e) {
51494
- this.events.push(e);
51495
- this.prune(e.ts);
51496
- }
51497
- prune(now) {
51498
- const cutoff = now - this.maxAgeMs;
51499
- if (this.events.length > 0 && this.events[0].ts < cutoff) {
51500
- this.events = this.events.filter((e) => e.ts >= cutoff);
51501
- }
51502
- }
51503
- /**
51504
- * Correlate a reindex flush (the changed relPaths at instant `t`) to a causal
51505
- * context, if any buffered tool event touched one of those paths within the
51506
- * window. The most recent match wins for session/tool; hints are unioned.
51507
- */
51508
- correlate(relPaths, t) {
51509
- const set2 = new Set(relPaths);
51510
- const matches = this.events.filter(
51511
- (e) => set2.has(e.relPath) && Math.abs(e.ts - t) <= this.windowMs
51512
- );
51513
- if (matches.length === 0) return null;
51514
- matches.sort((a, b) => b.ts - a.ts);
51515
- const top = matches[0];
51516
- const hints = /* @__PURE__ */ new Set();
51517
- const renames = [];
51518
- const seen = /* @__PURE__ */ new Set();
51519
- for (const m of matches) {
51520
- for (const h of m.intentHints) hints.add(h);
51521
- for (const r of m.declaredRenames) {
51522
- const key = `${r.from}->${r.to}`;
51523
- if (!seen.has(key)) {
51524
- seen.add(key);
51525
- renames.push(r);
51526
- }
51527
- }
51528
- }
51529
- return {
51530
- sessionId: top.sessionId,
51531
- tool: top.tool,
51532
- intentHints: [...hints],
51533
- declaredRenames: renames
51534
- };
51535
- }
51536
- size() {
51537
- return this.events.length;
51538
- }
51539
- };
51540
-
51541
52061
  // src/profile.ts
51542
52062
  init_src2();
51543
52063
  init_paths();
51544
- import { existsSync as existsSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
52064
+ import { existsSync as existsSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
51545
52065
  import { createHash as createHash12 } from "node:crypto";
51546
- import { join as join21 } from "node:path";
52066
+ import { join as join22 } from "node:path";
51547
52067
 
51548
52068
  // src/git-remote.ts
51549
52069
  init_src();
51550
- import { existsSync as existsSync16, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
51551
- import { isAbsolute as isAbsolute3, join as join20, resolve as resolve5 } from "node:path";
52070
+ import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
52071
+ import { isAbsolute as isAbsolute3, join as join21, resolve as resolve5 } from "node:path";
51552
52072
  function resolveGitDir(root) {
51553
- const dotGit = join20(root, ".git");
52073
+ const dotGit = join21(root, ".git");
51554
52074
  try {
51555
52075
  const st = statSync4(dotGit);
51556
52076
  if (st.isDirectory()) return dotGit;
51557
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync15(dotGit, "utf8"));
52077
+ const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync16(dotGit, "utf8"));
51558
52078
  if (!m) return null;
51559
52079
  const dir = m[1];
51560
52080
  return isAbsolute3(dir) ? dir : resolve5(root, dir);
@@ -51563,22 +52083,22 @@ function resolveGitDir(root) {
51563
52083
  }
51564
52084
  }
51565
52085
  function gitConfigPath(gitDir) {
51566
- const commondirFile = join20(gitDir, "commondir");
51567
- if (existsSync16(commondirFile)) {
51568
- const common = readFileSync15(commondirFile, "utf8").trim();
52086
+ const commondirFile = join21(gitDir, "commondir");
52087
+ if (existsSync17(commondirFile)) {
52088
+ const common = readFileSync16(commondirFile, "utf8").trim();
51569
52089
  const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
51570
- return join20(commonDir, "config");
52090
+ return join21(commonDir, "config");
51571
52091
  }
51572
- return join20(gitDir, "config");
52092
+ return join21(gitDir, "config");
51573
52093
  }
51574
52094
  function readRemotes(root) {
51575
52095
  const gitDir = resolveGitDir(root);
51576
52096
  if (!gitDir) return [];
51577
52097
  const cfgPath = gitConfigPath(gitDir);
51578
- if (!existsSync16(cfgPath)) return [];
52098
+ if (!existsSync17(cfgPath)) return [];
51579
52099
  let txt;
51580
52100
  try {
51581
- txt = readFileSync15(cfgPath, "utf8");
52101
+ txt = readFileSync16(cfgPath, "utf8");
51582
52102
  } catch {
51583
52103
  return [];
51584
52104
  }
@@ -51601,6 +52121,10 @@ function detectRepoLocator(root, remote) {
51601
52121
  function workspaceId(root) {
51602
52122
  return "wp_" + createHash12("sha256").update(root).digest("hex").slice(0, 12);
51603
52123
  }
52124
+ function sessionOriginKey(sessionId) {
52125
+ if (!sessionId) return void 0;
52126
+ return "ws_" + createHash12("sha256").update(sessionId).digest("hex").slice(0, 12);
52127
+ }
51604
52128
  function refreshRepoLocator(root, profile) {
51605
52129
  const detected = detectRepoLocator(root, profile.repoRemote);
51606
52130
  if (!detected || detected === profile.repoLocator) return false;
@@ -51610,13 +52134,13 @@ function refreshRepoLocator(root, profile) {
51610
52134
  }
51611
52135
  function loadProfile(root) {
51612
52136
  const p = workspacePaths(root);
51613
- if (!existsSync17(p.workspaceJson)) return null;
51614
- return JSON.parse(readFileSync16(p.workspaceJson, "utf8"));
52137
+ if (!existsSync18(p.workspaceJson)) return null;
52138
+ return JSON.parse(readFileSync17(p.workspaceJson, "utf8"));
51615
52139
  }
51616
52140
  function saveProfile(root, profile) {
51617
52141
  const p = workspacePaths(root);
51618
52142
  ensureDir(p.configDir);
51619
- writeFileSync13(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
52143
+ writeFileSync14(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
51620
52144
  }
51621
52145
  function autodetectProfile(root) {
51622
52146
  const id = workspaceId(root);
@@ -51624,10 +52148,10 @@ function autodetectProfile(root) {
51624
52148
  const p = emptyProfile(id, name2);
51625
52149
  const locator = detectRepoLocator(root);
51626
52150
  if (locator) p.repoLocator = locator;
51627
- const pkgPath = join21(root, "package.json");
51628
- if (existsSync17(pkgPath)) {
52151
+ const pkgPath = join22(root, "package.json");
52152
+ if (existsSync18(pkgPath)) {
51629
52153
  try {
51630
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
52154
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
51631
52155
  p.languages.push("typescript", "javascript");
51632
52156
  const nodeVer = pkg.engines?.node ?? "node";
51633
52157
  p.stack.push(`node@${nodeVer}`);
@@ -51648,10 +52172,10 @@ function autodetectProfile(root) {
51648
52172
  } catch {
51649
52173
  }
51650
52174
  }
51651
- const pyproject = join21(root, "pyproject.toml");
51652
- if (existsSync17(pyproject)) {
52175
+ const pyproject = join22(root, "pyproject.toml");
52176
+ if (existsSync18(pyproject)) {
51653
52177
  try {
51654
- const txt = readFileSync16(pyproject, "utf8");
52178
+ const txt = readFileSync17(pyproject, "utf8");
51655
52179
  const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
51656
52180
  p.languages.push("python");
51657
52181
  p.stack.push(`python@${py ?? "3"}`);
@@ -51662,16 +52186,16 @@ function autodetectProfile(root) {
51662
52186
  } catch {
51663
52187
  }
51664
52188
  }
51665
- const reqs = join21(root, "requirements.txt");
51666
- if (existsSync17(reqs)) {
52189
+ const reqs = join22(root, "requirements.txt");
52190
+ if (existsSync18(reqs)) {
51667
52191
  if (!p.languages.includes("python")) p.languages.push("python");
51668
52192
  if (!p.stack.includes("python@3")) p.stack.push("python@3");
51669
52193
  }
51670
- if (existsSync17(join21(root, "go.mod"))) {
52194
+ if (existsSync18(join22(root, "go.mod"))) {
51671
52195
  p.languages.push("go");
51672
52196
  p.stack.push("go");
51673
52197
  }
51674
- if (existsSync17(join21(root, "Cargo.toml"))) {
52198
+ if (existsSync18(join22(root, "Cargo.toml"))) {
51675
52199
  p.languages.push("rust");
51676
52200
  p.stack.push("rust");
51677
52201
  }
@@ -51680,6 +52204,160 @@ function autodetectProfile(root) {
51680
52204
  return p;
51681
52205
  }
51682
52206
 
52207
+ // src/witness-queue.ts
52208
+ import { readFileSync as readFileSync18, renameSync as renameSync2, writeFileSync as writeFileSync15 } from "node:fs";
52209
+ import { dirname as dirname9, join as join23 } from "node:path";
52210
+ var WITNESS_QUEUE_CAP = 500;
52211
+ var WITNESS_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
52212
+ var WITNESS_MAX_ATTEMPTS = 5;
52213
+ function witnessQueuePath(workspaceConfigDir) {
52214
+ return join23(workspaceConfigDir, "witness-queue.json");
52215
+ }
52216
+ function loadWitnessQueue(path2) {
52217
+ try {
52218
+ const raw2 = JSON.parse(readFileSync18(path2, "utf8"));
52219
+ if (!Array.isArray(raw2)) return [];
52220
+ return raw2.filter(
52221
+ (w) => !!w && typeof w === "object" && typeof w.nodeId === "string" && typeof w.witnessKey === "string"
52222
+ );
52223
+ } catch {
52224
+ return [];
52225
+ }
52226
+ }
52227
+ function saveWitnessQueue(path2, queue) {
52228
+ try {
52229
+ const tmp = join23(dirname9(path2), `.${Date.now()}.witness-queue.tmp`);
52230
+ writeFileSync15(tmp, JSON.stringify(queue), "utf8");
52231
+ renameSync2(tmp, path2);
52232
+ } catch {
52233
+ }
52234
+ }
52235
+ function pruneWitnessQueue(queue, now) {
52236
+ const live = queue.filter(
52237
+ (w) => now - w.ts < WITNESS_TTL_MS && w.attempts < WITNESS_MAX_ATTEMPTS
52238
+ );
52239
+ return live.length > WITNESS_QUEUE_CAP ? live.slice(live.length - WITNESS_QUEUE_CAP) : live;
52240
+ }
52241
+ function enqueueWitnesses(queue, fresh, now) {
52242
+ const byKey = new Map(queue.map((w) => [`${w.channel}:${w.witnessKey}`, w]));
52243
+ for (const f of fresh) {
52244
+ const k = `${f.channel}:${f.witnessKey}`;
52245
+ const existing = byKey.get(k);
52246
+ if (existing) existing.attempts += 1;
52247
+ else byKey.set(k, { ...f, ts: now, attempts: 1 });
52248
+ }
52249
+ return pruneWitnessQueue([...byKey.values()], now);
52250
+ }
52251
+ function pendingFor(queue, channel) {
52252
+ return queue.filter((w) => w.channel === channel);
52253
+ }
52254
+ function retireWitnesses(queue, channel, settledKeys) {
52255
+ return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
52256
+ }
52257
+
52258
+ // src/causal.ts
52259
+ var SUPPRESSORS = [
52260
+ { kind: "ts-nocheck", re: /@ts-nocheck\b/g },
52261
+ { kind: "ts-ignore", re: /@ts-(?:ignore|expect-error)\b/g },
52262
+ { kind: "eslint-disable", re: /eslint-disable(?:-next-line|-line)?\b/g },
52263
+ { kind: "py-ignore", re: /#\s*(?:noqa|type:\s*ignore|pyright:\s*ignore|mypy:\s*ignore|nosec|pragma:\s*no\s*cover)\b/gi },
52264
+ { kind: "test-skip", re: /\bx(?:it|describe|test)\s*\(|\.(?:skip|todo)\s*\(|@(?:pytest\.mark\.skip|unittest\.skip)|\bxfail\b|\bt\.Skip\s*\(/g },
52265
+ { kind: "lint-allow", re: /\/\/\s*nolint\b|#\[allow\(|istanbul\s+ignore|c8\s+ignore/g },
52266
+ { kind: "config-loosen", re: /"strict"\s*:\s*false|"skipLibCheck"\s*:\s*true|ignore_errors\s*=\s*True/gi }
52267
+ ];
52268
+ function detectSuppression(oldStr, newStr) {
52269
+ if (!newStr) return null;
52270
+ const old = oldStr ?? "";
52271
+ for (const { kind, re } of SUPPRESSORS) {
52272
+ const inNew = (newStr.match(re) ?? []).length;
52273
+ const inOld = (old.match(re) ?? []).length;
52274
+ if (inNew > inOld) return kind;
52275
+ }
52276
+ return null;
52277
+ }
52278
+ var DECL_RE = /\b(?:function|class|const|let|var|def|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g;
52279
+ function deriveIntentHints(tool, oldStr, newStr) {
52280
+ const hints = /* @__PURE__ */ new Set();
52281
+ if (tool === "Write" && (oldStr == null || oldStr === "")) hints.add("add");
52282
+ if (detectSuppression(oldStr, newStr)) hints.add("suppress");
52283
+ if (oldStr != null && newStr != null) {
52284
+ const oldNames = new Set([...oldStr.matchAll(DECL_RE)].map((m) => m[1]));
52285
+ const newNames = new Set([...newStr.matchAll(DECL_RE)].map((m) => m[1]));
52286
+ const removed = [...oldNames].filter((n) => !newNames.has(n));
52287
+ const added = [...newNames].filter((n) => !oldNames.has(n));
52288
+ if (removed.length > 0 && added.length > 0) hints.add("rename");
52289
+ else if (added.length > 0) hints.add("add");
52290
+ else if (removed.length > 0) hints.add("delete");
52291
+ }
52292
+ return [...hints];
52293
+ }
52294
+ function deriveDeclaredRenames(oldStr, newStr) {
52295
+ if (oldStr == null || newStr == null) return [];
52296
+ const oldNames = [...oldStr.matchAll(DECL_RE)].map((m) => m[1]);
52297
+ const newNames = [...newStr.matchAll(DECL_RE)].map((m) => m[1]);
52298
+ const oldSet = new Set(oldNames);
52299
+ const newSet = new Set(newNames);
52300
+ const removed = [...new Set(oldNames.filter((n) => !newSet.has(n)))];
52301
+ const added = [...new Set(newNames.filter((n) => !oldSet.has(n)))];
52302
+ if (removed.length === 1 && added.length === 1) {
52303
+ return [{ from: removed[0], to: added[0] }];
52304
+ }
52305
+ return [];
52306
+ }
52307
+ var CausalBuffer = class {
52308
+ constructor(windowMs = 5e3, maxAgeMs = 3e4) {
52309
+ this.windowMs = windowMs;
52310
+ this.maxAgeMs = maxAgeMs;
52311
+ }
52312
+ events = [];
52313
+ record(e) {
52314
+ this.events.push(e);
52315
+ this.prune(e.ts);
52316
+ }
52317
+ prune(now) {
52318
+ const cutoff = now - this.maxAgeMs;
52319
+ if (this.events.length > 0 && this.events[0].ts < cutoff) {
52320
+ this.events = this.events.filter((e) => e.ts >= cutoff);
52321
+ }
52322
+ }
52323
+ /**
52324
+ * Correlate a reindex flush (the changed relPaths at instant `t`) to a causal
52325
+ * context, if any buffered tool event touched one of those paths within the
52326
+ * window. The most recent match wins for session/tool; hints are unioned.
52327
+ */
52328
+ correlate(relPaths, t) {
52329
+ const set2 = new Set(relPaths);
52330
+ const matches = this.events.filter(
52331
+ (e) => set2.has(e.relPath) && Math.abs(e.ts - t) <= this.windowMs
52332
+ );
52333
+ if (matches.length === 0) return null;
52334
+ matches.sort((a, b) => b.ts - a.ts);
52335
+ const top = matches[0];
52336
+ const hints = /* @__PURE__ */ new Set();
52337
+ const renames = [];
52338
+ const seen = /* @__PURE__ */ new Set();
52339
+ for (const m of matches) {
52340
+ for (const h of m.intentHints) hints.add(h);
52341
+ for (const r of m.declaredRenames) {
52342
+ const key = `${r.from}->${r.to}`;
52343
+ if (!seen.has(key)) {
52344
+ seen.add(key);
52345
+ renames.push(r);
52346
+ }
52347
+ }
52348
+ }
52349
+ return {
52350
+ sessionId: top.sessionId,
52351
+ tool: top.tool,
52352
+ intentHints: [...hints],
52353
+ declaredRenames: renames
52354
+ };
52355
+ }
52356
+ size() {
52357
+ return this.events.length;
52358
+ }
52359
+ };
52360
+
51683
52361
  // src/engine.ts
51684
52362
  init_config();
51685
52363
  init_cloud_auth();
@@ -51829,7 +52507,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51829
52507
  }
51830
52508
 
51831
52509
  // src/engine.ts
51832
- var DAEMON_VERSION = true ? "2.0.2-dev.125" : "2.0.0-alpha.0";
52510
+ var DAEMON_VERSION = true ? "2.0.2-dev.133" : "2.0.0-alpha.0";
51833
52511
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51834
52512
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51835
52513
  var GIT_OP_MUTE_MS = 4e3;
@@ -51839,8 +52517,8 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
51839
52517
  function appendIdentityAudit(path2, record2, line) {
51840
52518
  if (!record2.accepted && record2.score <= 0) return;
51841
52519
  try {
51842
- if (existsSync18(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
51843
- renameSync2(path2, `${path2}.1`);
52520
+ if (existsSync19(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52521
+ renameSync3(path2, `${path2}.1`);
51844
52522
  }
51845
52523
  appendFileSync2(path2, line);
51846
52524
  } catch {
@@ -51849,14 +52527,33 @@ function appendIdentityAudit(path2, record2, line) {
51849
52527
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
51850
52528
  function loadTurnCursors(path2) {
51851
52529
  try {
51852
- return new Map(Object.entries(JSON.parse(readFileSync17(path2, "utf8"))));
52530
+ const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
52531
+ return new Map(
52532
+ Object.entries(raw2).map(([k, v]) => [k, typeof v === "string" ? v : String(v?.uuid ?? "")])
52533
+ );
52534
+ } catch {
52535
+ return /* @__PURE__ */ new Map();
52536
+ }
52537
+ }
52538
+ function loadTurnOffsets(path2) {
52539
+ try {
52540
+ const raw2 = JSON.parse(readFileSync19(path2, "utf8"));
52541
+ const out2 = /* @__PURE__ */ new Map();
52542
+ for (const [k, v] of Object.entries(raw2)) {
52543
+ const off = typeof v === "object" && v !== null ? v.offset : void 0;
52544
+ if (typeof off === "number" && off >= 0) out2.set(k, off);
52545
+ }
52546
+ return out2;
51853
52547
  } catch {
51854
52548
  return /* @__PURE__ */ new Map();
51855
52549
  }
51856
52550
  }
51857
- function saveTurnCursors(path2, cursors) {
52551
+ function saveTurnCursors(path2, cursors, offsets) {
51858
52552
  try {
51859
- writeFileSync14(path2, JSON.stringify(Object.fromEntries(cursors)), "utf8");
52553
+ const merged = {};
52554
+ for (const [k, uuid3] of cursors) merged[k] = { uuid: uuid3, offset: offsets.get(k) ?? 0 };
52555
+ for (const [k, offset] of offsets) if (!merged[k]) merged[k] = { uuid: "", offset };
52556
+ writeFileSync16(path2, JSON.stringify(merged), "utf8");
51860
52557
  } catch {
51861
52558
  }
51862
52559
  }
@@ -51878,7 +52575,7 @@ function gitSourceWatchTargets(root) {
51878
52575
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
51879
52576
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
51880
52577
  );
51881
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join22(root, d) + sep4));
52578
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join24(root, d) + sep4));
51882
52579
  } catch {
51883
52580
  }
51884
52581
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -51890,19 +52587,19 @@ function gitSourceWatchTargets(root) {
51890
52587
  if (!f.startsWith(prefix)) continue;
51891
52588
  const rest2 = f.slice(prefix.length);
51892
52589
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
51893
- else targets.add(join22(root, f));
52590
+ else targets.add(join24(root, f));
51894
52591
  }
51895
52592
  for (const c of children) {
51896
- if (IGNORED_PATH.test(join22(root, c) + sep4)) continue;
52593
+ if (IGNORED_PATH.test(join24(root, c) + sep4)) continue;
51897
52594
  if (hasIgnoredChild(c)) addUnder(c);
51898
- else targets.add(join22(root, c));
52595
+ else targets.add(join24(root, c));
51899
52596
  }
51900
52597
  };
51901
52598
  addUnder("");
51902
52599
  if (targets.size > 0) return [...targets];
51903
52600
  } catch {
51904
52601
  }
51905
- return readdirSync8(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join22(root, String(e.name)) + sep4)).map((e) => join22(root, String(e.name)));
52602
+ 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)));
51906
52603
  }
51907
52604
  function createWorkspaceEngine(opts) {
51908
52605
  const paths = workspacePaths(opts.workspaceRoot);
@@ -51917,6 +52614,10 @@ function createWorkspaceEngine(opts) {
51917
52614
  refreshRepoLocator(opts.workspaceRoot, profile);
51918
52615
  }
51919
52616
  const store = openGraphStore({ path: paths.castalia });
52617
+ let witnessQueue = pruneWitnessQueue(
52618
+ loadWitnessQueue(witnessQueuePath(paths.configDir)),
52619
+ Date.now()
52620
+ );
51920
52621
  const log = openEventLog({ path: paths.eventLog });
51921
52622
  const bundled = import.meta.url.endsWith(".mjs");
51922
52623
  const passWorker = bundled && paths.castalia !== ":memory:" ? new PassWorker({
@@ -52056,7 +52757,7 @@ function createWorkspaceEngine(opts) {
52056
52757
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
52057
52758
  let episodeId2;
52058
52759
  if (srcPaths.length > 0) {
52059
- const abs = srcPaths.map((p) => join22(opts.workspaceRoot, p));
52760
+ const abs = srcPaths.map((p) => join24(opts.workspaceRoot, p));
52060
52761
  try {
52061
52762
  const r = await runReindexPass(
52062
52763
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -52092,8 +52793,8 @@ function createWorkspaceEngine(opts) {
52092
52793
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
52093
52794
  );
52094
52795
  };
52095
- const gitDir = join22(opts.workspaceRoot, ".git");
52096
- if (existsSync18(gitDir)) {
52796
+ const gitDir = join24(opts.workspaceRoot, ".git");
52797
+ if (existsSync19(gitDir)) {
52097
52798
  stopGit = startGitSensor(gitDir, (ev) => {
52098
52799
  void handleGitEvent(ev).catch((err2) => {
52099
52800
  console.warn("[errata] git event handler failed:", err2);
@@ -52163,10 +52864,10 @@ function createWorkspaceEngine(opts) {
52163
52864
  });
52164
52865
  doneRender?.();
52165
52866
  writeContextFile(opts.workspaceRoot, body2);
52166
- const target = join22(opts.workspaceRoot, "AGENTS.md");
52867
+ const target = join24(opts.workspaceRoot, "AGENTS.md");
52167
52868
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
52168
52869
  if (elicit) {
52169
- writePrimingHandles(join22(paths.configDir, "priming-handles.json"), [
52870
+ writePrimingHandles(join24(paths.configDir, "priming-handles.json"), [
52170
52871
  ...snapshot.recentProblems.map((r) => r.node),
52171
52872
  // Resolved-band handles: the ✓ problem AND its Solution are citable
52172
52873
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -52257,6 +52958,21 @@ function createWorkspaceEngine(opts) {
52257
52958
  } catch (err2) {
52258
52959
  console.warn("[errata] anchor backfill failed:", err2);
52259
52960
  }
52961
+ try {
52962
+ const c = backfillConstraintKind(store, {
52963
+ root: opts.workspaceRoot,
52964
+ configDir: paths.configDir,
52965
+ now: Date.now()
52966
+ });
52967
+ if (!c.skipped && (c.stamped > 0 || c.detached > 0)) {
52968
+ console.log(
52969
+ `[errata] constraint backfill: ${c.stamped} design tension(s) marked` + (c.detached > 0 ? `; ${c.detached} fabricated resolution(s) detached, ${c.reopened} reopened` : "") + (c.witnessed > 0 ? `; ${c.witnessed} cited discharge(s) kept` : "") + (c.cloudTwins.length > 0 ? ` \u2014 ${c.cloudTwins.length} already contributed; run scripts/repair-cloud-constraints.ts to repair the cloud twins` : "")
52970
+ );
52971
+ refreshContextNow();
52972
+ }
52973
+ } catch (err2) {
52974
+ console.warn("[errata] constraint backfill failed:", err2);
52975
+ }
52260
52976
  const report = runNightlyPipeline(store);
52261
52977
  try {
52262
52978
  const m = mergeDuplicateProblems(store, { ts: Date.now() });
@@ -52356,8 +53072,9 @@ function createWorkspaceEngine(opts) {
52356
53072
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
52357
53073
  });
52358
53074
  };
52359
- const turnCursorPath = join22(paths.configDir, "turn-cursors.json");
53075
+ const turnCursorPath = join24(paths.configDir, "turn-cursors.json");
52360
53076
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
53077
+ const turnOffset = loadTurnOffsets(turnCursorPath);
52361
53078
  const sessionLastProblem = /* @__PURE__ */ new Map();
52362
53079
  const sessionThreads = /* @__PURE__ */ new Map();
52363
53080
  const harvestTexts = async (sessionId, items) => {
@@ -52379,7 +53096,7 @@ function createWorkspaceEngine(opts) {
52379
53096
  const t = Date.now();
52380
53097
  let processedTurns = 0;
52381
53098
  const elicit = isEdgeElicitationEnabled();
52382
- const handleMap = elicit ? readPrimingHandles(join22(paths.configDir, "priming-handles.json")) : {};
53099
+ const handleMap = elicit ? readPrimingHandles(join24(paths.configDir, "priming-handles.json")) : {};
52383
53100
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
52384
53101
  const toRel = (abs) => {
52385
53102
  const p = abs.replace(/\\/g, "/");
@@ -52421,7 +53138,7 @@ function createWorkspaceEngine(opts) {
52421
53138
  ts: t
52422
53139
  });
52423
53140
  if (r.created || r.corroborated) minted++;
52424
- sessionLastProblem.set(sessionId, designProblemId(flag.problem));
53141
+ if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52425
53142
  if (r.created || r.corroborated) {
52426
53143
  try {
52427
53144
  if (editedTurnFile) {
@@ -52501,11 +53218,13 @@ function createWorkspaceEngine(opts) {
52501
53218
  const dedupPath = anchorPath ?? hintPath;
52502
53219
  if (dedupPath) {
52503
53220
  try {
52504
- const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t);
53221
+ const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t, p.kind);
52505
53222
  if (dupId) {
52506
53223
  minted++;
52507
- sessionLastProblem.set(sessionId, dupId);
52508
- inScopeProblemId = dupId;
53224
+ if (p.kind !== "constraint") {
53225
+ sessionLastProblem.set(sessionId, dupId);
53226
+ inScopeProblemId = dupId;
53227
+ }
52509
53228
  if (p.threadId) threads.set(p.threadId, dupId);
52510
53229
  continue;
52511
53230
  }
@@ -52519,8 +53238,10 @@ function createWorkspaceEngine(opts) {
52519
53238
  });
52520
53239
  if (r.created || r.corroborated) {
52521
53240
  minted++;
52522
- sessionLastProblem.set(sessionId, designProblemId(p.statement));
52523
- inScopeProblemId = designProblemId(p.statement);
53241
+ if (p.kind !== "constraint") {
53242
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
53243
+ inScopeProblemId = designProblemId(p.statement);
53244
+ }
52524
53245
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
52525
53246
  try {
52526
53247
  if (anchorPath) {
@@ -52717,18 +53438,83 @@ function createWorkspaceEngine(opts) {
52717
53438
  mintCiteEdge(solId, targetId, sl.relation, 0.4, { solutionStructure: true, hypothesis: true });
52718
53439
  }
52719
53440
  }
52720
- if (plan.refutes.length > 0 && typeof cloud.reportContradictions === "function") {
52721
- void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: stampWitnessOrigin(plan.refutes, profile.id) }).then((r) => {
52722
- if (r?.recorded) console.log(`[errata] refute: ${r.recorded} contradiction(s) recorded`);
52723
- }).catch(
52724
- (err2) => console.warn("[errata] refute transport failed (continuing):", err2 instanceof Error ? err2.message : err2)
53441
+ const citingSession = sessionOriginKey(sessionId);
53442
+ const mintedHere = (nodeId) => store.getNode(nodeId)?.attrs["sources"]?.[0] === sessionId;
53443
+ const sendWitnesses = async (channel, fresh, send) => {
53444
+ const queued = pendingFor(witnessQueue, channel);
53445
+ if (fresh.length === 0 && queued.length === 0) return;
53446
+ const groups = /* @__PURE__ */ new Map();
53447
+ for (const q of queued) {
53448
+ const g = groups.get(q.sessionKey) ?? [];
53449
+ g.push({ nodeId: q.nodeId, witnessKey: q.witnessKey });
53450
+ groups.set(q.sessionKey, g);
53451
+ }
53452
+ const freshGroup = groups.get(citingSession) ?? [];
53453
+ for (const f of fresh) {
53454
+ if (freshGroup.some((x) => x.witnessKey === f.witnessKey)) continue;
53455
+ freshGroup.push(f);
53456
+ }
53457
+ groups.set(citingSession, freshGroup);
53458
+ const settled = /* @__PURE__ */ new Set();
53459
+ const stillUnmatched = [];
53460
+ let recorded = 0, unmatched = 0, duplicate = 0, selfGated = 0;
53461
+ for (const [session, items2] of groups) {
53462
+ if (items2.length === 0) continue;
53463
+ try {
53464
+ const raw2 = await send(items2, session);
53465
+ const d = {
53466
+ recorded: raw2?.recorded ?? 0,
53467
+ unmatched: raw2?.unmatched ?? 0,
53468
+ duplicate: raw2?.duplicate ?? 0,
53469
+ selfGated: raw2?.selfGated ?? 0,
53470
+ unmatchedIds: raw2?.unmatchedIds ?? []
53471
+ };
53472
+ recorded += d.recorded;
53473
+ unmatched += d.unmatched;
53474
+ duplicate += d.duplicate;
53475
+ selfGated += d.selfGated;
53476
+ const missing = new Set(d.unmatchedIds);
53477
+ for (const it of items2) {
53478
+ if (missing.has(it.nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session });
53479
+ else settled.add(it.witnessKey);
53480
+ }
53481
+ } catch (err2) {
53482
+ console.warn(`[errata] ${channel} transport failed (witnesses kept for retry):`, err2 instanceof Error ? err2.message : err2);
53483
+ for (const it of items2) stillUnmatched.push({ channel, ...it, sessionKey: session });
53484
+ }
53485
+ }
53486
+ witnessQueue = retireWitnesses(witnessQueue, channel, settled);
53487
+ witnessQueue = enqueueWitnesses(witnessQueue, stillUnmatched, Date.now());
53488
+ saveWitnessQueue(witnessQueuePath(paths.configDir), witnessQueue);
53489
+ console.log(
53490
+ `[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "")
53491
+ );
53492
+ };
53493
+ if (typeof cloud.reportContradictions === "function") {
53494
+ await sendWitnesses(
53495
+ "contradict",
53496
+ plan.refutes,
53497
+ (items2, session) => cloud.reportContradictions({
53498
+ daemonVersion: DAEMON_VERSION,
53499
+ projectId: profile.id,
53500
+ ...session ? { sessionId: session } : {},
53501
+ items: stampWitnessOrigin(items2, profile.id)
53502
+ })
52725
53503
  );
52726
53504
  }
52727
- if (plan.corroborations.length > 0 && typeof cloud.reportCorroborations === "function") {
52728
- void cloud.reportCorroborations({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: stampWitnessOrigin(plan.corroborations, profile.id) }).then((r) => {
52729
- if (r?.recorded) console.log(`[errata] corroborate: ${r.recorded} corroboration(s) recorded`);
52730
- }).catch(
52731
- (err2) => console.warn("[errata] corroboration transport failed (continuing):", err2 instanceof Error ? err2.message : err2)
53505
+ if (typeof cloud.reportCorroborations === "function") {
53506
+ const emit = plan.corroborations.filter((c) => !mintedHere(c.nodeId));
53507
+ const skipped = plan.corroborations.length - emit.length;
53508
+ if (skipped > 0) console.log(`[errata] corroborate: ${skipped} skipped (this session authored the node)`);
53509
+ await sendWitnesses(
53510
+ "corroborate",
53511
+ emit,
53512
+ (items2, session) => cloud.reportCorroborations({
53513
+ daemonVersion: DAEMON_VERSION,
53514
+ projectId: profile.id,
53515
+ ...session ? { sessionId: session } : {},
53516
+ items: stampWitnessOrigin(items2, profile.id)
53517
+ })
52732
53518
  );
52733
53519
  }
52734
53520
  } catch (err2) {
@@ -52757,17 +53543,28 @@ function createWorkspaceEngine(opts) {
52757
53543
  }
52758
53544
  };
52759
53545
  const harvestTurns = async (sessionId, transcriptPath) => {
53546
+ const known = turnOffset.get(sessionId);
52760
53547
  let turns;
53548
+ let nextOffset;
52761
53549
  try {
52762
- turns = readAssistantTurns(transcriptPath);
53550
+ if (known === void 0) {
53551
+ turns = readAssistantTurns(transcriptPath);
53552
+ nextOffset = transcriptSize(transcriptPath);
53553
+ } else {
53554
+ ({ turns, nextOffset } = readAssistantTurnsFrom(transcriptPath, known));
53555
+ }
52763
53556
  } catch {
52764
53557
  return;
52765
53558
  }
52766
53559
  const fresh = turnsSince(turns, lastTurnUuid.get(sessionId));
52767
- if (fresh.length === 0) return;
53560
+ turnOffset.set(sessionId, nextOffset);
53561
+ if (fresh.length === 0) {
53562
+ saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
53563
+ return;
53564
+ }
52768
53565
  lastTurnUuid.set(sessionId, turns[turns.length - 1].uuid);
52769
53566
  await harvestTexts(sessionId, fresh);
52770
- saveTurnCursors(turnCursorPath, lastTurnUuid);
53567
+ saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
52771
53568
  };
52772
53569
  const seenMessageIds = /* @__PURE__ */ new Set();
52773
53570
  const onMessage = (e) => {
@@ -52893,9 +53690,24 @@ function createWorkspaceEngine(opts) {
52893
53690
  return typeof cloudId === "string" && cloudId !== p.id ? [p.id, cloudId] : [p.id];
52894
53691
  });
52895
53692
  const pins = profile?.projectId ? await cloud.getSkillPins(profile.projectId).then((r) => r.pins).catch(() => []) : [];
52896
- const result = await syncSkills(paths, cloud, skillSeed, pins);
53693
+ const anchorMap = buildBucketTokenAnchors(
53694
+ profile.languages ?? [],
53695
+ loadConfig().consent.contributePackages ? store.findNodesByLabel("Package").map((p) => ({
53696
+ name: String(p.attrs["name"] ?? ""),
53697
+ purl: String(p.attrs["purl"] ?? p.id)
53698
+ })) : []
53699
+ );
53700
+ const stackTokens = new Set((profile.stack ?? []).map((t) => canonicalizeToken(String(t))).filter(Boolean));
53701
+ const techSeed = [
53702
+ ...new Set(
53703
+ Object.entries(anchorMap).filter(([tok, a]) => a.startsWith("lang:") || stackTokens.has(tok)).map(([, a]) => a)
53704
+ )
53705
+ ].slice(0, 24);
53706
+ const result = await syncSkills(paths, cloud, skillSeed, pins, techSeed);
52897
53707
  try {
52898
- emitAndProjectSkills(opts.workspaceRoot, emitInputsFromManifest(paths.configDir, paths.skillsManifest));
53708
+ const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
53709
+ emitAndProjectSkills(opts.workspaceRoot, inputs);
53710
+ writePrimingHandles(join24(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
52899
53711
  } catch (err2) {
52900
53712
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
52901
53713
  }
@@ -53082,7 +53894,7 @@ function createWorkspaceEngine(opts) {
53082
53894
  console.log(
53083
53895
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
53084
53896
  );
53085
- const pending = existsSync18(paths.outbox) ? readdirSync8(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
53897
+ const pending = existsSync19(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
53086
53898
  return { uploaded: 0, failed: 0, remaining: pending };
53087
53899
  }
53088
53900
  try {
@@ -53169,7 +53981,7 @@ async function startDaemon(opts) {
53169
53981
  reviewUrl: () => webUiUrl + "/review"
53170
53982
  });
53171
53983
  const writeLockFile = (url2) => {
53172
- writeFileSync15(
53984
+ writeFileSync17(
53173
53985
  engine.paths.daemonLock,
53174
53986
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
53175
53987
  "utf8"
@@ -53212,7 +54024,7 @@ async function startDaemon(opts) {
53212
54024
  );
53213
54025
  await engine.stop();
53214
54026
  try {
53215
- if (existsSync19(engine.paths.daemonLock)) {
54027
+ if (existsSync20(engine.paths.daemonLock)) {
53216
54028
  }
53217
54029
  } catch {
53218
54030
  }
@@ -53229,16 +54041,16 @@ async function listenServer(fetchFn, port) {
53229
54041
 
53230
54042
  // src/registry.ts
53231
54043
  init_paths();
53232
- import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
53233
- import { join as join23 } from "node:path";
54044
+ import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "node:fs";
54045
+ import { join as join25 } from "node:path";
53234
54046
  function registryPath() {
53235
- return process.env["ERRATA_REGISTRY_PATH"] ?? join23(globalDir(), "workspaces.json");
54047
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join25(globalDir(), "workspaces.json");
53236
54048
  }
53237
54049
  function read() {
53238
54050
  const p = registryPath();
53239
- if (!existsSync20(p)) return { version: 1, workspaces: {} };
54051
+ if (!existsSync21(p)) return { version: 1, workspaces: {} };
53240
54052
  try {
53241
- const parsed = JSON.parse(readFileSync18(p, "utf8"));
54053
+ const parsed = JSON.parse(readFileSync20(p, "utf8"));
53242
54054
  return { version: 1, workspaces: parsed.workspaces ?? {} };
53243
54055
  } catch {
53244
54056
  return { version: 1, workspaces: {} };
@@ -53246,7 +54058,7 @@ function read() {
53246
54058
  }
53247
54059
  function write(reg) {
53248
54060
  ensureDir(globalDir());
53249
- writeFileSync16(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54061
+ writeFileSync18(registryPath(), JSON.stringify(reg, null, 2), "utf8");
53250
54062
  }
53251
54063
  function registerWorkspace(profile, root, now = Date.now()) {
53252
54064
  const reg = read();
@@ -53263,7 +54075,7 @@ function pruneMissingWorkspaces() {
53263
54075
  const reg = read();
53264
54076
  const removed = [];
53265
54077
  for (const [id, entry] of Object.entries(reg.workspaces)) {
53266
- if (!existsSync20(entry.path)) {
54078
+ if (!existsSync21(entry.path)) {
53267
54079
  removed.push(entry);
53268
54080
  delete reg.workspaces[id];
53269
54081
  }
@@ -53272,13 +54084,13 @@ function pruneMissingWorkspaces() {
53272
54084
  return removed;
53273
54085
  }
53274
54086
  function workspaceStatus(entry) {
53275
- const missing = !existsSync20(entry.path);
54087
+ const missing = !existsSync21(entry.path);
53276
54088
  const lockPath = workspacePaths(entry.path).daemonLock;
53277
54089
  let running = false;
53278
54090
  let webUiUrl = null;
53279
- if (existsSync20(lockPath)) {
54091
+ if (existsSync21(lockPath)) {
53280
54092
  try {
53281
- const lock = JSON.parse(readFileSync18(lockPath, "utf8"));
54093
+ const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
53282
54094
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
53283
54095
  running = true;
53284
54096
  webUiUrl = lock.webUiUrl;
@@ -53306,7 +54118,7 @@ function pidAlive(pid) {
53306
54118
  // src/multi.ts
53307
54119
  init_dist();
53308
54120
  init_src4();
53309
- import { readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync17 } from "node:fs";
54121
+ import { readFileSync as readFileSync23, unlinkSync as unlinkSync3, writeFileSync as writeFileSync19 } from "node:fs";
53310
54122
 
53311
54123
  // src/principle-sync.ts
53312
54124
  init_src4();
@@ -53334,8 +54146,8 @@ init_reconcile();
53334
54146
 
53335
54147
  // src/lockfile-auto.ts
53336
54148
  init_src();
53337
- import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
53338
- import { join as join24 } from "node:path";
54149
+ import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
54150
+ import { join as join26 } from "node:path";
53339
54151
 
53340
54152
  // src/package-index.ts
53341
54153
  init_src();
@@ -53421,10 +54233,12 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53421
54233
  const ignored = (s) => ignorePatterns.length > 0 && ignorePatterns.some((p) => s.toLowerCase().includes(p));
53422
54234
  const nodes = [];
53423
54235
  const seen = /* @__PURE__ */ new Set();
54236
+ const localIdByWireId = {};
53424
54237
  for (const label of labels) {
53425
54238
  for (const n of store.findNodesByLabel(label)) {
53426
54239
  if (n.attrs["source"] === "cloud") continue;
53427
54240
  if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
54241
+ if (typeof n.attrs["contributedAtSeq"] === "number") continue;
53428
54242
  nodes.push({
53429
54243
  ...n,
53430
54244
  // Language wire id = the shared `languageCanonicalId` (warming-spine
@@ -53449,6 +54263,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53449
54263
  anchorVisibility: "public"
53450
54264
  });
53451
54265
  seen.add(n.id);
54266
+ localIdByWireId[nodes[nodes.length - 1].id] = n.id;
53452
54267
  }
53453
54268
  }
53454
54269
  if (nodes.length === 0) return null;
@@ -53468,7 +54283,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53468
54283
  nodes,
53469
54284
  edges
53470
54285
  };
53471
- return { ...base, payloadDigest: digest(base) };
54286
+ return { ...base, payloadDigest: digest(base), localIds: [...seen], localIdByWireId };
53472
54287
  }
53473
54288
 
53474
54289
  // src/lockfile-auto.ts
@@ -53481,11 +54296,11 @@ function runLockfilePass(opts) {
53481
54296
  { file: "package-lock.json", parse: parsePackageLockJson }
53482
54297
  ];
53483
54298
  for (const c of candidates) {
53484
- const p = join24(opts.root, c.file);
53485
- if (!existsSync21(p)) continue;
54299
+ const p = join26(opts.root, c.file);
54300
+ if (!existsSync22(p)) continue;
53486
54301
  let sbom;
53487
54302
  try {
53488
- sbom = c.parse(readFileSync19(p, "utf8"));
54303
+ sbom = c.parse(readFileSync21(p, "utf8"));
53489
54304
  } catch {
53490
54305
  continue;
53491
54306
  }
@@ -53500,6 +54315,7 @@ function runLockfilePass(opts) {
53500
54315
  // src/instance-ingest.ts
53501
54316
  init_src();
53502
54317
  init_src2();
54318
+ init_src4();
53503
54319
  init_src8();
53504
54320
  init_src();
53505
54321
 
@@ -53550,6 +54366,10 @@ function stripCodebaseScope(scope) {
53550
54366
  delete s["codebase"];
53551
54367
  return s;
53552
54368
  }
54369
+ function contributedWireId(n) {
54370
+ const cloudId = n.attrs["cloudNodeId"];
54371
+ return isMembraneSaltedId(cloudId) ? cloudId : n.id;
54372
+ }
53553
54373
  function wireContextId(n) {
53554
54374
  if (n.label === "Language" && n.attrs["source"] !== "cloud") {
53555
54375
  const name2 = String(n.attrs["name"] ?? "").trim() || n.description.trim() || n.id.replace(/^lang:/, "");
@@ -53590,10 +54410,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53590
54410
  const level = opts.level ?? 1;
53591
54411
  const noveltyOpts = opts.dedupCosine != null ? { dedupCosine: opts.dedupCosine } : {};
53592
54412
  const ignored = (text) => ignorePatterns.length > 0 && ignorePatterns.some((p) => text.toLowerCase().includes(p));
54413
+ const originSessionOf = (n) => {
54414
+ const key = sessionOriginKey(n.attrs["sources"]?.[0]);
54415
+ return key ? { originSession: key } : {};
54416
+ };
53593
54417
  const shareable = (n) => ({
53594
54418
  ...n,
53595
54419
  description: generalize(n.description, { level }).text,
53596
54420
  embedding: [],
54421
+ ...originSessionOf(n),
53597
54422
  attrs: {
53598
54423
  scope: stripCodebaseScope(n.attrs["scope"]),
53599
54424
  ...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
@@ -53604,6 +54429,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53604
54429
  const seen = /* @__PURE__ */ new Set();
53605
54430
  const shippedById = /* @__PURE__ */ new Map();
53606
54431
  const anchorSources = /* @__PURE__ */ new Set();
54432
+ const semanticEndpoints = /* @__PURE__ */ new Map();
53607
54433
  const includedByLabel = /* @__PURE__ */ new Map();
53608
54434
  for (const label of INSTANCE_LABELS) {
53609
54435
  const ref = [];
@@ -53616,8 +54442,16 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53616
54442
  const contributedAtSeq = n.attrs["contributedAtSeq"];
53617
54443
  if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
53618
54444
  anchorSources.add(n.id);
54445
+ semanticEndpoints.set(n.id, contributedWireId(n));
53619
54446
  continue;
53620
54447
  }
54448
+ if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) {
54449
+ const problemShippable = store.inEdges(n.id, ["SOLVED_BY"]).some((e) => {
54450
+ const p = store.getNode(e.from);
54451
+ return p?.label === "Problem" && (seen.has(p.id) || typeof p.attrs["contributedAtSeq"] === "number");
54452
+ });
54453
+ if (!problemShippable) continue;
54454
+ }
53621
54455
  if (!noveltyAgainst(n, ref, noveltyOpts).ready) continue;
53622
54456
  ref.push(n);
53623
54457
  const wireNode = shareable(n);
@@ -53625,6 +54459,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53625
54459
  shippedById.set(n.id, wireNode);
53626
54460
  seen.add(n.id);
53627
54461
  anchorSources.add(n.id);
54462
+ semanticEndpoints.set(n.id, n.id);
53628
54463
  }
53629
54464
  }
53630
54465
  for (const n of nodes) {
@@ -53665,8 +54500,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53665
54500
  const sourceNode = store.getNode(sourceId);
53666
54501
  if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
53667
54502
  anchorDigests[sourceId] = dg;
53668
- const skippedCloudId = !seen.has(sourceId) ? sourceNode?.attrs["cloudNodeId"] : void 0;
53669
- const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
54503
+ const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
53670
54504
  for (const { e, t, wireId } of targets) {
53671
54505
  anchorEdges.push({ ...e, from: wireFrom, to: wireId, attrs: {} });
53672
54506
  if (t.attrs["source"] === "cloud" || contextSeen.has(wireId)) continue;
@@ -53683,14 +54517,28 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53683
54517
  }
53684
54518
  }
53685
54519
  }
54520
+ for (const [localId, shipped] of shippedById) {
54521
+ const local = store.getNode(localId);
54522
+ if (!local || local.label !== "Solution" || !local.description.startsWith(AUTO_MINT_PREFIX)) continue;
54523
+ const problem = store.inEdges(localId, ["SOLVED_BY"]).map((e) => store.getNode(e.from)).find((p) => p?.label === "Problem");
54524
+ if (!problem) continue;
54525
+ const inBatch = shippedById.get(problem.id);
54526
+ if (inBatch) {
54527
+ if (inBatch.anchorVisibility) shipped.anchorVisibility = inBatch.anchorVisibility;
54528
+ if (inBatch.anchor != null) shipped.anchor = inBatch.anchor;
54529
+ else delete shipped.anchor;
54530
+ } else if (isMembraneSaltedId(problem.attrs["cloudNodeId"])) {
54531
+ shipped.anchorVisibility = "private";
54532
+ delete shipped.anchor;
54533
+ }
54534
+ }
53686
54535
  const project = opts.project;
53687
54536
  if (project) {
53688
54537
  const now = Date.now();
53689
54538
  for (const sourceId of anchorSources) {
53690
54539
  const source = store.getNode(sourceId);
53691
54540
  if (!source) continue;
53692
- const skippedCloudId = !seen.has(sourceId) ? source.attrs["cloudNodeId"] : void 0;
53693
- const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
54541
+ const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
53694
54542
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
53695
54543
  const target = store.getNode(e.to);
53696
54544
  if (!target) continue;
@@ -53729,7 +54577,17 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53729
54577
  }
53730
54578
  }
53731
54579
  }
53732
- if (nodes.length === 0 && anchorEdges.length === 0) return null;
54580
+ const edges = [];
54581
+ const semanticEdgeDigests = {};
54582
+ for (const [sourceId, wireFrom] of semanticEndpoints) {
54583
+ const outs = store.outEdges(sourceId, [...INSTANCE_EDGES]).map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) => x.wireTo != null);
54584
+ if (outs.length === 0) continue;
54585
+ const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
54586
+ if (store.getNode(sourceId)?.attrs["semanticEdgesContributedDigest"] === dg) continue;
54587
+ semanticEdgeDigests[sourceId] = dg;
54588
+ for (const { e, wireTo } of outs) edges.push({ ...e, from: wireFrom, to: wireTo, attrs: {} });
54589
+ }
54590
+ if (nodes.length === 0 && anchorEdges.length === 0 && edges.length === 0) return null;
53733
54591
  let symbolSummaries;
53734
54592
  if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
53735
54593
  const lexicon = opts.lexicon;
@@ -53751,12 +54609,6 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53751
54609
  }
53752
54610
  if (count > 0) symbolSummaries = sidecar;
53753
54611
  }
53754
- const edges = [];
53755
- for (const id of seen) {
53756
- for (const e of store.outEdges(id, [...INSTANCE_EDGES])) {
53757
- if (seen.has(e.to)) edges.push({ ...e, attrs: {} });
53758
- }
53759
- }
53760
54612
  edges.push(...anchorEdges);
53761
54613
  const base = {
53762
54614
  daemonVersion,
@@ -53775,7 +54627,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53775
54627
  // payloadDigest — byte-identical to the pre-sidecar batch (backward compat).
53776
54628
  ...symbolSummaries ? { symbolSummaries } : {}
53777
54629
  };
53778
- return { ...base, payloadDigest: digest(base), anchorDigests };
54630
+ return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests };
53779
54631
  }
53780
54632
 
53781
54633
  // src/backfill-edges.ts
@@ -53896,7 +54748,7 @@ var ConsolidateWorker = class {
53896
54748
  init_paths();
53897
54749
 
53898
54750
  // src/lock.ts
53899
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "node:fs";
54751
+ import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
53900
54752
  function isProcessAlive(pid) {
53901
54753
  if (!pid || pid <= 0) return false;
53902
54754
  try {
@@ -53907,9 +54759,9 @@ function isProcessAlive(pid) {
53907
54759
  }
53908
54760
  }
53909
54761
  function readDaemonLock(lockPath) {
53910
- if (!existsSync22(lockPath)) return null;
54762
+ if (!existsSync23(lockPath)) return null;
53911
54763
  try {
53912
- const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
54764
+ const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
53913
54765
  return typeof lock.pid === "number" ? lock : null;
53914
54766
  } catch {
53915
54767
  return null;
@@ -54151,13 +55003,13 @@ async function reanchorProject(opts) {
54151
55003
  }
54152
55004
 
54153
55005
  // src/adopt.ts
54154
- import { existsSync as existsSync23 } from "node:fs";
54155
- import { dirname as dirname9, join as join25 } from "node:path";
55006
+ import { existsSync as existsSync24 } from "node:fs";
55007
+ import { dirname as dirname10, join as join27 } from "node:path";
54156
55008
  function findGitRoot(absPath) {
54157
55009
  let dir = absPath;
54158
55010
  for (let depth = 0; depth < 64; depth++) {
54159
- if (existsSync23(join25(dir, ".git"))) return dir;
54160
- const parent = dirname9(dir);
55011
+ if (existsSync24(join27(dir, ".git"))) return dir;
55012
+ const parent = dirname10(dir);
54161
55013
  if (parent === dir) return null;
54162
55014
  dir = parent;
54163
55015
  }
@@ -54265,6 +55117,18 @@ async function startMultiDaemon(opts = {}) {
54265
55117
  startLoopLagMonitor();
54266
55118
  let baseUrl = "";
54267
55119
  const records = [];
55120
+ const machineDominantLanguage = () => {
55121
+ const langCounts = /* @__PURE__ */ new Map();
55122
+ for (const r of records) {
55123
+ for (const l of r.engine.profile.languages ?? []) {
55124
+ const k = String(l).trim().toLowerCase();
55125
+ if (k) langCounts.set(k, (langCounts.get(k) ?? 0) + 1);
55126
+ }
55127
+ }
55128
+ return [...langCounts.entries()].sort(
55129
+ (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1)
55130
+ )[0]?.[0];
55131
+ };
54268
55132
  const updatePoller = opts.updateCheck ? startUpdatePoller({
54269
55133
  channel: loadConfig().updateChannel,
54270
55134
  onChange: (pending) => {
@@ -54277,6 +55141,47 @@ async function startMultiDaemon(opts = {}) {
54277
55141
  let serverPending = null;
54278
55142
  const boundaryListeners = [];
54279
55143
  let boundaryFlushing = false;
55144
+ let boundaryFlushStartedAt = 0;
55145
+ let boundaryFlushStage = "";
55146
+ let boundaryFlushUnits = 0;
55147
+ let boundaryFlushLastAdvanceAt = 0;
55148
+ let boundaryFlushBatches = 0;
55149
+ const beginFlush = (stage) => {
55150
+ boundaryFlushing = true;
55151
+ boundaryFlushStartedAt = Date.now();
55152
+ boundaryFlushLastAdvanceAt = Date.now();
55153
+ boundaryFlushUnits = 0;
55154
+ boundaryFlushBatches = 0;
55155
+ boundaryFlushItems = 0;
55156
+ boundaryFlushDetail = "";
55157
+ boundaryFlushStage = stage;
55158
+ };
55159
+ const flushStage = (stage) => {
55160
+ boundaryFlushStage = stage;
55161
+ };
55162
+ let boundaryFlushItems = 0;
55163
+ let boundaryFlushDetail = "";
55164
+ const noteFlushProgress = (units, items = 0, detail = "") => {
55165
+ boundaryFlushBatches++;
55166
+ boundaryFlushUnits += Math.max(0, units);
55167
+ boundaryFlushItems += Math.max(0, items);
55168
+ if (detail) boundaryFlushDetail = detail;
55169
+ if (units > 0 || items > 0) boundaryFlushLastAdvanceAt = Date.now();
55170
+ };
55171
+ const flushSnapshot = () => ({
55172
+ running: boundaryFlushing,
55173
+ stage: boundaryFlushStage,
55174
+ detail: boundaryFlushDetail,
55175
+ ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
55176
+ units: boundaryFlushUnits,
55177
+ items: boundaryFlushItems,
55178
+ chunks: boundaryFlushBatches,
55179
+ stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0
55180
+ });
55181
+ const endFlush = () => {
55182
+ boundaryFlushing = false;
55183
+ boundaryFlushStage = "";
55184
+ };
54280
55185
  const fireBoundary = () => {
54281
55186
  for (const l of boundaryListeners) {
54282
55187
  try {
@@ -54352,7 +55257,7 @@ async function startMultiDaemon(opts = {}) {
54352
55257
  void ambientLinkAll();
54353
55258
  app.route(`/ws/${rec.id}`, rec.webApp);
54354
55259
  try {
54355
- writeFileSync17(
55260
+ writeFileSync19(
54356
55261
  rec.engine.paths.daemonLock,
54357
55262
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
54358
55263
  "utf8"
@@ -54541,7 +55446,7 @@ async function startMultiDaemon(opts = {}) {
54541
55446
  baseUrl = `http://127.0.0.1:${port}`;
54542
55447
  try {
54543
55448
  ensureDir(globalDir());
54544
- writeFileSync17(
55449
+ writeFileSync19(
54545
55450
  lockPath,
54546
55451
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
54547
55452
  "utf8"
@@ -54550,7 +55455,7 @@ async function startMultiDaemon(opts = {}) {
54550
55455
  }
54551
55456
  for (const r of records) {
54552
55457
  try {
54553
- writeFileSync17(
55458
+ writeFileSync19(
54554
55459
  r.engine.paths.daemonLock,
54555
55460
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
54556
55461
  "utf8"
@@ -54813,26 +55718,32 @@ async function startMultiDaemon(opts = {}) {
54813
55718
  async syncPrinciplesPublic() {
54814
55719
  if (!loadConfig().consent.sync) return { uploaded: 0, skipped: "consent-off" };
54815
55720
  const ignore = loadClaimIgnorePatterns(globalDir());
54816
- const payload = buildPrincipleIngest(sharedStore, DAEMON_VERSION, ignore);
55721
+ const primaryLanguage = machineDominantLanguage();
55722
+ const payload = buildPrincipleIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
55723
+ ...primaryLanguage ? { primaryLanguage } : {}
55724
+ });
54817
55725
  if (!payload) return { uploaded: 0 };
54818
55726
  const res = await cloudNow().ingest(payload);
54819
55727
  return { uploaded: res.accepted };
54820
55728
  },
54821
55729
  async syncTriagePublic() {
54822
- if (!loadConfig().consent.sync) return { uploaded: 0, skipped: "consent-off" };
55730
+ const cfg2 = loadConfig();
55731
+ if (!cfg2.consent.sync) return { uploaded: 0, skipped: "consent-off" };
54823
55732
  const ignore = loadClaimIgnorePatterns(globalDir());
54824
- const langCounts = /* @__PURE__ */ new Map();
54825
- for (const r of records) {
54826
- for (const l of r.engine.profile.languages ?? []) {
54827
- const k = String(l).trim().toLowerCase();
54828
- if (k) langCounts.set(k, (langCounts.get(k) ?? 0) + 1);
54829
- }
54830
- }
54831
- const primaryLanguage = [...langCounts.entries()].sort(
54832
- (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1)
54833
- )[0]?.[0];
55733
+ const primaryLanguage = machineDominantLanguage();
55734
+ const languages = records.flatMap((r) => r.engine.profile.languages ?? []);
55735
+ const packages = cfg2.consent.contributePackages ? records.flatMap(
55736
+ (r) => r.engine.store.findNodesByLabel("Package").map((p) => ({
55737
+ name: String(p.attrs["name"] ?? ""),
55738
+ purl: String(p.attrs["purl"] ?? p.id)
55739
+ }))
55740
+ ) : [];
55741
+ const tokenAnchors = buildBucketTokenAnchors(languages, packages);
54834
55742
  const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
54835
- ...primaryLanguage ? { primaryLanguage } : {}
55743
+ ...primaryLanguage ? { primaryLanguage } : {},
55744
+ tokenAnchors,
55745
+ // Scoped bucket keys carry package names — same consent as the purls.
55746
+ includeScopedBuckets: cfg2.consent.contributePackages
54836
55747
  });
54837
55748
  if (!payload) return { uploaded: 0 };
54838
55749
  const res = await cloudNow().ingest(payload);
@@ -54862,8 +55773,25 @@ async function startMultiDaemon(opts = {}) {
54862
55773
  });
54863
55774
  if (context) {
54864
55775
  await lane("context", projectName, async () => {
54865
- const res = await client.ingest(context);
55776
+ const res = await client.ingest(context, {
55777
+ onChunk: (p) => {
55778
+ const seq = store.currentIngestSeq();
55779
+ for (const wireId of p.nodeIds) {
55780
+ const localId = context.localIdByWireId[wireId] ?? wireId;
55781
+ const local = store.getNode(localId);
55782
+ if (!local) continue;
55783
+ store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
55784
+ }
55785
+ if (p.blockedNodeIds?.length) {
55786
+ console.log(
55787
+ `[sync\u2192cloud] [${projectName}] context: ${p.blockedNodeIds.length} node(s) refused by an edge/CDN filter on content \u2014 left pending, will retry`
55788
+ );
55789
+ }
55790
+ noteFlushProgress(p.accepted, p.nodeIds.length, `context ${p.chunkIndex}/${p.totalChunks}`);
55791
+ }
55792
+ });
54866
55793
  uploaded += res.accepted;
55794
+ return res.accepted;
54867
55795
  });
54868
55796
  }
54869
55797
  let lexicon;
@@ -54888,7 +55816,18 @@ async function startMultiDaemon(opts = {}) {
54888
55816
  if (instances) {
54889
55817
  if (project) instances.projectId = project.projectId;
54890
55818
  await lane("instances", projectName, async () => {
54891
- const res = await client.ingest(instances);
55819
+ const blocked = /* @__PURE__ */ new Set();
55820
+ const res = await client.ingest(instances, {
55821
+ onChunk: (p) => {
55822
+ for (const id of p.blockedNodeIds ?? []) blocked.add(id);
55823
+ noteFlushProgress(p.accepted, p.nodeIds.length, `instances ${p.chunkIndex}/${p.totalChunks}`);
55824
+ }
55825
+ });
55826
+ if (blocked.size > 0) {
55827
+ console.log(
55828
+ `[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]}`
55829
+ );
55830
+ }
54892
55831
  uploaded += res.accepted;
54893
55832
  const seq = store.currentIngestSeq();
54894
55833
  const cloudIdByLocal = new Map(
@@ -54897,6 +55836,7 @@ async function startMultiDaemon(opts = {}) {
54897
55836
  for (const n of instances.nodes) {
54898
55837
  const local = store.getNode(n.id);
54899
55838
  if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
55839
+ if (blocked.has(n.id)) continue;
54900
55840
  const cloudNodeId = cloudIdByLocal.get(n.id);
54901
55841
  store.updateNode(n.id, {
54902
55842
  attrs: {
@@ -54913,6 +55853,14 @@ async function startMultiDaemon(opts = {}) {
54913
55853
  attrs: { ...local.attrs, anchorsContributedDigest: dg }
54914
55854
  });
54915
55855
  }
55856
+ for (const [localId, dg] of Object.entries(instances.semanticEdgeDigests)) {
55857
+ const local = store.getNode(localId);
55858
+ if (!local) continue;
55859
+ store.updateNode(localId, {
55860
+ attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
55861
+ });
55862
+ }
55863
+ return res.accepted;
54916
55864
  });
54917
55865
  }
54918
55866
  }
@@ -54981,12 +55929,14 @@ async function startMultiDaemon(opts = {}) {
54981
55929
  },
54982
55930
  async sessionBoundaryFlush() {
54983
55931
  if (boundaryFlushing) return { uploaded: 0, written: 0, pruned: 0, skipped: "in-flight" };
54984
- boundaryFlushing = true;
55932
+ beginFlush("embed");
54985
55933
  try {
54986
55934
  for (const r of records) {
54987
55935
  await r.engine.embedSettled();
54988
55936
  }
55937
+ flushStage("instances");
54989
55938
  const inst = await daemon.syncInstancesPublic();
55939
+ flushStage("skills");
54990
55940
  const skills = await daemon.syncSkillsAll();
54991
55941
  return {
54992
55942
  uploaded: inst.uploaded,
@@ -54995,7 +55945,7 @@ async function startMultiDaemon(opts = {}) {
54995
55945
  ...inst.errors ? { errors: inst.errors } : {}
54996
55946
  };
54997
55947
  } finally {
54998
- boundaryFlushing = false;
55948
+ endFlush();
54999
55949
  }
55000
55950
  },
55001
55951
  onSessionBoundary(cb) {
@@ -55003,7 +55953,7 @@ async function startMultiDaemon(opts = {}) {
55003
55953
  },
55004
55954
  async stop() {
55005
55955
  try {
55006
- const cur = readFileSync21(lockPath, "utf8");
55956
+ const cur = readFileSync23(lockPath, "utf8");
55007
55957
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
55008
55958
  } catch {
55009
55959
  }
@@ -55020,20 +55970,24 @@ async function startMultiDaemon(opts = {}) {
55020
55970
  }
55021
55971
  };
55022
55972
  app.post("/api/sync", async (c) => {
55023
- if (boundaryFlushing) return c.json({ skipped: "in-flight" }, 409);
55024
- boundaryFlushing = true;
55973
+ if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
55974
+ beginFlush("principles");
55025
55975
  try {
55026
55976
  const principles = await daemon.syncPrinciplesPublic();
55977
+ flushStage("triage");
55027
55978
  const triage = await daemon.syncTriagePublic();
55979
+ flushStage("instances");
55028
55980
  const instances = await daemon.syncInstancesPublic();
55981
+ flushStage("edge-backfill");
55029
55982
  const edgeBackfill = await daemon.backfillCausalEdges();
55030
55983
  return c.json({ principles, triage, instances, edgeBackfill });
55031
55984
  } catch (err2) {
55032
55985
  return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 500);
55033
55986
  } finally {
55034
- boundaryFlushing = false;
55987
+ endFlush();
55035
55988
  }
55036
55989
  });
55990
+ app.get("/api/sync", (c) => c.json(flushSnapshot()));
55037
55991
  app.post("/api/tick", async (c) => {
55038
55992
  try {
55039
55993
  return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
@@ -56016,21 +56970,21 @@ async function cmdInit() {
56016
56970
  if (!skipHooks) {
56017
56971
  console.log("");
56018
56972
  console.log("installing harness hooks...");
56019
- const { existsSync: existsSync25 } = await import("node:fs");
56020
- const { join: join27 } = await import("node:path");
56973
+ const { existsSync: existsSync26 } = await import("node:fs");
56974
+ const { join: join29 } = await import("node:path");
56021
56975
  try {
56022
56976
  await installClaudeHooks(port);
56023
56977
  } catch (err2) {
56024
56978
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
56025
56979
  }
56026
- if (existsSync25(join27(ROOT, ".cursor"))) {
56980
+ if (existsSync26(join29(ROOT, ".cursor"))) {
56027
56981
  try {
56028
56982
  await installCursorMcpConfig();
56029
56983
  } catch (err2) {
56030
56984
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
56031
56985
  }
56032
56986
  }
56033
- if (existsSync25(join27(ROOT, ".codex"))) {
56987
+ if (existsSync26(join29(ROOT, ".codex"))) {
56034
56988
  try {
56035
56989
  await installCodexHooks(port);
56036
56990
  } catch (err2) {
@@ -56187,8 +57141,30 @@ async function cmdStatus() {
56187
57141
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
56188
57142
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
56189
57143
  }
56190
- console.log(` graph db: ${existsSync24(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
56191
- console.log(` event log: ${existsSync24(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57144
+ console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
57145
+ console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
57146
+ if (existsSync25(paths.castalia)) {
57147
+ try {
57148
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57149
+ const store = openGraphStore2({ path: paths.castalia });
57150
+ try {
57151
+ let pending = 0;
57152
+ let total = 0;
57153
+ for (const label of ["Problem", "Solution", "RootCause"]) {
57154
+ for (const n of store.findNodesByLabel(label)) {
57155
+ if (n.attrs["source"] === "cloud") continue;
57156
+ total++;
57157
+ const c = n.attrs["contributedAtSeq"];
57158
+ if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
57159
+ }
57160
+ }
57161
+ console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
57162
+ } finally {
57163
+ store.close();
57164
+ }
57165
+ } catch {
57166
+ }
57167
+ }
56192
57168
  const lockPath = globalDaemonLock();
56193
57169
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
56194
57170
  console.log(
@@ -56818,11 +57794,11 @@ function cmdInstallationProfile(args2) {
56818
57794
  }
56819
57795
  async function cmdReview() {
56820
57796
  const paths = workspacePaths(ROOT);
56821
- if (!existsSync24(paths.reviewQueue)) {
57797
+ if (!existsSync25(paths.reviewQueue)) {
56822
57798
  console.log("(review queue empty)");
56823
57799
  return;
56824
57800
  }
56825
- const queue = JSON.parse(readFileSync22(paths.reviewQueue, "utf8"));
57801
+ const queue = JSON.parse(readFileSync24(paths.reviewQueue, "utf8"));
56826
57802
  if (queue.length === 0) {
56827
57803
  console.log("(review queue empty)");
56828
57804
  return;
@@ -57241,8 +58217,8 @@ async function cmdSimilar(args2) {
57241
58217
  const limitIdx = args2.indexOf("--limit");
57242
58218
  const limit = limitIdx >= 0 && args2[limitIdx + 1] ? Number(args2[limitIdx + 1]) : 5;
57243
58219
  const { runTool: runTool2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
57244
- await withStore((store) => {
57245
- const r = runTool2("errata.similar", { nodeId: seed, qname: seed, limit }, store);
58220
+ await withStore(async (store) => {
58221
+ const r = await runTool2("errata.similar", { nodeId: seed, qname: seed, limit }, store);
57246
58222
  if (!r.found) {
57247
58223
  console.log("seed not found");
57248
58224
  return;
@@ -57493,7 +58469,7 @@ async function gatherRepo(store, ws) {
57493
58469
  };
57494
58470
  }
57495
58471
  async function gatherReportData(generatedAt) {
57496
- const { existsSync: existsSync25 } = await import("node:fs");
58472
+ const { existsSync: existsSync26 } = await import("node:fs");
57497
58473
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57498
58474
  const cfg = loadConfig();
57499
58475
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -57501,7 +58477,7 @@ async function gatherReportData(generatedAt) {
57501
58477
  for (const ws of listWorkspaces()) {
57502
58478
  if (ws.missing) continue;
57503
58479
  const dbPath = workspacePaths(ws.path).castalia;
57504
- if (!existsSync25(dbPath)) continue;
58480
+ if (!existsSync26(dbPath)) continue;
57505
58481
  let store = null;
57506
58482
  try {
57507
58483
  store = openGraphStore2({ path: dbPath });
@@ -57532,7 +58508,7 @@ async function gatherReportData(generatedAt) {
57532
58508
  };
57533
58509
  }
57534
58510
  async function cmdReport(args2) {
57535
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync18 } = await import("node:fs");
58511
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync20 } = await import("node:fs");
57536
58512
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
57537
58513
  const includeFutureVerbs = args2.includes("--future-verbs");
57538
58514
  const now = /* @__PURE__ */ new Date();
@@ -57545,8 +58521,8 @@ async function cmdReport(args2) {
57545
58521
  const outDir = workspacePaths(ROOT).configDir;
57546
58522
  mkdirSync8(outDir, { recursive: true });
57547
58523
  const files = renderReport2(data, { includeFutureVerbs });
57548
- for (const f of files) writeFileSync18(join26(outDir, f.name), f.html, "utf8");
57549
- const indexPath = join26(outDir, "report.html");
58524
+ for (const f of files) writeFileSync20(join28(outDir, f.name), f.html, "utf8");
58525
+ const indexPath = join28(outDir, "report.html");
57550
58526
  console.log(`report \u2192 ${indexPath}`);
57551
58527
  console.log(
57552
58528
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -57598,7 +58574,7 @@ function spawnDaemonDetached() {
57598
58574
  try {
57599
58575
  const logPath = daemonLogPath();
57600
58576
  try {
57601
- if (statSync6(logPath).size > 5 * 1024 * 1024) renameSync3(logPath, `${logPath}.1`);
58577
+ if (statSync6(logPath).size > 5 * 1024 * 1024) renameSync4(logPath, `${logPath}.1`);
57602
58578
  } catch {
57603
58579
  }
57604
58580
  out2 = openSync2(logPath, "a");
@@ -57664,15 +58640,15 @@ function hookRelayCommand(port, path2) {
57664
58640
  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 '{}'`;
57665
58641
  }
57666
58642
  async function installClaudeHooks(port) {
57667
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57668
- const { join: join27 } = await import("node:path");
57669
- const dir = join27(ROOT, ".claude");
57670
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57671
- const file2 = join27(dir, "settings.json");
58643
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58644
+ const { join: join29 } = await import("node:path");
58645
+ const dir = join29(ROOT, ".claude");
58646
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58647
+ const file2 = join29(dir, "settings.json");
57672
58648
  let settings = {};
57673
- if (existsSync25(file2)) {
58649
+ if (existsSync26(file2)) {
57674
58650
  try {
57675
- settings = JSON.parse(readFileSync23(file2, "utf8"));
58651
+ settings = JSON.parse(readFileSync25(file2, "utf8"));
57676
58652
  } catch {
57677
58653
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57678
58654
  process.exit(2);
@@ -57718,10 +58694,10 @@ async function installClaudeHooks(port) {
57718
58694
  dropErrata(list);
57719
58695
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
57720
58696
  }
57721
- writeFileSync18(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58697
+ writeFileSync20(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
57722
58698
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
57723
58699
  await installClaudeMcpConfig();
57724
- const claudeMd = join27(ROOT, "CLAUDE.md");
58700
+ const claudeMd = join29(ROOT, "CLAUDE.md");
57725
58701
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
57726
58702
  if (recall.kind === "collision") {
57727
58703
  console.warn(
@@ -57733,15 +58709,15 @@ async function installClaudeHooks(port) {
57733
58709
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
57734
58710
  }
57735
58711
  async function installClaudeMcpConfig() {
57736
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57737
- const { join: join27, dirname: dirname10 } = await import("node:path");
57738
- const file2 = join27(ROOT, ".mcp.json");
57739
- const dir = dirname10(file2);
57740
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
58712
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58713
+ const { join: join29, dirname: dirname11 } = await import("node:path");
58714
+ const file2 = join29(ROOT, ".mcp.json");
58715
+ const dir = dirname11(file2);
58716
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
57741
58717
  let cfg = {};
57742
- if (existsSync25(file2)) {
58718
+ if (existsSync26(file2)) {
57743
58719
  try {
57744
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58720
+ cfg = JSON.parse(readFileSync25(file2, "utf8"));
57745
58721
  } catch {
57746
58722
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57747
58723
  process.exit(2);
@@ -57749,21 +58725,21 @@ async function installClaudeMcpConfig() {
57749
58725
  }
57750
58726
  cfg.mcpServers ??= {};
57751
58727
  cfg.mcpServers["errata"] = errataMcpInvocation();
57752
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58728
+ writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
57753
58729
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
57754
58730
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
57755
58731
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
57756
58732
  }
57757
58733
  async function installCursorMcpConfig() {
57758
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57759
- const { join: join27 } = await import("node:path");
57760
- const dir = join27(ROOT, ".cursor");
57761
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57762
- const file2 = join27(dir, "mcp.json");
58734
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58735
+ const { join: join29 } = await import("node:path");
58736
+ const dir = join29(ROOT, ".cursor");
58737
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58738
+ const file2 = join29(dir, "mcp.json");
57763
58739
  let cfg = {};
57764
- if (existsSync25(file2)) {
58740
+ if (existsSync26(file2)) {
57765
58741
  try {
57766
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58742
+ cfg = JSON.parse(readFileSync25(file2, "utf8"));
57767
58743
  } catch {
57768
58744
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57769
58745
  process.exit(2);
@@ -57771,7 +58747,7 @@ async function installCursorMcpConfig() {
57771
58747
  }
57772
58748
  cfg.mcpServers ??= {};
57773
58749
  cfg.mcpServers["errata"] = errataMcpInvocation();
57774
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58750
+ writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
57775
58751
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
57776
58752
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
57777
58753
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -57779,16 +58755,16 @@ async function installCursorMcpConfig() {
57779
58755
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
57780
58756
  }
57781
58757
  async function installCodexHooks(port) {
57782
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57783
- const { join: join27 } = await import("node:path");
57784
- const dir = join27(ROOT, ".codex");
57785
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57786
- const file2 = join27(dir, "config.toml");
58758
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync25, writeFileSync: writeFileSync20 } = await import("node:fs");
58759
+ const { join: join29 } = await import("node:path");
58760
+ const dir = join29(ROOT, ".codex");
58761
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58762
+ const file2 = join29(dir, "config.toml");
57787
58763
  const BEGIN = `# >>> errata hooks (errata-managed)`;
57788
58764
  const END = `# <<< errata hooks`;
57789
58765
  let existing = "";
57790
- if (existsSync25(file2)) {
57791
- existing = readFileSync23(file2, "utf8");
58766
+ if (existsSync26(file2)) {
58767
+ existing = readFileSync25(file2, "utf8");
57792
58768
  const beginIdx = existing.indexOf(BEGIN);
57793
58769
  const endIdx = existing.indexOf(END);
57794
58770
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -57817,7 +58793,7 @@ ${END}
57817
58793
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
57818
58794
 
57819
58795
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
57820
- writeFileSync18(file2, final, "utf8");
58796
+ writeFileSync20(file2, final, "utf8");
57821
58797
  console.log(`installed Codex hooks \u2192 ${file2}`);
57822
58798
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
57823
58799
  console.log("");
@@ -57858,7 +58834,30 @@ async function cmdSync(arg) {
57858
58834
  signal: AbortSignal.timeout(15 * 6e4)
57859
58835
  });
57860
58836
  if (res.status === 409) {
57861
- console.log("sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started.");
58837
+ const body2 = await res.json().catch(() => null);
58838
+ const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
58839
+ const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
58840
+ const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
58841
+ const units = typeof body2?.units === "number" ? body2.units : 0;
58842
+ const items = typeof body2?.items === "number" ? body2.items : 0;
58843
+ const chunks = typeof body2?.chunks === "number" ? body2.chunks : 0;
58844
+ const stalledMs = typeof body2?.stalledMs === "number" ? body2.stalledMs : 0;
58845
+ const where = body2?.detail ? ` [${body2.detail}]` : "";
58846
+ const moved = chunks > 0 ? ` \xB7 ${chunks} chunk(s), ${items} shipped, ${units} accepted${where}` : "";
58847
+ console.log(
58848
+ ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}${moved}. It will finish on its own; nothing new was started.` : "sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started."
58849
+ );
58850
+ if (stalledMs > 12e4 && chunks > 0) {
58851
+ const stalledMin = Math.floor(stalledMs / 6e4);
58852
+ console.log(
58853
+ ` \u26A0 STALLED: ${chunks} chunk(s) shipped but nothing has moved for ${stalledMin}m \u2014 the lane is looping, not draining. Check \`errata status\` for the backlog depth and daemon.log for the failing lane.`
58854
+ );
58855
+ }
58856
+ if (ageMs > 30 * 6e4) {
58857
+ console.log(
58858
+ " \u26A0 this pass has run past 30 min \u2014 if daemon.log shows no upload progress it may be stranded (a sleep/resume can do this). Restart with: errata stop && errata start"
58859
+ );
58860
+ }
57862
58861
  return;
57863
58862
  }
57864
58863
  if (!res.ok) {
@@ -57934,7 +58933,19 @@ async function cmdConsent(args2) {
57934
58933
  console.log(" note: run `errata login` to actually upload (no cloud credential yet).");
57935
58934
  }
57936
58935
  }
58936
+ function installLogTimestamps() {
58937
+ const ISO_AT_START = /^\[?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
58938
+ for (const level of ["log", "warn", "error"]) {
58939
+ const original = console[level].bind(console);
58940
+ console[level] = (...args2) => {
58941
+ const first = args2[0];
58942
+ if (typeof first === "string" && ISO_AT_START.test(first)) return original(...args2);
58943
+ original(`[${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args2);
58944
+ };
58945
+ }
58946
+ }
57937
58947
  async function cmdDash(args2) {
58948
+ installLogTimestamps();
57938
58949
  const portIdx = args2.indexOf("--port");
57939
58950
  const port = portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : 7891;
57940
58951
  const reindexOnStart = !args2.includes("--no-reindex");
@@ -58096,7 +59107,7 @@ async function cmdDash(args2) {
58096
59107
  await yieldToLoop2();
58097
59108
  try {
58098
59109
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
58099
- const res = bleedRules(join26(r.root, ".claude", "rules"), items);
59110
+ const res = bleedRules(join28(r.root, ".claude", "rules"), items);
58100
59111
  if (res.written || res.pruned) {
58101
59112
  console.log(
58102
59113
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")