@inerrata-corporation/errata 2.0.1-dev.99 → 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,
@@ -938,6 +949,12 @@ function packageCanonicalId(p) {
938
949
  const name2 = eco === "npm" ? p.name.toLowerCase() : p.name;
939
950
  return `pkg:${eco}/${name2}${p.version ? `@${p.version}` : ""}`;
940
951
  }
952
+ function versionlessPurl(purl) {
953
+ if (!purl.startsWith("pkg:")) return null;
954
+ const lastSlash = purl.lastIndexOf("/");
955
+ const lastAt = purl.lastIndexOf("@");
956
+ return lastAt > lastSlash ? purl.slice(0, lastAt) : purl;
957
+ }
941
958
  function parsePackageRef(ref) {
942
959
  let rest2 = ref.trim();
943
960
  if (rest2.startsWith("pkg:")) {
@@ -15363,7 +15380,10 @@ var init_edge_rules = __esm({
15363
15380
  // NOT ruled here — the type pre-exists with broader extractor senses, and a
15364
15381
  // new rule on an old type would reject legitimate live flows (reject-never-flip
15365
15382
  // cuts both ways: only rule types you introduce or senses that are documented).
15366
- CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15383
+ // `Component` joined the target set with OM-agent-anchors: an agent-named
15384
+ // component ("React Router") is the same knowledge→named-unit anchor shape as
15385
+ // a Tool — the knowledge is ABOUT it, not dependent on it.
15386
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15367
15387
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15368
15388
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15369
15389
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -15596,13 +15616,27 @@ var init_wire = __esm({
15596
15616
  /** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
15597
15617
  description: external_exports.string().min(1).max(4e3),
15598
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(),
15599
15628
  extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
15600
15629
  validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
15601
15630
  /** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
15602
15631
  * knows which packages are private. The door does NOT trust a `public` claim
15603
15632
  * blindly (it re-confirms on the spine); absent ⇒ unknown ⇒ fail-closed to
15604
15633
  * org-private. Accepted-but-ignored until ORG_MEMBRANE_ENABLED flips. */
15605
- anchorVisibility: external_exports.enum(["public", "private"]).optional()
15634
+ anchorVisibility: external_exports.enum(["public", "private"]).optional(),
15635
+ /** The spine-resolvable anchor id backing a `public` tag (OM-anchor-tag): a
15636
+ * Package purl or `languageCanonicalId`. The door validates THIS on the
15637
+ * public spine (falling back to `canonicalId` when absent — context stubs
15638
+ * are self-anchored). Never trusted without spine confirmation. */
15639
+ anchor: external_exports.string().min(1).max(300).optional()
15606
15640
  });
15607
15641
  RouteContextCountWireSchema = external_exports.object({
15608
15642
  confirmed: external_exports.number().int().min(0),
@@ -15706,6 +15740,9 @@ function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
15706
15740
  const path2 = normalizeSymbolPath(relPath);
15707
15741
  return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
15708
15742
  }
15743
+ function isMembraneSaltedId(id) {
15744
+ return typeof id === "string" && /^(orgn_|teamn_|projn_)/.test(id);
15745
+ }
15709
15746
  var init_project_symbol = __esm({
15710
15747
  "../../packages/shared/src/project-symbol.ts"() {
15711
15748
  "use strict";
@@ -15862,7 +15899,7 @@ var init_canonicalize = __esm({
15862
15899
 
15863
15900
  // ../../packages/shared/src/nlp/triage-canon.ts
15864
15901
  function canonicalizeToken(t) {
15865
- const base = t.trim().toLowerCase().replace(/[@\s].*$/, "");
15902
+ const base = t.trim().toLowerCase().replace(/(?!^)@.*$|\s.*$/, "");
15866
15903
  if (!base) return "";
15867
15904
  return resolveCanonicalId(base) ?? base;
15868
15905
  }
@@ -15955,6 +15992,7 @@ __export(src_exports, {
15955
15992
  isCodeLabel: () => isCodeLabel,
15956
15993
  isContextLabel: () => isContextLabel,
15957
15994
  isInferredSource: () => isInferredSource,
15995
+ isMembraneSaltedId: () => isMembraneSaltedId,
15958
15996
  isProjectOnlyEdgeType: () => isProjectOnlyEdgeType,
15959
15997
  isProjectOnlyNodeLabel: () => isProjectOnlyNodeLabel,
15960
15998
  isQuarantinedExtractionSource: () => isQuarantinedExtractionSource,
@@ -15993,6 +16031,7 @@ __export(src_exports, {
15993
16031
  toCloudAttrs: () => toCloudAttrs,
15994
16032
  toolCanonicalId: () => toolCanonicalId,
15995
16033
  validateCastaliaPayload: () => validateCastaliaPayload,
16034
+ versionlessPurl: () => versionlessPurl,
15996
16035
  vetSidecarSummaries: () => vetSidecarSummaries
15997
16036
  });
15998
16037
  var init_src = __esm({
@@ -16249,6 +16288,9 @@ var init_profile = __esm({
16249
16288
  });
16250
16289
 
16251
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
+ }
16252
16294
  var init_daemon_wire = __esm({
16253
16295
  "../../packages/local-shared/src/daemon-wire.ts"() {
16254
16296
  "use strict";
@@ -16662,6 +16704,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16662
16704
  findByLabel: this.db.prepare(
16663
16705
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
16664
16706
  ),
16707
+ // Scalar count twin of findByLabel — same live-row semantics, no row
16708
+ // hydration. Exists because status surfaces (daemon `/` + `/health`) used
16709
+ // findNodesByLabel(...).length, materializing every row's attrs JSON and
16710
+ // embedding blob per request — seconds of synchronous loop-hold per poll.
16711
+ countByLabel: this.db.prepare(
16712
+ "SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
16713
+ ),
16665
16714
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
16666
16715
  // clean-reindex purge so a true wipe removes history too, not just live rows.
16667
16716
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -16915,6 +16964,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16915
16964
  const rows = this.stmts.findByLabel.all(label);
16916
16965
  return rows.map(rowToNode);
16917
16966
  }
16967
+ /** Live-row count for a label — `findNodesByLabel(label).length` without the
16968
+ * per-row hydration (attrs JSON + embedding blob). Status surfaces poll this
16969
+ * per project per request; the materializing form held the daemon's event
16970
+ * loop for seconds at scale. */
16971
+ countNodesByLabel(label) {
16972
+ return Number(this.stmts.countByLabel.get(label).n);
16973
+ }
16918
16974
  findAllVersionsByLabel(label) {
16919
16975
  const rows = this.stmts.findByLabelAll.all(label);
16920
16976
  return rows.map(rowToNode);
@@ -18139,6 +18195,9 @@ var init_problem_package_link = __esm({
18139
18195
  });
18140
18196
 
18141
18197
  // ../../packages/local-graph/src/design-problem.ts
18198
+ function isConstraintProblem(node2) {
18199
+ return node2.attrs["kind"] === "constraint";
18200
+ }
18142
18201
  function isPlaceholderStatement(statement) {
18143
18202
  const s = statement.trim();
18144
18203
  if (s.length < 8) return true;
@@ -18316,6 +18375,55 @@ function mintDomainNode(store, name2, ts) {
18316
18375
  }
18317
18376
  return id;
18318
18377
  }
18378
+ function mintCitedPackageNode(store, ref, ts) {
18379
+ const cleaned = ref.replace(/\s+/g, " ").trim();
18380
+ const slash = /^([a-z0-9-]+)\/(.+)$/i.exec(cleaned);
18381
+ const hasEco = slash != null && !cleaned.startsWith("@");
18382
+ const name2 = (hasEco ? slash[2] : cleaned).trim();
18383
+ const nameLc = name2.toLowerCase();
18384
+ const existing = store.findNodesByLabel("Package").find((n) => String(n.attrs["name"] ?? "").toLowerCase() === nameLc);
18385
+ if (existing) return existing.id;
18386
+ let eco = hasEco ? slash[1].toLowerCase() : "";
18387
+ if (!eco && cleaned.startsWith("@")) eco = "npm";
18388
+ if (!eco) {
18389
+ const counts = /* @__PURE__ */ new Map();
18390
+ for (const p of store.findNodesByLabel("Package")) {
18391
+ const e = String(p.attrs["ecosystem"] ?? "").toLowerCase();
18392
+ if (e) counts.set(e, (counts.get(e) ?? 0) + 1);
18393
+ }
18394
+ eco = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "npm";
18395
+ }
18396
+ const purlName = eco === "npm" ? nameLc : name2;
18397
+ const purl = `pkg:${eco}/${purlName}`;
18398
+ if (!store.getNode(purl)) {
18399
+ store.mergeNode(
18400
+ buildNode(purl, "Package", name2, ts, {
18401
+ purl,
18402
+ name: name2,
18403
+ ecosystem: eco,
18404
+ resolved: false,
18405
+ // agent-cited, no lockfile resolution
18406
+ source: "convo",
18407
+ provisional: true
18408
+ })
18409
+ );
18410
+ }
18411
+ return purl;
18412
+ }
18413
+ function mintComponentNode(store, name2, ts) {
18414
+ const display = name2.replace(/\s+/g, " ").trim();
18415
+ const slug2 = display.toLowerCase();
18416
+ if (!store.getNode(slug2)) {
18417
+ store.mergeNode(
18418
+ buildNode(slug2, "Component", display, ts, {
18419
+ name: display,
18420
+ source: "convo",
18421
+ provisional: true
18422
+ })
18423
+ );
18424
+ }
18425
+ return slug2;
18426
+ }
18319
18427
  function resolveFileNode(store, path2, workspaceId2) {
18320
18428
  const want = path2.trim().replace(/^\.?\//, "");
18321
18429
  for (const n of store.findNodesByLabel("File")) {
@@ -18339,7 +18447,7 @@ function tokenJaccard(a, b) {
18339
18447
  for (const t of sa) if (sb.has(t)) inter++;
18340
18448
  return inter / (sa.size + sb.size - inter);
18341
18449
  }
18342
- function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts) {
18450
+ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
18343
18451
  const stmt = statement.trim();
18344
18452
  if (!ANCHORABLE_CODE.test(relPath)) return null;
18345
18453
  const file2 = resolveFileNode(store, relPath, workspaceId2);
@@ -18350,6 +18458,8 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
18350
18458
  const cand = store.getNode(e.from);
18351
18459
  if (!cand || cand.label !== "Problem" || cand.attrs["resolvedAt"]) continue;
18352
18460
  if (cand.id === selfId) continue;
18461
+ const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
18462
+ if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
18353
18463
  const score2 = tokenJaccard(stmt, cand.description);
18354
18464
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
18355
18465
  }
@@ -18384,6 +18494,7 @@ function priorsForFile(store, relPath) {
18384
18494
  }
18385
18495
  if (!file2) return null;
18386
18496
  const openProblems = [];
18497
+ const constraints = [];
18387
18498
  const seenProblem = /* @__PURE__ */ new Set();
18388
18499
  const related = /* @__PURE__ */ new Map();
18389
18500
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
@@ -18392,14 +18503,14 @@ function priorsForFile(store, relPath) {
18392
18503
  if (n.label === "Problem") {
18393
18504
  if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
18394
18505
  seenProblem.add(n.id);
18395
- openProblems.push(n);
18506
+ (isConstraintProblem(n) ? constraints : openProblems).push(n);
18396
18507
  }
18397
18508
  } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
18398
18509
  related.set(n.id, n);
18399
18510
  }
18400
18511
  }
18401
18512
  const solutionsByProblem = /* @__PURE__ */ new Map();
18402
- for (const p of openProblems) {
18513
+ for (const p of [...openProblems, ...constraints]) {
18403
18514
  for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
18404
18515
  const n = store.getNode(e.to);
18405
18516
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
@@ -18410,9 +18521,11 @@ function priorsForFile(store, relPath) {
18410
18521
  }
18411
18522
  }
18412
18523
  }
18413
- if (openProblems.length === 0 && related.size === 0) return null;
18414
- openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
18415
- 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 };
18416
18529
  }
18417
18530
  function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
18418
18531
  return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
@@ -18483,7 +18596,8 @@ function resolveDesignProblemById(store, id, fixNote, t) {
18483
18596
  }
18484
18597
  function closeDesignProblem(store, problemId, fixNote, t) {
18485
18598
  const p = store.getNode(problemId);
18486
- 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;
18487
18601
  if (fixNote?.trim() && store.outEdges(problemId, ["SOLVED_BY"]).length === 0) {
18488
18602
  const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
18489
18603
  store.mergeNode(
@@ -18491,6 +18605,7 @@ function closeDesignProblem(store, problemId, fixNote, t) {
18491
18605
  );
18492
18606
  mergeEdge(store, problemId, solId, "SOLVED_BY", t);
18493
18607
  }
18608
+ if (p.attrs["resolvedAt"]) return false;
18494
18609
  store.updateNode(problemId, {
18495
18610
  attrs: { ...p.attrs, provisional: false, resolvedAt: t },
18496
18611
  lastUpdatedAt: t
@@ -18531,6 +18646,7 @@ function resolveDesignProblems(store, t) {
18531
18646
  for (const p of store.findNodesByLabel("Problem")) {
18532
18647
  if (!p.id.startsWith("dprob_")) continue;
18533
18648
  if (p.attrs["resolvedAt"]) continue;
18649
+ if (isConstraintProblem(p)) continue;
18534
18650
  let symName = "";
18535
18651
  let symRelPath;
18536
18652
  let edited = false;
@@ -18548,7 +18664,7 @@ function resolveDesignProblems(store, t) {
18548
18664
  if (store.outEdges(p.id, ["SOLVED_BY"]).length === 0) {
18549
18665
  const solId = `dfix_${digest({ problemId: p.id, edit: t })}`.slice(0, 56);
18550
18666
  store.mergeNode(
18551
- buildNode(solId, "Solution", `addressed by an edit to ${symName}`, t, {
18667
+ buildNode(solId, "Solution", `${AUTO_MINT_PREFIX}${symName}`, t, {
18552
18668
  source: "convo",
18553
18669
  provisional: false,
18554
18670
  // AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
@@ -18569,7 +18685,7 @@ function resolveDesignProblems(store, t) {
18569
18685
  }
18570
18686
  return resolved;
18571
18687
  }
18572
- 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;
18573
18689
  var init_design_problem = __esm({
18574
18690
  "../../packages/local-graph/src/design-problem.ts"() {
18575
18691
  "use strict";
@@ -18611,6 +18727,7 @@ var init_design_problem = __esm({
18611
18727
  ]);
18612
18728
  ANCHORABLE_CODE = /\.(ts|tsx|js|mjs|cjs|py|go|rs|java|rb|c|cpp|h)$/;
18613
18729
  MAX_ANCHOR_FILES = 5;
18730
+ AUTO_MINT_PREFIX = "addressed by an edit to ";
18614
18731
  }
18615
18732
  });
18616
18733
 
@@ -18675,13 +18792,11 @@ function backfillLegacyAnchors(store, ts) {
18675
18792
  }
18676
18793
  return report;
18677
18794
  }
18678
- var AUTO_MINT_PREFIX;
18679
18795
  var init_anchor_backfill = __esm({
18680
18796
  "../../packages/local-graph/src/anchor-backfill.ts"() {
18681
18797
  "use strict";
18682
18798
  init_justification();
18683
18799
  init_design_problem();
18684
- AUTO_MINT_PREFIX = "addressed by an edit to ";
18685
18800
  }
18686
18801
  });
18687
18802
 
@@ -20442,6 +20557,7 @@ function mergeDuplicateProblems(store, opts) {
20442
20557
  if (consumed.has(b.id)) continue;
20443
20558
  const tb = tokens.get(b.id);
20444
20559
  if (Math.min(ta.size, tb.size) < minTokens) continue;
20560
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
20445
20561
  if (overlap(ta, tb) >= minOverlap) {
20446
20562
  cluster.push(b);
20447
20563
  consumed.add(b.id);
@@ -20498,6 +20614,7 @@ var init_problem_dedup = __esm({
20498
20614
  "use strict";
20499
20615
  init_src();
20500
20616
  init_src();
20617
+ init_design_problem();
20501
20618
  REDIRECT_EDGES = [
20502
20619
  "CAUSED_BY",
20503
20620
  "SOLVED_BY",
@@ -20704,6 +20821,7 @@ var init_principle_sync = __esm({
20704
20821
  // ../../packages/local-graph/src/index.ts
20705
20822
  var src_exports2 = {};
20706
20823
  __export(src_exports2, {
20824
+ AUTO_MINT_PREFIX: () => AUTO_MINT_PREFIX,
20707
20825
  CAUSAL_FAMILY: () => CAUSAL_FAMILY,
20708
20826
  CITABLE_PRIOR_LABELS: () => CITABLE_PRIOR_LABELS,
20709
20827
  CODE_REACH_EDGES: () => CODE_REACH_EDGES,
@@ -20755,6 +20873,7 @@ __export(src_exports2, {
20755
20873
  induceAbstractions: () => induceAbstractions,
20756
20874
  induceTriage: () => induceTriage,
20757
20875
  ingestDesignProblem: () => ingestDesignProblem,
20876
+ isConstraintProblem: () => isConstraintProblem,
20758
20877
  isPlaceholderStatement: () => isPlaceholderStatement,
20759
20878
  linkProblemToLanguages: () => linkProblemToLanguages,
20760
20879
  linkProblemToPackages: () => linkProblemToPackages,
@@ -20766,6 +20885,8 @@ __export(src_exports2, {
20766
20885
  matchSymbolsInText: () => matchSymbolsInText,
20767
20886
  mergeCloudCounts: () => mergeCloudCounts,
20768
20887
  mergeDuplicateProblems: () => mergeDuplicateProblems,
20888
+ mintCitedPackageNode: () => mintCitedPackageNode,
20889
+ mintComponentNode: () => mintComponentNode,
20769
20890
  mintDomainNode: () => mintDomainNode,
20770
20891
  mintPatternNode: () => mintPatternNode,
20771
20892
  openGraphStore: () => openGraphStore,
@@ -20838,12 +20959,14 @@ function isPassiveInjectable(node2) {
20838
20959
  }
20839
20960
  function buildSnapshot(opts) {
20840
20961
  const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
20841
- 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));
20842
20964
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
20843
20965
  const recent = allProblems.slice(0, 8).map((p) => ({
20844
20966
  node: p,
20845
20967
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
20846
20968
  }));
20969
+ const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
20847
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) => {
20848
20971
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
20849
20972
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
@@ -20871,10 +20994,13 @@ function buildSnapshot(opts) {
20871
20994
  episodeId: episodeId2,
20872
20995
  problems: problems2
20873
20996
  }));
20874
- const needsRevisit = listNeedsRevisit(
20875
- opts.store,
20876
- (opts.now ?? /* @__PURE__ */ new Date()).getTime()
20877
- ).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);
20878
21004
  const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
20879
21005
  const pkgBase = (x) => {
20880
21006
  const at = x.lastIndexOf("@");
@@ -20910,6 +21036,7 @@ function buildSnapshot(opts) {
20910
21036
  profileContext,
20911
21037
  recentProblems: recent,
20912
21038
  recentResolved,
21039
+ recentConstraints,
20913
21040
  ...causalNudge ? { causalNudge } : {},
20914
21041
  ...domainNudge ? { domainNudge } : {},
20915
21042
  motifs,
@@ -20962,6 +21089,7 @@ function sliceForFile(store, relPath) {
20962
21089
  const fp = priorsForFile(store, relPath);
20963
21090
  const priors = fp ? {
20964
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 })),
20965
21093
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
20966
21094
  solutionsByProblem: Object.fromEntries(
20967
21095
  fp.openProblems.slice(0, 3).map((p) => [
@@ -21056,7 +21184,7 @@ function renderSnapshot(s) {
21056
21184
  }
21057
21185
  const tagOf = (n) => s.edgeElicitation ? ` \`[${priorHandle(n)}]\`` : "";
21058
21186
  lines.push("### Recently observed problems in this workspace");
21059
- if (s.recentProblems.length === 0 && s.recentResolved.length === 0) {
21187
+ if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
21060
21188
  lines.push("- _none yet \u2014 errata is still building its model_");
21061
21189
  } else {
21062
21190
  if (s.recentProblems.length > 0) {
@@ -21078,6 +21206,12 @@ function renderSnapshot(s) {
21078
21206
  );
21079
21207
  }
21080
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
+ }
21081
21215
  if (s.recentResolved.length > 0) {
21082
21216
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
21083
21217
  for (const r of s.recentResolved) {
@@ -21145,6 +21279,14 @@ function renderSnapshot(s) {
21145
21279
  }
21146
21280
  }
21147
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
+ }
21148
21290
  if (w.priors?.related.length) {
21149
21291
  lines.push("- **Related priors** (cite with `([id])` where you lean on one):");
21150
21292
  for (const n of w.priors.related) {
@@ -21182,6 +21324,9 @@ function dropLowestUnit(s) {
21182
21324
  case "pendingEnrichment":
21183
21325
  if (s.pendingEnrichment.length) return s.pendingEnrichment.pop(), true;
21184
21326
  break;
21327
+ case "recentConstraints":
21328
+ if (s.recentConstraints.length) return s.recentConstraints.pop(), true;
21329
+ break;
21185
21330
  case "recentProblems":
21186
21331
  if (s.recentProblems.length) return s.recentProblems.pop(), true;
21187
21332
  break;
@@ -21253,8 +21398,19 @@ ${RECALL_FIRST_BODY}`;
21253
21398
  // budget they drop before anything open/actionable.
21254
21399
  "recentResolved",
21255
21400
  "pendingEnrichment",
21256
- "recentProblems",
21257
- "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"
21258
21414
  ];
21259
21415
  DEFAULT_AGENT_CONTEXT_BUDGET = 9e3;
21260
21416
  }
@@ -21487,6 +21643,7 @@ var init_oauth = __esm({
21487
21643
 
21488
21644
  // ../../packages/cloud-client/src/client.ts
21489
21645
  import { randomUUID } from "node:crypto";
21646
+ import { gzipSync } from "node:zlib";
21490
21647
  function wirePerContext(value) {
21491
21648
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
21492
21649
  const out2 = {};
@@ -21530,7 +21687,12 @@ function toWirePayload(batch, runId) {
21530
21687
  // attrs travel; node-level bookkeeping (embedding, momentum counters,
21531
21688
  // bi-temporal fields) is local-stratum and never crosses.
21532
21689
  attrs: n.attrs ?? {},
21533
- extractionSource: n.extractionSource
21690
+ extractionSource: n.extractionSource,
21691
+ // Org-membrane anchor tag (OM-anchor-tag): the batch builders set these
21692
+ // on wire-bound projections only; the door re-validates the claim on the
21693
+ // public spine, so lifting them is routing input, not a grant.
21694
+ ...n.anchorVisibility ? { anchorVisibility: n.anchorVisibility } : {},
21695
+ ...n.anchor ? { anchor: n.anchor } : {}
21534
21696
  }));
21535
21697
  const edges = batch.edges.filter((e) => !droppedNodeIds.has(e.from) && !droppedNodeIds.has(e.to)).map((e) => {
21536
21698
  const perContext = wirePerContext(e.attrs?.["perContext"]);
@@ -21679,6 +21841,11 @@ function normalizeSolution(input) {
21679
21841
  validationSource: input.validationSource
21680
21842
  };
21681
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
+ }
21682
21849
  function chunkArray(items, size) {
21683
21850
  if (items.length <= size) return items.length ? [[...items]] : [];
21684
21851
  const out2 = [];
@@ -21696,14 +21863,15 @@ function provenanceHeaders(provenance) {
21696
21863
  ...provenance.agentModel ? { "x-inerrata-agent-model": provenance.agentModel } : {}
21697
21864
  };
21698
21865
  }
21699
- var asWireCount, INGEST_NODE_CHUNK, CloudClient, CloudError;
21866
+ var asWireCount, INGEST_NODE_CHUNK, COMPRESS_MIN_BYTES, CloudClient, CloudError;
21700
21867
  var init_client = __esm({
21701
21868
  "../../packages/cloud-client/src/client.ts"() {
21702
21869
  "use strict";
21703
21870
  init_oauth();
21704
21871
  init_src();
21705
21872
  asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
21706
- INGEST_NODE_CHUNK = 25;
21873
+ INGEST_NODE_CHUNK = 8;
21874
+ COMPRESS_MIN_BYTES = 4096;
21707
21875
  CloudClient = class {
21708
21876
  baseUrl;
21709
21877
  apiKey;
@@ -21718,6 +21886,8 @@ var init_client = __esm({
21718
21886
  daemonVersion;
21719
21887
  daemonChannel;
21720
21888
  provenance;
21889
+ /** Cleared for the process once a server proves it cannot inflate (see `json`). */
21890
+ compressRequests;
21721
21891
  constructor(opts) {
21722
21892
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
21723
21893
  if (opts.apiKey !== void 0) this.apiKey = opts.apiKey;
@@ -21731,6 +21901,7 @@ var init_client = __esm({
21731
21901
  this.timeoutMs = opts.timeoutMs ?? 15e3;
21732
21902
  this.daemonVersion = opts.daemonVersion;
21733
21903
  this.daemonChannel = opts.daemonChannel;
21904
+ this.compressRequests = opts.compressRequests ?? true;
21734
21905
  this.provenance = opts.provenance ?? {
21735
21906
  clientProduct: "inerrata_cloud_client",
21736
21907
  clientKind: "sdk",
@@ -21851,20 +22022,48 @@ var init_client = __esm({
21851
22022
  * the per-decision response into flush accounting.
21852
22023
  *
21853
22024
  * The v1 door caps a payload at `MAX_NODES_PER_PAYLOAD` / `MAX_EDGES_PER_PAYLOAD`
21854
- * (413 over). A batch above the cap is split and drained across calls under ONE
21855
- * runId: every NODE chunk first (edges empty), then EDGE chunks (nodes empty).
22025
+ * (413 over). A batch above the cap is split and drained across calls, every
22026
+ * NODE chunk first (edges empty), then EDGE chunks (nodes empty). Each chunk
22027
+ * gets its OWN runId: the door's durable idempotency claim is keyed on
22028
+ * (agent, org, runId) with the payload digest, so reusing one runId across
22029
+ * different chunk payloads 409s "runId was already used with a different
22030
+ * payload" on the second chunk — exactly how the first real >25-node drain
22031
+ * died (2026-07-22). The runId never grouped anything server-side; it exists
22032
+ * for duplicate-POST protection, which is per-request by nature.
21856
22033
  * Recognition resolves an edge's endpoints against nodes already ingested this
21857
22034
  * drain (endpoint-label validation is deferred to the service when an endpoint
21858
22035
  * isn't in-payload), so the split never orphans an edge. Sub-results concat into
21859
22036
  * one. (fix: an un-chunked >200-node backlog 413'd forever and never drained.) */
21860
- async ingest(batch) {
22037
+ async ingest(batch, opts = {}) {
21861
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
+ };
21862
22058
  if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
21863
- const result = await this.ingestWire(toWirePayload(batch, runId));
21864
- 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 };
21865
22063
  }
21866
22064
  const merged = { runId, nodes: [], edges: [] };
21867
22065
  let pendingEdges = [...batch.edges];
22066
+ const echoedCloudId = /* @__PURE__ */ new Map();
21868
22067
  for (const nodeChunk of chunkArray(batch.nodes, INGEST_NODE_CHUNK)) {
21869
22068
  const ids = new Set(nodeChunk.map((n) => n.id));
21870
22069
  const inChunk = pendingEdges.filter((e) => ids.has(e.from) && ids.has(e.to)).slice(0, MAX_EDGES_PER_PAYLOAD);
@@ -21872,15 +22071,27 @@ var init_client = __esm({
21872
22071
  const shipped = new Set(inChunk);
21873
22072
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
21874
22073
  }
21875
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, runId));
22074
+ const r = await this.ingestWithSplit(batch, nodeChunk, inChunk, blocked);
22075
+ emit(nodeChunk.map((n) => n.id), summarizeIngestResult(r).accepted);
21876
22076
  merged.nodes.push(...r.nodes);
21877
22077
  merged.edges.push(...r.edges);
22078
+ for (const rn of r.nodes) {
22079
+ if (rn.nodeId && rn.nodeId !== rn.canonicalId && isMembraneSaltedId(rn.nodeId)) {
22080
+ echoedCloudId.set(rn.canonicalId, rn.nodeId);
22081
+ }
22082
+ }
21878
22083
  if (r.patternReconciliation) {
21879
22084
  merged.patternReconciliation = { ...merged.patternReconciliation, ...r.patternReconciliation };
21880
22085
  }
21881
22086
  }
21882
22087
  for (const edgeChunk of chunkArray(pendingEdges, MAX_EDGES_PER_PAYLOAD)) {
21883
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: edgeChunk }, runId));
22088
+ const rewritten = edgeChunk.map((e) => ({
22089
+ ...e,
22090
+ from: echoedCloudId.get(e.from) ?? e.from,
22091
+ to: echoedCloudId.get(e.to) ?? e.to
22092
+ }));
22093
+ const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
22094
+ emit([], summarizeIngestResult(r).accepted);
21884
22095
  merged.edges.push(...r.edges);
21885
22096
  }
21886
22097
  return { ...summarizeIngestResult(merged), result: merged };
@@ -21889,6 +22100,43 @@ var init_client = __esm({
21889
22100
  async ingestWire(payload) {
21890
22101
  return this.json("POST", "/v2/ingest", payload);
21891
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
+ }
21892
22140
  /**
21893
22141
  * SDK v2 graph-native write. It remembers a typed diagnostic spine through
21894
22142
  * `/v2/ingest` instead of the legacy forum question/answer routes:
@@ -22090,10 +22338,13 @@ var init_client = __esm({
22090
22338
  }
22091
22339
  /** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
22092
22340
  * canonical ids of the agent's recent problems — the cloud ranks the skills whose
22093
- * motifs those problems match first (the rest are baseline). */
22094
- 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) {
22095
22345
  const qs = new URLSearchParams({ context });
22096
22346
  if (seed && seed.length > 0) qs.set("seed", seed.join(","));
22347
+ if (techSeed && techSeed.length > 0) qs.set("techSeed", techSeed.join(","));
22097
22348
  if (this.daemonVersion) qs.set("daemonVersion", this.daemonVersion);
22098
22349
  if (this.daemonChannel) qs.set("daemonChannel", this.daemonChannel);
22099
22350
  return this.json("GET", `/v2/skills?${qs.toString()}`);
@@ -22179,15 +22430,43 @@ var init_client = __esm({
22179
22430
  const token = opts?.bearerToken ?? await this.authToken();
22180
22431
  if (token) headers["authorization"] = `Bearer ${token}`;
22181
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
+ }
22182
22443
  const ac = new AbortController();
22183
22444
  const tid = setTimeout(() => ac.abort(), this.timeoutMs);
22184
22445
  try {
22185
22446
  const res = await this.fetchFn(url2, {
22186
22447
  method,
22187
22448
  headers,
22188
- body: body2 === void 0 ? void 0 : JSON.stringify(body2),
22449
+ body: payload,
22189
22450
  signal: ac.signal
22190
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
+ }
22191
22470
  if (!res.ok) {
22192
22471
  const text = await res.text();
22193
22472
  throw new CloudError(
@@ -25101,15 +25380,22 @@ function triageBullet(ref) {
25101
25380
  }
25102
25381
  function linkBullet(ref) {
25103
25382
  return [
25104
- " \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:",
25105
25389
  ` \xB7 (instance:[${ref}],[another-prior],\u2026) \u2014 the priors this problem is an instance of.`,
25106
25390
  " Aim for ~3 DIFFERENT relevant priors; one link is weak, three triangulate it.",
25107
25391
  ` \xB7 ${TAG_EXAMPLE.pattern()} \u2014 name the general shape it instantiates (e.g. (pattern: unbounded`,
25108
25392
  " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
25109
25393
  " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
25110
- ` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
25111
- " (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
25112
- " an invisible island: the Domain is the topic other problems in the area cluster on. Title Case.",
25394
+ ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC PACKAGE the problem is about, even when this`,
25395
+ " workspace doesn't depend on it (e.g. (package: chokidar), (package: pypi/requests)) \u2014 a",
25396
+ " public-registry anchor lets the knowledge cross to the collective; internal names stay private.",
25397
+ ` \xB7 ${TAG_EXAMPLE.component()} \u2014 the framework/product-level UNIT it concerns when it's not a`,
25398
+ " package or a language (e.g. (component: React Router), (component: V8 Isolate)).",
25113
25399
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
25114
25400
  " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
25115
25401
  ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
@@ -25128,9 +25414,9 @@ function attemptBullet() {
25128
25414
  ].join("\n");
25129
25415
  }
25130
25416
  function buildAgentInstruction(opts = {}) {
25131
- const signals = opts.signals ?? ["prior", "problem", "fix", "constraint", "triage", "attempt", "link"];
25417
+ const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
25132
25418
  const ref = opts.referent ?? "its-handle";
25133
- 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);
25134
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:`;
25135
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.";
25136
25422
  return [
@@ -25181,6 +25467,12 @@ var init_agent_signals = __esm({
25181
25467
  // DOMAIN — the abstract area a problem is about; the concept layer an
25182
25468
  // anchor-less problem clusters on. Mints/resolves a Domain by name.
25183
25469
  domain: () => `(domain: The Area)`,
25470
+ // PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
25471
+ // package a problem is ABOUT (even when not a dependency) and the
25472
+ // framework/product-level unit it concerns. Public spine anchors → the
25473
+ // knowledge can cross to the collective.
25474
+ package: () => `(package: the-package-name)`,
25475
+ component: () => `(component: The Component)`,
25184
25476
  aids: (ref) => `(aids:[${ref}])`
25185
25477
  };
25186
25478
  GLOSS = {
@@ -25190,6 +25482,22 @@ var init_agent_signals = __esm({
25190
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)",
25191
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",
25192
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",
25193
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"
25194
25502
  };
25195
25503
  }
@@ -25792,6 +26100,33 @@ function federationHint(status) {
25792
26100
  return void 0;
25793
26101
  }
25794
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
+ }
25795
26130
  function formatDualNodes(merged) {
25796
26131
  return merged.nodes.map((n) => ({
25797
26132
  id: n.id,
@@ -25811,7 +26146,7 @@ function formatDualNodes(merged) {
25811
26146
  } : {}
25812
26147
  }));
25813
26148
  }
25814
- var BRIDGE_LABELS;
26149
+ var BRIDGE_LABELS, CANONICAL_KNOWLEDGE_ID, UUID_ID;
25815
26150
  var init_dual_augment = __esm({
25816
26151
  "src/dual-augment.ts"() {
25817
26152
  "use strict";
@@ -25819,6 +26154,8 @@ var init_dual_augment = __esm({
25819
26154
  init_generalize_graph();
25820
26155
  init_dual_burst();
25821
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;
25822
26159
  }
25823
26160
  });
25824
26161
 
@@ -26313,6 +26650,7 @@ function mergeProblemsByEmbedding(store, opts) {
26313
26650
  if (b.id === a.id || consumed.has(b.id)) continue;
26314
26651
  if (b.embedding.length !== a.embedding.length) continue;
26315
26652
  if ((a.attrs["embeddingVersion"] ?? "") !== (b.attrs["embeddingVersion"] ?? "")) continue;
26653
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
26316
26654
  if (cosine(a.embedding, b.embedding) >= minCosine) {
26317
26655
  cluster.push(b);
26318
26656
  consumed.add(b.id);
@@ -36734,6 +37072,12 @@ function cosine3(a, b) {
36734
37072
  for (let i2 = 0; i2 < a.length; i2++) dot += a[i2] * b[i2];
36735
37073
  return dot;
36736
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
+ }
36737
37081
  function resolveSymbol2(store, args2) {
36738
37082
  const id = args2["nodeId"];
36739
37083
  if (typeof id === "string" && store.getNode(id)) return id;
@@ -37084,7 +37428,7 @@ var init_mcp = __esm({
37084
37428
  },
37085
37429
  {
37086
37430
  name: "errata.neighbors",
37087
- 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.",
37088
37432
  inputSchema: {
37089
37433
  type: "object",
37090
37434
  properties: {
@@ -37093,9 +37437,27 @@ var init_mcp = __esm({
37093
37437
  limit: { type: "number", description: "Max edges per direction (default 30)" }
37094
37438
  }
37095
37439
  },
37096
- handler: (args2, store) => {
37440
+ handler: async (args2, store, ctx) => {
37097
37441
  const id = resolveSymbol2(store, args2);
37098
- 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
+ }
37099
37461
  const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 30)));
37100
37462
  const node2 = store.getNode(id);
37101
37463
  if (!node2) return { found: false, reason: "not-found" };
@@ -37142,32 +37504,17 @@ var init_mcp = __esm({
37142
37504
  handler: async (args2, store, ctx) => {
37143
37505
  const id = resolveSymbol2(store, args2);
37144
37506
  if (!id) {
37145
- const seedId = typeof args2["nodeId"] === "string" ? args2["nodeId"] : null;
37146
- if (seedId && ctx.cloud) {
37147
- const res = await ctx.cloud.search({
37148
- stack: [],
37149
- domains: [],
37150
- kinds: [],
37151
- seed: [seedId],
37152
- limit: args2["limit"] != null ? Number(args2["limit"]) : 20
37153
- }).catch(() => null);
37154
- if (res && res.nodes.length > 0) {
37155
- return {
37156
- seed: seedId,
37157
- seedProvenance: "collective",
37158
- collectiveReachable: true,
37159
- collectiveStatus: "cloud-seed",
37160
- count: res.nodes.length,
37161
- results: res.nodes.map((n) => ({
37162
- id: n.id,
37163
- label: n.label,
37164
- name: n.description,
37165
- score: Number((n.extractionConfidence ?? 0.5).toFixed(4)),
37166
- hops: 1,
37167
- provenance: "collective"
37168
- }))
37169
- };
37170
- }
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
+ };
37171
37518
  }
37172
37519
  return unresolved(args2);
37173
37520
  }
@@ -37324,7 +37671,7 @@ var init_mcp = __esm({
37324
37671
  },
37325
37672
  {
37326
37673
  name: "errata.why",
37327
- 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. 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.",
37328
37675
  inputSchema: {
37329
37676
  type: "object",
37330
37677
  properties: {
@@ -37334,12 +37681,52 @@ var init_mcp = __esm({
37334
37681
  limit: { type: "number", description: "Default 30" }
37335
37682
  }
37336
37683
  },
37337
- handler: (args2, store) => {
37684
+ handler: async (args2, store, ctx) => {
37338
37685
  const id = resolveSymbol2(store, args2);
37339
- 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
+ }
37340
37708
  const maxHops = Math.max(1, Math.min(8, Number(args2["maxHops"] ?? 4)));
37341
37709
  const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? 30)));
37342
- return { found: true, ...causalChain(store, { seedId: id, direction: "both", maxHops, limit }) };
37710
+ const local = causalChain(store, { seedId: id, direction: "both", maxHops, limit });
37711
+ try {
37712
+ const path2 = sharedStorePath();
37713
+ if (!existsSync10(path2)) return { found: true, ...local };
37714
+ const shared = openGraphStore({ path: path2 });
37715
+ try {
37716
+ if (!shared.getNode(id)) return { found: true, ...local };
37717
+ const l2 = causalChain(shared, { seedId: id, direction: "both", maxHops, limit });
37718
+ const seen = new Set(local.nodes.map((n) => n.id));
37719
+ const merged = [
37720
+ ...local.nodes,
37721
+ ...l2.nodes.filter((n) => !seen.has(n.id)).map((n) => ({ ...n, store: "shared" }))
37722
+ ].sort((a, b) => (b.relevance ?? 0) - (a.relevance ?? 0)).slice(0, limit);
37723
+ return { found: true, seedId: local.seedId, nodes: merged };
37724
+ } finally {
37725
+ shared.close();
37726
+ }
37727
+ } catch {
37728
+ return { found: true, ...local };
37729
+ }
37343
37730
  }
37344
37731
  },
37345
37732
  {
@@ -37500,11 +37887,11 @@ var init_mcp = __esm({
37500
37887
  },
37501
37888
  {
37502
37889
  name: "errata.problems",
37503
- 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.",
37504
37891
  inputSchema: {
37505
37892
  type: "object",
37506
37893
  properties: {
37507
- status: { type: "string", description: "open | resolved | retracted | all (default open)" },
37894
+ status: { type: "string", description: "open | resolved | retracted | constraint | all (default open)" },
37508
37895
  limit: { type: "number" }
37509
37896
  }
37510
37897
  },
@@ -37514,7 +37901,7 @@ var init_mcp = __esm({
37514
37901
  const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
37515
37902
  const rows = store.findAllVersionsByLabel("Problem").map((p) => {
37516
37903
  const ra = p.attrs["resolvedAs"];
37517
- 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";
37518
37905
  const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
37519
37906
  const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
37520
37907
  return {
@@ -37522,17 +37909,21 @@ var init_mcp = __esm({
37522
37909
  problem: p.description,
37523
37910
  status,
37524
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" } : {},
37525
37915
  ...p.attrs["provisional"] ? { provisional: true } : {},
37526
37916
  ...p.attrs["resolvedReason"] ? { reason: String(p.attrs["resolvedReason"]) } : {},
37527
37917
  ...anchor ? { anchor } : {}
37528
37918
  };
37529
37919
  });
37530
- 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";
37531
37921
  const items = rows.filter((r) => inScope(r.status)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
37532
37922
  const countBy = (s) => rows.filter((r) => r.status === s).length;
37533
37923
  return {
37534
37924
  open: countBy("open"),
37535
37925
  resolved: countBy("resolved"),
37926
+ constraints: countBy("constraint"),
37536
37927
  retracted: {
37537
37928
  falsePositive: countBy("false_positive"),
37538
37929
  dissolution: countBy("invalid"),
@@ -37621,7 +38012,8 @@ var init_mcp = __esm({
37621
38012
  inputSchema: { type: "object", properties: {} },
37622
38013
  handler: (_args, store) => {
37623
38014
  const problems = store.findNodesByLabel("Problem");
37624
- 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));
37625
38017
  const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
37626
38018
  const allProblems = store.findAllVersionsByLabel("Problem");
37627
38019
  const ungroundedDerived = store.findNodesByLabel("Claim").filter((c) => c.attrs["crystallized"] === "derived" && Number(c.attrs["groundedSupport"] ?? 0) === 0);
@@ -37631,6 +38023,7 @@ var init_mcp = __esm({
37631
38023
  strandedSymbols: findStrandedSymbols(store).length,
37632
38024
  openProblems: open2.length,
37633
38025
  openProblemsWithoutFix: noFix.length,
38026
+ designConstraints: constraints.length,
37634
38027
  retractedFalsePositive: allProblems.filter((p) => p.attrs["resolvedAs"] === "false_positive").length,
37635
38028
  retractedDissolution: allProblems.filter((p) => p.attrs["resolvedAs"] === "invalid").length,
37636
38029
  ungroundedDerivedClaims: ungroundedDerived.length,
@@ -38202,7 +38595,7 @@ function formatProblemsMd(r) {
38202
38595
  const lines = [head("problems")];
38203
38596
  const ret = r.retracted ?? { falsePositive: 0, dissolution: 0, duplicate: 0 };
38204
38597
  lines.push(
38205
- `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`
38206
38599
  );
38207
38600
  lines.push(`showing ${r.count}`);
38208
38601
  lines.push("");
@@ -45940,10 +46333,13 @@ function recallForFile(store, relPath) {
45940
46333
  recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
45941
46334
  );
45942
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
+ }
45943
46339
  for (const n of priors.related.slice(0, 4)) {
45944
46340
  lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
45945
46341
  }
45946
- const total = priors.openProblems.length + priors.related.length;
46342
+ const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
45947
46343
  return `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
45948
46344
  ${lines.join("\n")}
45949
46345
  ` + buildFileRecallInstruction();
@@ -46915,12 +47311,12 @@ var init_report_render = __esm({
46915
47311
 
46916
47312
  // src/cli.ts
46917
47313
  init_src5();
46918
- import { closeSync as closeSync2, existsSync as existsSync24, openSync as openSync2, readFileSync as readFileSync22, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
46919
- 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";
46920
47316
  import { spawn as spawn3 } from "node:child_process";
46921
47317
 
46922
47318
  // src/daemon.ts
46923
- import { existsSync as existsSync19, writeFileSync as writeFileSync15 } from "node:fs";
47319
+ import { existsSync as existsSync20, writeFileSync as writeFileSync17 } from "node:fs";
46924
47320
 
46925
47321
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
46926
47322
  import { createServer as createServerHTTP } from "http";
@@ -47500,8 +47896,8 @@ init_config();
47500
47896
 
47501
47897
  // src/engine.ts
47502
47898
  import { execFileSync as execFileSync3 } from "node:child_process";
47503
- 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";
47504
- 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";
47505
47901
 
47506
47902
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
47507
47903
  import { stat as statcb } from "fs";
@@ -48232,9 +48628,9 @@ var NodeFsHandler = class {
48232
48628
  if (this.fsw.closed) {
48233
48629
  return;
48234
48630
  }
48235
- const dirname10 = sysPath.dirname(file2);
48631
+ const dirname11 = sysPath.dirname(file2);
48236
48632
  const basename5 = sysPath.basename(file2);
48237
- const parent = this.fsw._getWatchedDir(dirname10);
48633
+ const parent = this.fsw._getWatchedDir(dirname11);
48238
48634
  let prevStats = stats;
48239
48635
  if (parent.has(basename5))
48240
48636
  return;
@@ -48261,7 +48657,7 @@ var NodeFsHandler = class {
48261
48657
  prevStats = newStats2;
48262
48658
  }
48263
48659
  } catch (error48) {
48264
- this.fsw._remove(dirname10, basename5);
48660
+ this.fsw._remove(dirname11, basename5);
48265
48661
  }
48266
48662
  } else if (parent.has(basename5)) {
48267
48663
  const at = newStats.atimeMs;
@@ -49366,6 +49762,39 @@ init_review2();
49366
49762
  import { closeSync, existsSync as existsSync12, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
49367
49763
  import { basename as basename3, dirname as dirname8, join as join15 } from "node:path";
49368
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
+ }
49369
49798
  function readTail(path2, maxBytes) {
49370
49799
  let fd;
49371
49800
  try {
@@ -49443,7 +49872,13 @@ function isUserTurnBoundary(obj) {
49443
49872
  return blocks.some((b) => b["type"] === "text");
49444
49873
  }
49445
49874
  function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2e6) {
49446
- 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) {
49447
49882
  if (!raw2) return [];
49448
49883
  const turns = [];
49449
49884
  const lines = raw2.split(/\r?\n/);
@@ -49566,10 +50001,13 @@ init_src4();
49566
50001
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
49567
50002
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
49568
50003
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
49569
- 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;
49570
50005
  var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
49571
50006
  var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
49572
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");
49573
50011
  var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
49574
50012
  var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
49575
50013
  var AIDS_PREFIX = /^\s*aids:(?:#([a-z][\w-]{0,63}))?\s*/i;
@@ -49579,8 +50017,10 @@ var ALTERNATIVE_PREFIX = /^\s*alternative:(?:#([a-z][\w-]{0,63}))?\s*/i;
49579
50017
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
49580
50018
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
49581
50019
  var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
50020
+ var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
50021
+ var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
49582
50022
  var CAUSE_TEXT_MIN = 8;
49583
- 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;
49584
50024
  var CONSTRAINT_MIN = 8;
49585
50025
  function clauseBefore(sentence, idx) {
49586
50026
  const win = sentence.slice(Math.max(0, idx - 80), idx);
@@ -49595,6 +50035,17 @@ function fixRationaleBefore(sentence, idx) {
49595
50035
  const clause = m ? win.slice(m.index + 1) : win;
49596
50036
  return clause.replace(/\s+/g, " ").trim();
49597
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
+ }
49598
50049
  function parseInlineTags(text) {
49599
50050
  const out2 = [];
49600
50051
  const deFenced = text.replace(/```[\s\S]*?```/g, " ");
@@ -49608,9 +50059,11 @@ function parseInlineTags(text) {
49608
50059
  (m) => /\[[!?]|\((?:fix|cause|constraint|tried|failed):|\(\[/.test(m) ? " " : m
49609
50060
  );
49610
50061
  const sfield = sentence.replace(/\s+/g, " ").trim().slice(0, 160);
50062
+ const citeMatched = /* @__PURE__ */ new Set();
49611
50063
  CITE_GROUP_RE.lastIndex = 0;
49612
50064
  let g;
49613
50065
  while ((g = CITE_GROUP_RE.exec(sentence)) !== null) {
50066
+ citeMatched.add(g.index);
49614
50067
  let content = g[1];
49615
50068
  if (/^\s*pattern:/i.test(content)) continue;
49616
50069
  const fixM = FIX_PREFIX.exec(content);
@@ -49664,7 +50117,7 @@ function parseInlineTags(text) {
49664
50117
  if (!isFix && NEG.test(clauseBefore(sentence, g.index))) continue;
49665
50118
  let fixRationale;
49666
50119
  if (isFix) {
49667
- const r = fixRationaleBefore(sentence, g.index);
50120
+ const r = deriveFixRationale(sentence, g.index, g.index + g[0].length);
49668
50121
  if (r.length >= FIX_RATIONALE_MIN) fixRationale = r;
49669
50122
  }
49670
50123
  HANDLE_RE.lastIndex = 0;
@@ -49689,6 +50142,55 @@ function parseInlineTags(text) {
49689
50142
  out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49690
50143
  }
49691
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
+ }
49692
50194
  FLAG_RE.lastIndex = 0;
49693
50195
  let f;
49694
50196
  while ((f = FLAG_RE.exec(sentence)) !== null) {
@@ -49774,6 +50276,18 @@ function parseInlineTags(text) {
49774
50276
  const raw3 = dm[1].replace(/\s+/g, " ").trim();
49775
50277
  if (raw3.length >= 3) out2.push({ kind: "domain", domainText: raw3, sentence: sfield });
49776
50278
  }
50279
+ PACKAGE_RE.lastIndex = 0;
50280
+ let pk;
50281
+ while ((pk = PACKAGE_RE.exec(sentence)) !== null) {
50282
+ const raw3 = pk[1].replace(/\s+/g, " ").trim();
50283
+ if (raw3.length >= 2) out2.push({ kind: "package", packageText: raw3, sentence: sfield });
50284
+ }
50285
+ COMPONENT_RE.lastIndex = 0;
50286
+ let cm;
50287
+ while ((cm = COMPONENT_RE.exec(sentence)) !== null) {
50288
+ const raw3 = cm[1].replace(/\s+/g, " ").trim();
50289
+ if (raw3.length >= 2) out2.push({ kind: "component", componentText: raw3, sentence: sfield });
50290
+ }
49777
50291
  CONSTRAINT_RE.lastIndex = 0;
49778
50292
  let c;
49779
50293
  while ((c = CONSTRAINT_RE.exec(sentence)) !== null) {
@@ -49808,7 +50322,12 @@ var LABEL_PAIR = {
49808
50322
  // EDGE_RULES-clean by construction: CONCERNS allows exactly these sources.
49809
50323
  "Problem>Tool": "CONCERNS",
49810
50324
  "Solution>Tool": "CONCERNS",
49811
- "RootCause>Tool": "CONCERNS"
50325
+ "RootCause>Tool": "CONCERNS",
50326
+ // Agent-named component anchors (OM-agent-anchors): a Component is a
50327
+ // framework/product-level unit the knowledge is ABOUT — CONCERNS, like Tool.
50328
+ "Problem>Component": "CONCERNS",
50329
+ "Solution>Component": "CONCERNS",
50330
+ "RootCause>Component": "CONCERNS"
49812
50331
  };
49813
50332
  var STACK_GROUNDING_EDGES = ["WRITTEN_IN", "DEPENDS_ON", "OCCURS_IN"];
49814
50333
  var TIEBREAK = [
@@ -49907,9 +50426,9 @@ function harvestInlineTags(store, text, opts) {
49907
50426
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
49908
50427
  const mintPriors = opts.mintPriors ?? true;
49909
50428
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
49910
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], refutes: [], corroborations: [] };
50429
+ const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
49911
50430
  const tags = parseInlineTags(text);
49912
- 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);
49913
50432
  const bindSymptom = (seq, threadId) => {
49914
50433
  if (threadId) {
49915
50434
  const hit = symptomSeqs.find((s) => s.threadId === threadId);
@@ -49939,7 +50458,7 @@ function harvestInlineTags(store, text, opts) {
49939
50458
  ...tag.threadId ? { threadId: tag.threadId } : {}
49940
50459
  });
49941
50460
  } else if (tag.kind === "constraint") {
49942
- plan.problems.push({ statement: tag.statement, kind: "problem" });
50461
+ plan.problems.push({ statement: tag.statement, kind: "constraint" });
49943
50462
  } else if (tag.kind === "fix") {
49944
50463
  if (tag.handle) {
49945
50464
  const problemId = resolveHandle(store, tag.handle, opts.handleMap);
@@ -50047,6 +50566,16 @@ function harvestInlineTags(store, text, opts) {
50047
50566
  ...tag.threadId ? { threadId: tag.threadId } : {},
50048
50567
  evidence: b.evidence
50049
50568
  });
50569
+ } else if (tag.kind === "package" || tag.kind === "component") {
50570
+ const b = bindSymptom(tag.seq, tag.threadId);
50571
+ const bound = {
50572
+ ...b.statement ? { boundStatement: b.statement } : {},
50573
+ ...b.problemId ? { problemId: b.problemId } : {},
50574
+ ...tag.threadId ? { threadId: tag.threadId } : {},
50575
+ evidence: b.evidence
50576
+ };
50577
+ if (tag.kind === "package") plan.packages.push({ packageText: tag.packageText, ...bound });
50578
+ else plan.components.push({ componentText: tag.componentText, ...bound });
50050
50579
  } else if (tag.kind === "attempt" || tag.kind === "failure") {
50051
50580
  for (const h of tag.refuteHandles ?? []) {
50052
50581
  const nodeId = resolveHandle(store, h, opts.handleMap);
@@ -50071,6 +50600,17 @@ function harvestInlineTags(store, text, opts) {
50071
50600
  boundStatement: b.statement
50072
50601
  });
50073
50602
  }
50603
+ } else if (tag.kind === "prior") {
50604
+ const targetId = resolveHandle(store, tag.handle, opts.handleMap);
50605
+ const target = targetId ? store.getNode(targetId) : null;
50606
+ const citedLabel = target?.label ?? opts.handleMap[tag.handle]?.label;
50607
+ if (targetId && citedLabel && CORROBORATABLE_LABELS.has(citedLabel)) {
50608
+ const witnessKey = `corrob:${targetId}:lean:${digest({ h: tag.handle })}`.slice(0, 72);
50609
+ if (!plan.corroborations.some((c) => c.witnessKey === witnessKey)) {
50610
+ plan.corroborations.push({ nodeId: targetId, witnessKey });
50611
+ }
50612
+ }
50613
+ if (mintPriors && source && target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
50074
50614
  } else if (mintPriors && source) {
50075
50615
  const targetId = resolveHandle(store, tag.handle, opts.handleMap);
50076
50616
  const target = targetId ? store.getNode(targetId) : null;
@@ -50079,6 +50619,20 @@ function harvestInlineTags(store, text, opts) {
50079
50619
  }
50080
50620
  return plan;
50081
50621
  }
50622
+ var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
50623
+ "Problem",
50624
+ "Solution",
50625
+ "RootCause",
50626
+ "Pattern",
50627
+ "AntiPattern",
50628
+ "Technique",
50629
+ "Weakness"
50630
+ ]);
50631
+ function stampWitnessOrigin(items, origin) {
50632
+ if (!origin) return [...items];
50633
+ const o = digest({ p: origin }).slice(0, 12);
50634
+ return items.map((i2) => ({ ...i2, witnessKey: `${i2.witnessKey}:o:${o}` }));
50635
+ }
50082
50636
 
50083
50637
  // src/rollup.ts
50084
50638
  function readConversation(transcriptPath, includeThinking = true, maxChars = 6e4) {
@@ -50124,7 +50678,10 @@ function parseRollupJson(text) {
50124
50678
  ...str("cause") ? { cause: str("cause") } : {},
50125
50679
  ...str("fix") ? { fix: str("fix") } : {},
50126
50680
  ...str("anchor") ? { anchor: str("anchor") } : {},
50127
- 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"
50128
50685
  });
50129
50686
  }
50130
50687
  return flags2.slice(0, 12);
@@ -50162,6 +50719,174 @@ ${conversation}` }]
50162
50719
  };
50163
50720
  }
50164
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
+
50165
50890
  // src/engine.ts
50166
50891
  init_symbol_summaries();
50167
50892
  init_reconcile();
@@ -50360,11 +51085,11 @@ init_outbox();
50360
51085
  init_src8();
50361
51086
  init_src();
50362
51087
  init_src2();
50363
- import { readFileSync as readFileSync11 } from "node:fs";
50364
- import { join as join16 } from "node:path";
51088
+ import { readFileSync as readFileSync12 } from "node:fs";
51089
+ import { join as join17 } from "node:path";
50365
51090
  function loadClaimIgnorePatterns(workspaceRoot) {
50366
51091
  try {
50367
- 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());
50368
51093
  } catch {
50369
51094
  return [];
50370
51095
  }
@@ -50496,14 +51221,16 @@ function generalizePrincipleForSync(principle, level = 2) {
50496
51221
  delete node2.logicalId;
50497
51222
  return node2;
50498
51223
  }
50499
- 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}` } : {};
50500
51227
  const selected = sharedStore.findNodesByLabel("Claim").filter((n) => isSyncablePrinciple(n, ignorePatterns));
50501
51228
  for (const n of selected) {
50502
51229
  if (Number(n.attrs["abstractionLevel"] ?? ABSTRACTION_LEVEL.CONCRETE) !== ABSTRACTION_LEVEL.PRINCIPLE || n.attrs["provisional"] !== false) {
50503
51230
  throw new Error(`buildPrincipleIngest: refusing to sync non-canonical-principle ${n.id} \u2014 C7 invariant violated`);
50504
51231
  }
50505
51232
  }
50506
- const nodes = selected.map((n) => generalizePrincipleForSync(n, level));
51233
+ const nodes = selected.map((n) => ({ ...generalizePrincipleForSync(n, level), ...claim }));
50507
51234
  if (nodes.length === 0) return null;
50508
51235
  const base = {
50509
51236
  daemonVersion,
@@ -50556,11 +51283,17 @@ function isSyncableRoute(edge2, shared, triage, ignorePatterns = []) {
50556
51283
  ].join(" ").toLowerCase();
50557
51284
  return !ignorePatterns.some((p) => blob.includes(p));
50558
51285
  }
50559
- 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) {
50560
51292
  const disc = edge2.attrs["discriminator"];
50561
51293
  const local = edge2.attrs["perContext"] ?? {};
50562
51294
  const perContext = {};
50563
51295
  for (const [b, c] of Object.entries(local)) {
51296
+ if (!includeScopedBuckets && isScopedBucket(b)) continue;
50564
51297
  const independent = c.independent ?? c.contributors?.length;
50565
51298
  const inferredIndependent = c.inferredIndependent ?? c.inferredContributors?.length;
50566
51299
  perContext[b] = {
@@ -50581,11 +51314,52 @@ function generalizeRouteForSync(edge2, level) {
50581
51314
  navFailures: 0
50582
51315
  };
50583
51316
  }
50584
- function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2) {
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
+ }
51353
+ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
51354
+ const lang = opts.primaryLanguage?.trim().toLowerCase();
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 } : {};
50585
51359
  const nodes = [];
50586
51360
  const edges = [];
50587
51361
  const seenIds = /* @__PURE__ */ new Set();
50588
- const shareEndpoint = (id) => {
51362
+ const shareEndpoint = (id, anchor) => {
50589
51363
  if (seenIds.has(id)) return;
50590
51364
  const n = shared.getNode(id);
50591
51365
  if (!n) return;
@@ -50594,12 +51368,14 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50594
51368
  ...n,
50595
51369
  description: generalize(n.description, { level }).text,
50596
51370
  embedding: [],
50597
- attrs: { scope: {} }
51371
+ attrs: { scope: {} },
51372
+ ...claimOf(anchor ?? fallbackAnchor)
50598
51373
  });
50599
51374
  };
50600
51375
  for (const tri of shared.findNodesByLabel("Triage")) {
50601
51376
  const routes = shared.outEdges(tri.id, ["CONFIRMS", "INDICATES", "ROUTES_TO"]).filter((e) => isSyncableRoute(e, shared, tri, ignorePatterns));
50602
51377
  if (routes.length === 0) continue;
51378
+ const triAnchor = witnessedAnchor(routes, tokenAnchors);
50603
51379
  if (!seenIds.has(tri.id)) {
50604
51380
  seenIds.add(tri.id);
50605
51381
  const statement = String(tri.attrs["statement"] ?? "");
@@ -50609,20 +51385,26 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50609
51385
  embedding: [],
50610
51386
  attrs: {
50611
51387
  ...statement ? { statement: generalize(statement, { level }).text } : {},
50612
- ...tri.attrs["perContextSeen"] ? { perContextSeen: tri.attrs["perContextSeen"] } : {}
50613
- }
51388
+ ...tri.attrs["perContextSeen"] ? {
51389
+ perContextSeen: filterScopedBucketKeys(
51390
+ tri.attrs["perContextSeen"],
51391
+ includeScopedBuckets
51392
+ )
51393
+ } : {}
51394
+ },
51395
+ ...claimOf(triAnchor ?? fallbackAnchor)
50614
51396
  });
50615
51397
  }
50616
51398
  for (const tb of shared.inEdges(tri.id, ["TRIAGED_BY"])) {
50617
- shareEndpoint(tb.from);
51399
+ shareEndpoint(tb.from, triAnchor);
50618
51400
  edges.push({ ...tb, navSuccesses: 0, navFailures: 0 });
50619
51401
  }
50620
51402
  for (const e of routes) {
50621
51403
  if (!(e.type === "CONFIRMS" || e.type === "ROUTES_TO" && e.attrs["provisional"] === false)) {
50622
51404
  throw new Error(`buildTriageIngest: refusing to sync non-router route ${e.id} \u2014 C7 invariant violated`);
50623
51405
  }
50624
- shareEndpoint(e.to);
50625
- edges.push(generalizeRouteForSync(e, level));
51406
+ shareEndpoint(e.to, witnessedAnchor([e], tokenAnchors));
51407
+ edges.push(generalizeRouteForSync(e, level, includeScopedBuckets));
50626
51408
  }
50627
51409
  }
50628
51410
  if (edges.length === 0) return null;
@@ -50668,22 +51450,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
50668
51450
  }
50669
51451
 
50670
51452
  // src/git-sensor.ts
50671
- import { existsSync as existsSync13, readFileSync as readFileSync12, watch as fsWatch } from "node:fs";
50672
- 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";
50673
51455
  function readFirstLine(path2) {
50674
51456
  try {
50675
- return readFileSync12(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51457
+ return readFileSync13(path2, "utf8").split(/\r?\n/, 1)[0].trim();
50676
51458
  } catch {
50677
51459
  return null;
50678
51460
  }
50679
51461
  }
50680
51462
  function readGitRefState(gitDir) {
50681
- const head2 = readFirstLine(join17(gitDir, "HEAD"));
51463
+ const head2 = readFirstLine(join18(gitDir, "HEAD"));
50682
51464
  const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
50683
51465
  const branch = m ? m[1] : null;
50684
51466
  let sha2 = null;
50685
51467
  if (branch) {
50686
- sha2 = readFirstLine(join17(gitDir, "refs", "heads", branch));
51468
+ sha2 = readFirstLine(join18(gitDir, "refs", "heads", branch));
50687
51469
  if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
50688
51470
  } else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
50689
51471
  sha2 = head2;
@@ -50691,13 +51473,13 @@ function readGitRefState(gitDir) {
50691
51473
  return {
50692
51474
  branch,
50693
51475
  sha: sha2,
50694
- mergeHeadExists: existsSync13(join17(gitDir, "MERGE_HEAD")),
50695
- origHeadExists: existsSync13(join17(gitDir, "ORIG_HEAD"))
51476
+ mergeHeadExists: existsSync14(join18(gitDir, "MERGE_HEAD")),
51477
+ origHeadExists: existsSync14(join18(gitDir, "ORIG_HEAD"))
50696
51478
  };
50697
51479
  }
50698
51480
  function shaFromPackedRefs(gitDir, ref) {
50699
51481
  try {
50700
- 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/)) {
50701
51483
  const [sha2, name2] = line.split(/\s+/);
50702
51484
  if (name2 === ref && sha2) return sha2;
50703
51485
  }
@@ -50731,7 +51513,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
50731
51513
  const settle = () => {
50732
51514
  if (timer) clearTimeout(timer);
50733
51515
  timer = setTimeout(() => {
50734
- if (existsSync13(join17(gitDir, "index.lock"))) {
51516
+ if (existsSync14(join18(gitDir, "index.lock"))) {
50735
51517
  settle();
50736
51518
  return;
50737
51519
  }
@@ -50742,7 +51524,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
50742
51524
  }, debounceMs);
50743
51525
  };
50744
51526
  for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
50745
- const p = join17(gitDir, sub);
51527
+ const p = join18(gitDir, sub);
50746
51528
  try {
50747
51529
  watchers.push(fsWatch(p, settle));
50748
51530
  } catch {
@@ -50967,21 +51749,21 @@ var TelemetryRecorder = class {
50967
51749
 
50968
51750
  // src/skills.ts
50969
51751
  import {
50970
- existsSync as existsSync14,
51752
+ existsSync as existsSync15,
50971
51753
  mkdirSync as mkdirSync6,
50972
- readFileSync as readFileSync13,
50973
- readdirSync as readdirSync6,
51754
+ readFileSync as readFileSync14,
51755
+ readdirSync as readdirSync7,
50974
51756
  unlinkSync as unlinkSync2,
50975
- writeFileSync as writeFileSync11
51757
+ writeFileSync as writeFileSync12
50976
51758
  } from "node:fs";
50977
- import { basename as basename4, join as join18 } from "node:path";
51759
+ import { basename as basename4, join as join19 } from "node:path";
50978
51760
  function skillFileName(id) {
50979
51761
  return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
50980
51762
  }
50981
51763
  function readSkillManifest(manifestPath) {
50982
- if (!existsSync14(manifestPath)) return [];
51764
+ if (!existsSync15(manifestPath)) return [];
50983
51765
  try {
50984
- const parsed = JSON.parse(readFileSync13(manifestPath, "utf8"));
51766
+ const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
50985
51767
  return (parsed.skills ?? []).map((s) => ({
50986
51768
  title: s.title ?? "",
50987
51769
  layer: s.layer ?? "technique",
@@ -50992,12 +51774,12 @@ function readSkillManifest(manifestPath) {
50992
51774
  return [];
50993
51775
  }
50994
51776
  }
50995
- async function syncSkills(paths, client, seed, pins = []) {
50996
- 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);
50997
51779
  const staleDaemon = res.staleDaemon && res.latestDaemonVersion ? { latest: res.latestDaemonVersion } : void 0;
50998
51780
  mkdirSync6(paths.skillsDir, { recursive: true });
50999
51781
  if (res.skills.length === 0 && pins.length === 0) {
51000
- const existing = readdirSync6(paths.skillsDir).filter((f) => f.endsWith(".md"));
51782
+ const existing = readdirSync7(paths.skillsDir).filter((f) => f.endsWith(".md"));
51001
51783
  if (existing.length > 0) return { written: 0, pruned: 0, ...staleDaemon ? { staleDaemon } : {} };
51002
51784
  }
51003
51785
  const rows = [];
@@ -51005,7 +51787,7 @@ async function syncSkills(paths, client, seed, pins = []) {
51005
51787
  for (const s of res.skills) {
51006
51788
  const fileName = skillFileName(s.id);
51007
51789
  keep.add(fileName);
51008
- writeFileSync11(join18(paths.skillsDir, fileName), s.markdown, "utf8");
51790
+ writeFileSync12(join19(paths.skillsDir, fileName), s.markdown, "utf8");
51009
51791
  rows.push({
51010
51792
  id: s.id,
51011
51793
  title: s.title,
@@ -51018,7 +51800,7 @@ async function syncSkills(paths, client, seed, pins = []) {
51018
51800
  const fileName = skillFileName(p.id);
51019
51801
  if (keep.has(fileName)) continue;
51020
51802
  keep.add(fileName);
51021
- writeFileSync11(join18(paths.skillsDir, fileName), p.markdown, "utf8");
51803
+ writeFileSync12(join19(paths.skillsDir, fileName), p.markdown, "utf8");
51022
51804
  rows.push({
51023
51805
  id: p.id,
51024
51806
  title: p.title,
@@ -51028,17 +51810,17 @@ async function syncSkills(paths, client, seed, pins = []) {
51028
51810
  });
51029
51811
  }
51030
51812
  let pruned = 0;
51031
- for (const f of readdirSync6(paths.skillsDir)) {
51813
+ for (const f of readdirSync7(paths.skillsDir)) {
51032
51814
  if (!f.endsWith(".md")) continue;
51033
51815
  if (keep.has(basename4(f))) continue;
51034
51816
  try {
51035
- unlinkSync2(join18(paths.skillsDir, f));
51817
+ unlinkSync2(join19(paths.skillsDir, f));
51036
51818
  pruned++;
51037
51819
  } catch {
51038
51820
  }
51039
51821
  }
51040
51822
  rows.sort((a, b) => a.id.localeCompare(b.id));
51041
- writeFileSync11(
51823
+ writeFileSync12(
51042
51824
  paths.skillsManifest,
51043
51825
  JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
51044
51826
  "utf8"
@@ -51047,24 +51829,25 @@ async function syncSkills(paths, client, seed, pins = []) {
51047
51829
  }
51048
51830
 
51049
51831
  // src/agent-skills.ts
51832
+ init_src2();
51050
51833
  import {
51051
51834
  cpSync,
51052
- existsSync as existsSync15,
51835
+ existsSync as existsSync16,
51053
51836
  lstatSync,
51054
51837
  mkdirSync as mkdirSync7,
51055
- readFileSync as readFileSync14,
51056
- readdirSync as readdirSync7,
51838
+ readFileSync as readFileSync15,
51839
+ readdirSync as readdirSync8,
51057
51840
  rmSync as rmSync2,
51058
51841
  symlinkSync,
51059
- writeFileSync as writeFileSync12
51842
+ writeFileSync as writeFileSync13
51060
51843
  } from "node:fs";
51061
- import { join as join19 } from "node:path";
51844
+ import { join as join20 } from "node:path";
51062
51845
  var SKILL_NS = "errata-";
51063
51846
  var HARNESS_SKILL_DIRS = [
51064
- { configDir: ".claude", skillsDir: join19(".claude", "skills") },
51847
+ { configDir: ".claude", skillsDir: join20(".claude", "skills") },
51065
51848
  // Cursor adopted the standard; its exact project dir is still moving — kept
51066
51849
  // best-effort and gated on `.cursor/` presence so we never create it blind.
51067
- { configDir: ".cursor", skillsDir: join19(".cursor", "skills") }
51850
+ { configDir: ".cursor", skillsDir: join20(".cursor", "skills") }
51068
51851
  ];
51069
51852
  function skillSlug(title, id) {
51070
51853
  const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
@@ -51079,22 +51862,42 @@ function deriveDescription(title, layer, body2) {
51079
51862
  const when = ` Apply when working on ${layer === "antipattern" ? "code that risks this pitfall" : "problems in this area"}.`;
51080
51863
  return `${lead}${lead.endsWith(".") ? "" : "."}${when}`.slice(0, 1024);
51081
51864
  }
51082
- 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";
51083
51871
  return `---
51084
51872
  name: ${name2}
51085
51873
  description: ${yamlScalar(description)}
51086
51874
  ---
51087
51875
 
51088
- ${body2.trimEnd()}
51089
- `;
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 });
51090
51893
  }
51091
51894
  function reconcileNamespaced(dir, keep) {
51092
- if (!existsSync15(dir)) return 0;
51895
+ if (!existsSync16(dir)) return 0;
51093
51896
  let pruned = 0;
51094
- for (const name2 of readdirSync7(dir)) {
51897
+ for (const name2 of readdirSync8(dir)) {
51095
51898
  if (!name2.startsWith(SKILL_NS) || keep.has(name2)) continue;
51096
51899
  try {
51097
- rmSync2(join19(dir, name2), { recursive: true, force: true });
51900
+ rmSync2(join20(dir, name2), { recursive: true, force: true });
51098
51901
  pruned++;
51099
51902
  } catch {
51100
51903
  }
@@ -51103,7 +51906,7 @@ function reconcileNamespaced(dir, keep) {
51103
51906
  }
51104
51907
  function linkOrCopy(linkPath, target) {
51105
51908
  try {
51106
- if (existsSync15(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51909
+ if (existsSync16(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51107
51910
  } catch {
51108
51911
  }
51109
51912
  try {
@@ -51124,7 +51927,7 @@ function safeLstat(p) {
51124
51927
  }
51125
51928
  }
51126
51929
  function emitAndProjectSkills(root, skills) {
51127
- const agentsSkillsDir = join19(root, ".agents", "skills");
51930
+ const agentsSkillsDir = join20(root, ".agents", "skills");
51128
51931
  mkdirSync7(agentsSkillsDir, { recursive: true });
51129
51932
  const slugs = [];
51130
51933
  const keep = /* @__PURE__ */ new Set();
@@ -51132,7 +51935,7 @@ function emitAndProjectSkills(root, skills) {
51132
51935
  for (const s of skills) {
51133
51936
  let body2;
51134
51937
  try {
51135
- body2 = readFileSync14(s.bodyPath, "utf8");
51938
+ body2 = readFileSync15(s.bodyPath, "utf8");
51136
51939
  } catch {
51137
51940
  continue;
51138
51941
  }
@@ -51141,10 +51944,10 @@ function emitAndProjectSkills(root, skills) {
51141
51944
  keep.add(slug2);
51142
51945
  slugs.push(slug2);
51143
51946
  const description = deriveDescription(s.title, s.layer, body2);
51144
- mkdirSync7(join19(agentsSkillsDir, slug2), { recursive: true });
51145
- writeFileSync12(
51146
- join19(agentsSkillsDir, slug2, "SKILL.md"),
51147
- 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)),
51148
51951
  "utf8"
51149
51952
  );
51150
51953
  emitted++;
@@ -51152,11 +51955,11 @@ function emitAndProjectSkills(root, skills) {
51152
51955
  reconcileNamespaced(agentsSkillsDir, keep);
51153
51956
  let projected = 0;
51154
51957
  for (const h of HARNESS_SKILL_DIRS) {
51155
- if (!existsSync15(join19(root, h.configDir))) continue;
51156
- const dir = join19(root, h.skillsDir);
51958
+ if (!existsSync16(join20(root, h.configDir))) continue;
51959
+ const dir = join20(root, h.skillsDir);
51157
51960
  mkdirSync7(dir, { recursive: true });
51158
51961
  for (const slug2 of slugs) {
51159
- linkOrCopy(join19(dir, slug2), join19(agentsSkillsDir, slug2));
51962
+ linkOrCopy(join20(dir, slug2), join20(agentsSkillsDir, slug2));
51160
51963
  projected++;
51161
51964
  }
51162
51965
  reconcileNamespaced(dir, keep);
@@ -51165,15 +51968,15 @@ function emitAndProjectSkills(root, skills) {
51165
51968
  return { slugs, emitted, projected };
51166
51969
  }
51167
51970
  function emitInputsFromManifest(erretaDir, manifestPath) {
51168
- if (!existsSync15(manifestPath)) return [];
51971
+ if (!existsSync16(manifestPath)) return [];
51169
51972
  try {
51170
- const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51973
+ const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
51171
51974
  return (parsed.skills ?? []).filter((s) => Boolean(s.id && s.file)).map((s) => ({
51172
51975
  id: s.id,
51173
51976
  title: s.title ?? s.id,
51174
51977
  layer: s.layer ?? "technique",
51175
51978
  confidence: s.confidence ?? 0,
51176
- bodyPath: join19(erretaDir, s.file)
51979
+ bodyPath: join20(erretaDir, s.file)
51177
51980
  }));
51178
51981
  } catch {
51179
51982
  return [];
@@ -51187,17 +51990,17 @@ var GITIGNORE_LINES = [
51187
51990
  ".cursor/skills/errata-*/"
51188
51991
  ];
51189
51992
  function ensureSkillGitignore(root) {
51190
- const path2 = join19(root, ".gitignore");
51993
+ const path2 = join20(root, ".gitignore");
51191
51994
  let current = "";
51192
51995
  try {
51193
- current = existsSync15(path2) ? readFileSync14(path2, "utf8") : "";
51996
+ current = existsSync16(path2) ? readFileSync15(path2, "utf8") : "";
51194
51997
  } catch {
51195
51998
  return;
51196
51999
  }
51197
52000
  if (current.includes(GITIGNORE_MARK)) return;
51198
52001
  const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
51199
52002
  try {
51200
- writeFileSync12(path2, `${current}${prefix}
52003
+ writeFileSync13(path2, `${current}${prefix}
51201
52004
  ${GITIGNORE_LINES.join("\n")}
51202
52005
  `, "utf8");
51203
52006
  } catch {
@@ -51255,6 +52058,203 @@ function createToolRunState() {
51255
52058
  // src/engine.ts
51256
52059
  init_paths();
51257
52060
 
52061
+ // src/profile.ts
52062
+ init_src2();
52063
+ init_paths();
52064
+ import { existsSync as existsSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
52065
+ import { createHash as createHash12 } from "node:crypto";
52066
+ import { join as join22 } from "node:path";
52067
+
52068
+ // src/git-remote.ts
52069
+ init_src();
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";
52072
+ function resolveGitDir(root) {
52073
+ const dotGit = join21(root, ".git");
52074
+ try {
52075
+ const st = statSync4(dotGit);
52076
+ if (st.isDirectory()) return dotGit;
52077
+ const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync16(dotGit, "utf8"));
52078
+ if (!m) return null;
52079
+ const dir = m[1];
52080
+ return isAbsolute3(dir) ? dir : resolve5(root, dir);
52081
+ } catch {
52082
+ return null;
52083
+ }
52084
+ }
52085
+ function gitConfigPath(gitDir) {
52086
+ const commondirFile = join21(gitDir, "commondir");
52087
+ if (existsSync17(commondirFile)) {
52088
+ const common = readFileSync16(commondirFile, "utf8").trim();
52089
+ const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
52090
+ return join21(commonDir, "config");
52091
+ }
52092
+ return join21(gitDir, "config");
52093
+ }
52094
+ function readRemotes(root) {
52095
+ const gitDir = resolveGitDir(root);
52096
+ if (!gitDir) return [];
52097
+ const cfgPath = gitConfigPath(gitDir);
52098
+ if (!existsSync17(cfgPath)) return [];
52099
+ let txt;
52100
+ try {
52101
+ txt = readFileSync16(cfgPath, "utf8");
52102
+ } catch {
52103
+ return [];
52104
+ }
52105
+ const out2 = [];
52106
+ const sectionRe = /^\s*\[remote "([^"]+)"\]\s*$([\s\S]*?)(?=^\s*\[|\s*$(?![\s\S]))/gm;
52107
+ for (const m of txt.matchAll(sectionRe)) {
52108
+ const url2 = /^\s*url\s*=\s*(.+?)\s*$/m.exec(m[2])?.[1];
52109
+ if (url2) out2.push({ name: m[1], url: url2 });
52110
+ }
52111
+ return out2;
52112
+ }
52113
+ function detectRepoLocator(root, remote) {
52114
+ const remotes = readRemotes(root);
52115
+ if (remotes.length === 0) return null;
52116
+ const pick2 = remote ? remotes.find((r) => r.name === remote) ?? null : remotes.find((r) => r.name === "origin") ?? (remotes.length === 1 ? remotes[0] : null);
52117
+ return pick2 ? normalizeRepoLocator(pick2.url) : null;
52118
+ }
52119
+
52120
+ // src/profile.ts
52121
+ function workspaceId(root) {
52122
+ return "wp_" + createHash12("sha256").update(root).digest("hex").slice(0, 12);
52123
+ }
52124
+ function sessionOriginKey(sessionId) {
52125
+ if (!sessionId) return void 0;
52126
+ return "ws_" + createHash12("sha256").update(sessionId).digest("hex").slice(0, 12);
52127
+ }
52128
+ function refreshRepoLocator(root, profile) {
52129
+ const detected = detectRepoLocator(root, profile.repoRemote);
52130
+ if (!detected || detected === profile.repoLocator) return false;
52131
+ profile.repoLocator = detected;
52132
+ saveProfile(root, profile);
52133
+ return true;
52134
+ }
52135
+ function loadProfile(root) {
52136
+ const p = workspacePaths(root);
52137
+ if (!existsSync18(p.workspaceJson)) return null;
52138
+ return JSON.parse(readFileSync17(p.workspaceJson, "utf8"));
52139
+ }
52140
+ function saveProfile(root, profile) {
52141
+ const p = workspacePaths(root);
52142
+ ensureDir(p.configDir);
52143
+ writeFileSync14(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
52144
+ }
52145
+ function autodetectProfile(root) {
52146
+ const id = workspaceId(root);
52147
+ const name2 = root.split(/[\\/]/).filter(Boolean).pop() ?? "workspace";
52148
+ const p = emptyProfile(id, name2);
52149
+ const locator = detectRepoLocator(root);
52150
+ if (locator) p.repoLocator = locator;
52151
+ const pkgPath = join22(root, "package.json");
52152
+ if (existsSync18(pkgPath)) {
52153
+ try {
52154
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
52155
+ p.languages.push("typescript", "javascript");
52156
+ const nodeVer = pkg.engines?.node ?? "node";
52157
+ p.stack.push(`node@${nodeVer}`);
52158
+ const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
52159
+ const interesting = [
52160
+ "next",
52161
+ "react",
52162
+ "vue",
52163
+ "svelte",
52164
+ "express",
52165
+ "hono",
52166
+ "fastify",
52167
+ "@modelcontextprotocol/sdk"
52168
+ ];
52169
+ for (const k of interesting) {
52170
+ if (deps[k]) p.stack.push(k);
52171
+ }
52172
+ } catch {
52173
+ }
52174
+ }
52175
+ const pyproject = join22(root, "pyproject.toml");
52176
+ if (existsSync18(pyproject)) {
52177
+ try {
52178
+ const txt = readFileSync17(pyproject, "utf8");
52179
+ const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
52180
+ p.languages.push("python");
52181
+ p.stack.push(`python@${py ?? "3"}`);
52182
+ const candidates = ["fastapi", "django", "flask", "mcp", "pytest"];
52183
+ for (const k of candidates) {
52184
+ if (new RegExp(`\\b${k}\\b`, "i").test(txt)) p.stack.push(k);
52185
+ }
52186
+ } catch {
52187
+ }
52188
+ }
52189
+ const reqs = join22(root, "requirements.txt");
52190
+ if (existsSync18(reqs)) {
52191
+ if (!p.languages.includes("python")) p.languages.push("python");
52192
+ if (!p.stack.includes("python@3")) p.stack.push("python@3");
52193
+ }
52194
+ if (existsSync18(join22(root, "go.mod"))) {
52195
+ p.languages.push("go");
52196
+ p.stack.push("go");
52197
+ }
52198
+ if (existsSync18(join22(root, "Cargo.toml"))) {
52199
+ p.languages.push("rust");
52200
+ p.stack.push("rust");
52201
+ }
52202
+ p.languages = [...new Set(p.languages)];
52203
+ p.stack = [...new Set(p.stack)];
52204
+ return p;
52205
+ }
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
+
51258
52258
  // src/causal.ts
51259
52259
  var SUPPRESSORS = [
51260
52260
  { kind: "ts-nocheck", re: /@ts-nocheck\b/g },
@@ -51358,148 +52358,6 @@ var CausalBuffer = class {
51358
52358
  }
51359
52359
  };
51360
52360
 
51361
- // src/profile.ts
51362
- init_src2();
51363
- init_paths();
51364
- import { existsSync as existsSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
51365
- import { createHash as createHash12 } from "node:crypto";
51366
- import { join as join21 } from "node:path";
51367
-
51368
- // src/git-remote.ts
51369
- init_src();
51370
- import { existsSync as existsSync16, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
51371
- import { isAbsolute as isAbsolute3, join as join20, resolve as resolve5 } from "node:path";
51372
- function resolveGitDir(root) {
51373
- const dotGit = join20(root, ".git");
51374
- try {
51375
- const st = statSync4(dotGit);
51376
- if (st.isDirectory()) return dotGit;
51377
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync15(dotGit, "utf8"));
51378
- if (!m) return null;
51379
- const dir = m[1];
51380
- return isAbsolute3(dir) ? dir : resolve5(root, dir);
51381
- } catch {
51382
- return null;
51383
- }
51384
- }
51385
- function gitConfigPath(gitDir) {
51386
- const commondirFile = join20(gitDir, "commondir");
51387
- if (existsSync16(commondirFile)) {
51388
- const common = readFileSync15(commondirFile, "utf8").trim();
51389
- const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
51390
- return join20(commonDir, "config");
51391
- }
51392
- return join20(gitDir, "config");
51393
- }
51394
- function readRemotes(root) {
51395
- const gitDir = resolveGitDir(root);
51396
- if (!gitDir) return [];
51397
- const cfgPath = gitConfigPath(gitDir);
51398
- if (!existsSync16(cfgPath)) return [];
51399
- let txt;
51400
- try {
51401
- txt = readFileSync15(cfgPath, "utf8");
51402
- } catch {
51403
- return [];
51404
- }
51405
- const out2 = [];
51406
- const sectionRe = /^\s*\[remote "([^"]+)"\]\s*$([\s\S]*?)(?=^\s*\[|\s*$(?![\s\S]))/gm;
51407
- for (const m of txt.matchAll(sectionRe)) {
51408
- const url2 = /^\s*url\s*=\s*(.+?)\s*$/m.exec(m[2])?.[1];
51409
- if (url2) out2.push({ name: m[1], url: url2 });
51410
- }
51411
- return out2;
51412
- }
51413
- function detectRepoLocator(root, remote) {
51414
- const remotes = readRemotes(root);
51415
- if (remotes.length === 0) return null;
51416
- const pick2 = remote ? remotes.find((r) => r.name === remote) ?? null : remotes.find((r) => r.name === "origin") ?? (remotes.length === 1 ? remotes[0] : null);
51417
- return pick2 ? normalizeRepoLocator(pick2.url) : null;
51418
- }
51419
-
51420
- // src/profile.ts
51421
- function workspaceId(root) {
51422
- return "wp_" + createHash12("sha256").update(root).digest("hex").slice(0, 12);
51423
- }
51424
- function refreshRepoLocator(root, profile) {
51425
- const detected = detectRepoLocator(root, profile.repoRemote);
51426
- if (!detected || detected === profile.repoLocator) return false;
51427
- profile.repoLocator = detected;
51428
- saveProfile(root, profile);
51429
- return true;
51430
- }
51431
- function loadProfile(root) {
51432
- const p = workspacePaths(root);
51433
- if (!existsSync17(p.workspaceJson)) return null;
51434
- return JSON.parse(readFileSync16(p.workspaceJson, "utf8"));
51435
- }
51436
- function saveProfile(root, profile) {
51437
- const p = workspacePaths(root);
51438
- ensureDir(p.configDir);
51439
- writeFileSync13(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
51440
- }
51441
- function autodetectProfile(root) {
51442
- const id = workspaceId(root);
51443
- const name2 = root.split(/[\\/]/).filter(Boolean).pop() ?? "workspace";
51444
- const p = emptyProfile(id, name2);
51445
- const locator = detectRepoLocator(root);
51446
- if (locator) p.repoLocator = locator;
51447
- const pkgPath = join21(root, "package.json");
51448
- if (existsSync17(pkgPath)) {
51449
- try {
51450
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
51451
- p.languages.push("typescript", "javascript");
51452
- const nodeVer = pkg.engines?.node ?? "node";
51453
- p.stack.push(`node@${nodeVer}`);
51454
- const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
51455
- const interesting = [
51456
- "next",
51457
- "react",
51458
- "vue",
51459
- "svelte",
51460
- "express",
51461
- "hono",
51462
- "fastify",
51463
- "@modelcontextprotocol/sdk"
51464
- ];
51465
- for (const k of interesting) {
51466
- if (deps[k]) p.stack.push(k);
51467
- }
51468
- } catch {
51469
- }
51470
- }
51471
- const pyproject = join21(root, "pyproject.toml");
51472
- if (existsSync17(pyproject)) {
51473
- try {
51474
- const txt = readFileSync16(pyproject, "utf8");
51475
- const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
51476
- p.languages.push("python");
51477
- p.stack.push(`python@${py ?? "3"}`);
51478
- const candidates = ["fastapi", "django", "flask", "mcp", "pytest"];
51479
- for (const k of candidates) {
51480
- if (new RegExp(`\\b${k}\\b`, "i").test(txt)) p.stack.push(k);
51481
- }
51482
- } catch {
51483
- }
51484
- }
51485
- const reqs = join21(root, "requirements.txt");
51486
- if (existsSync17(reqs)) {
51487
- if (!p.languages.includes("python")) p.languages.push("python");
51488
- if (!p.stack.includes("python@3")) p.stack.push("python@3");
51489
- }
51490
- if (existsSync17(join21(root, "go.mod"))) {
51491
- p.languages.push("go");
51492
- p.stack.push("go");
51493
- }
51494
- if (existsSync17(join21(root, "Cargo.toml"))) {
51495
- p.languages.push("rust");
51496
- p.stack.push("rust");
51497
- }
51498
- p.languages = [...new Set(p.languages)];
51499
- p.stack = [...new Set(p.stack)];
51500
- return p;
51501
- }
51502
-
51503
52361
  // src/engine.ts
51504
52362
  init_config();
51505
52363
  init_cloud_auth();
@@ -51649,7 +52507,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51649
52507
  }
51650
52508
 
51651
52509
  // src/engine.ts
51652
- var DAEMON_VERSION = true ? "2.0.1-dev.99" : "2.0.0-alpha.0";
52510
+ var DAEMON_VERSION = true ? "2.0.2-dev.133" : "2.0.0-alpha.0";
51653
52511
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51654
52512
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51655
52513
  var GIT_OP_MUTE_MS = 4e3;
@@ -51659,8 +52517,8 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
51659
52517
  function appendIdentityAudit(path2, record2, line) {
51660
52518
  if (!record2.accepted && record2.score <= 0) return;
51661
52519
  try {
51662
- if (existsSync18(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
51663
- renameSync2(path2, `${path2}.1`);
52520
+ if (existsSync19(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52521
+ renameSync3(path2, `${path2}.1`);
51664
52522
  }
51665
52523
  appendFileSync2(path2, line);
51666
52524
  } catch {
@@ -51669,14 +52527,33 @@ function appendIdentityAudit(path2, record2, line) {
51669
52527
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
51670
52528
  function loadTurnCursors(path2) {
51671
52529
  try {
51672
- 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;
51673
52547
  } catch {
51674
52548
  return /* @__PURE__ */ new Map();
51675
52549
  }
51676
52550
  }
51677
- function saveTurnCursors(path2, cursors) {
52551
+ function saveTurnCursors(path2, cursors, offsets) {
51678
52552
  try {
51679
- 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");
51680
52557
  } catch {
51681
52558
  }
51682
52559
  }
@@ -51698,7 +52575,7 @@ function gitSourceWatchTargets(root) {
51698
52575
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
51699
52576
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
51700
52577
  );
51701
- 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));
51702
52579
  } catch {
51703
52580
  }
51704
52581
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -51710,19 +52587,19 @@ function gitSourceWatchTargets(root) {
51710
52587
  if (!f.startsWith(prefix)) continue;
51711
52588
  const rest2 = f.slice(prefix.length);
51712
52589
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
51713
- else targets.add(join22(root, f));
52590
+ else targets.add(join24(root, f));
51714
52591
  }
51715
52592
  for (const c of children) {
51716
- if (IGNORED_PATH.test(join22(root, c) + sep4)) continue;
52593
+ if (IGNORED_PATH.test(join24(root, c) + sep4)) continue;
51717
52594
  if (hasIgnoredChild(c)) addUnder(c);
51718
- else targets.add(join22(root, c));
52595
+ else targets.add(join24(root, c));
51719
52596
  }
51720
52597
  };
51721
52598
  addUnder("");
51722
52599
  if (targets.size > 0) return [...targets];
51723
52600
  } catch {
51724
52601
  }
51725
- 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)));
51726
52603
  }
51727
52604
  function createWorkspaceEngine(opts) {
51728
52605
  const paths = workspacePaths(opts.workspaceRoot);
@@ -51737,6 +52614,10 @@ function createWorkspaceEngine(opts) {
51737
52614
  refreshRepoLocator(opts.workspaceRoot, profile);
51738
52615
  }
51739
52616
  const store = openGraphStore({ path: paths.castalia });
52617
+ let witnessQueue = pruneWitnessQueue(
52618
+ loadWitnessQueue(witnessQueuePath(paths.configDir)),
52619
+ Date.now()
52620
+ );
51740
52621
  const log = openEventLog({ path: paths.eventLog });
51741
52622
  const bundled = import.meta.url.endsWith(".mjs");
51742
52623
  const passWorker = bundled && paths.castalia !== ":memory:" ? new PassWorker({
@@ -51876,7 +52757,7 @@ function createWorkspaceEngine(opts) {
51876
52757
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
51877
52758
  let episodeId2;
51878
52759
  if (srcPaths.length > 0) {
51879
- const abs = srcPaths.map((p) => join22(opts.workspaceRoot, p));
52760
+ const abs = srcPaths.map((p) => join24(opts.workspaceRoot, p));
51880
52761
  try {
51881
52762
  const r = await runReindexPass(
51882
52763
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -51912,8 +52793,8 @@ function createWorkspaceEngine(opts) {
51912
52793
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
51913
52794
  );
51914
52795
  };
51915
- const gitDir = join22(opts.workspaceRoot, ".git");
51916
- if (existsSync18(gitDir)) {
52796
+ const gitDir = join24(opts.workspaceRoot, ".git");
52797
+ if (existsSync19(gitDir)) {
51917
52798
  stopGit = startGitSensor(gitDir, (ev) => {
51918
52799
  void handleGitEvent(ev).catch((err2) => {
51919
52800
  console.warn("[errata] git event handler failed:", err2);
@@ -51983,10 +52864,10 @@ function createWorkspaceEngine(opts) {
51983
52864
  });
51984
52865
  doneRender?.();
51985
52866
  writeContextFile(opts.workspaceRoot, body2);
51986
- const target = join22(opts.workspaceRoot, "AGENTS.md");
52867
+ const target = join24(opts.workspaceRoot, "AGENTS.md");
51987
52868
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
51988
52869
  if (elicit) {
51989
- writePrimingHandles(join22(paths.configDir, "priming-handles.json"), [
52870
+ writePrimingHandles(join24(paths.configDir, "priming-handles.json"), [
51990
52871
  ...snapshot.recentProblems.map((r) => r.node),
51991
52872
  // Resolved-band handles: the ✓ problem AND its Solution are citable
51992
52873
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -52077,6 +52958,21 @@ function createWorkspaceEngine(opts) {
52077
52958
  } catch (err2) {
52078
52959
  console.warn("[errata] anchor backfill failed:", err2);
52079
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
+ }
52080
52976
  const report = runNightlyPipeline(store);
52081
52977
  try {
52082
52978
  const m = mergeDuplicateProblems(store, { ts: Date.now() });
@@ -52176,8 +53072,9 @@ function createWorkspaceEngine(opts) {
52176
53072
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
52177
53073
  });
52178
53074
  };
52179
- const turnCursorPath = join22(paths.configDir, "turn-cursors.json");
53075
+ const turnCursorPath = join24(paths.configDir, "turn-cursors.json");
52180
53076
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
53077
+ const turnOffset = loadTurnOffsets(turnCursorPath);
52181
53078
  const sessionLastProblem = /* @__PURE__ */ new Map();
52182
53079
  const sessionThreads = /* @__PURE__ */ new Map();
52183
53080
  const harvestTexts = async (sessionId, items) => {
@@ -52199,7 +53096,7 @@ function createWorkspaceEngine(opts) {
52199
53096
  const t = Date.now();
52200
53097
  let processedTurns = 0;
52201
53098
  const elicit = isEdgeElicitationEnabled();
52202
- const handleMap = elicit ? readPrimingHandles(join22(paths.configDir, "priming-handles.json")) : {};
53099
+ const handleMap = elicit ? readPrimingHandles(join24(paths.configDir, "priming-handles.json")) : {};
52203
53100
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
52204
53101
  const toRel = (abs) => {
52205
53102
  const p = abs.replace(/\\/g, "/");
@@ -52241,7 +53138,7 @@ function createWorkspaceEngine(opts) {
52241
53138
  ts: t
52242
53139
  });
52243
53140
  if (r.created || r.corroborated) minted++;
52244
- sessionLastProblem.set(sessionId, designProblemId(flag.problem));
53141
+ if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52245
53142
  if (r.created || r.corroborated) {
52246
53143
  try {
52247
53144
  if (editedTurnFile) {
@@ -52321,11 +53218,13 @@ function createWorkspaceEngine(opts) {
52321
53218
  const dedupPath = anchorPath ?? hintPath;
52322
53219
  if (dedupPath) {
52323
53220
  try {
52324
- 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);
52325
53222
  if (dupId) {
52326
53223
  minted++;
52327
- sessionLastProblem.set(sessionId, dupId);
52328
- inScopeProblemId = dupId;
53224
+ if (p.kind !== "constraint") {
53225
+ sessionLastProblem.set(sessionId, dupId);
53226
+ inScopeProblemId = dupId;
53227
+ }
52329
53228
  if (p.threadId) threads.set(p.threadId, dupId);
52330
53229
  continue;
52331
53230
  }
@@ -52339,8 +53238,10 @@ function createWorkspaceEngine(opts) {
52339
53238
  });
52340
53239
  if (r.created || r.corroborated) {
52341
53240
  minted++;
52342
- sessionLastProblem.set(sessionId, designProblemId(p.statement));
52343
- inScopeProblemId = designProblemId(p.statement);
53241
+ if (p.kind !== "constraint") {
53242
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
53243
+ inScopeProblemId = designProblemId(p.statement);
53244
+ }
52344
53245
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
52345
53246
  try {
52346
53247
  if (anchorPath) {
@@ -52483,6 +53384,20 @@ function createWorkspaceEngine(opts) {
52483
53384
  if (domainId === pid) continue;
52484
53385
  mintCiteEdge(pid, domainId, "PERTAIN_TO", d.evidence === "witnessed" ? 0.4 : 0.3, { domainCite: true, evidence: d.evidence });
52485
53386
  }
53387
+ for (const p of plan.packages) {
53388
+ const pid = bindPid(p);
53389
+ if (!pid) continue;
53390
+ const pkgId = mintCitedPackageNode(store, p.packageText, t);
53391
+ if (pkgId === pid) continue;
53392
+ mintCiteEdge(pid, pkgId, "DEPENDS_ON", p.evidence === "witnessed" ? 0.4 : 0.3, { packageCite: true, evidence: p.evidence });
53393
+ }
53394
+ for (const cpt of plan.components) {
53395
+ const pid = bindPid(cpt);
53396
+ if (!pid) continue;
53397
+ const componentId = mintComponentNode(store, cpt.componentText, t);
53398
+ if (componentId === pid) continue;
53399
+ mintCiteEdge(pid, componentId, "CONCERNS", cpt.evidence === "witnessed" ? 0.4 : 0.3, { componentCite: true, evidence: cpt.evidence });
53400
+ }
52486
53401
  for (const inst of plan.instances) {
52487
53402
  const pid = bindPid(inst);
52488
53403
  if (!pid) continue;
@@ -52523,18 +53438,83 @@ function createWorkspaceEngine(opts) {
52523
53438
  mintCiteEdge(solId, targetId, sl.relation, 0.4, { solutionStructure: true, hypothesis: true });
52524
53439
  }
52525
53440
  }
52526
- if (plan.refutes.length > 0 && typeof cloud.reportContradictions === "function") {
52527
- void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.refutes }).then((r) => {
52528
- if (r?.recorded) console.log(`[errata] refute: ${r.recorded} contradiction(s) recorded`);
52529
- }).catch(
52530
- (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
+ })
52531
53503
  );
52532
53504
  }
52533
- if (plan.corroborations.length > 0 && typeof cloud.reportCorroborations === "function") {
52534
- void cloud.reportCorroborations({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.corroborations }).then((r) => {
52535
- if (r?.recorded) console.log(`[errata] corroborate: ${r.recorded} corroboration(s) recorded`);
52536
- }).catch(
52537
- (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
+ })
52538
53518
  );
52539
53519
  }
52540
53520
  } catch (err2) {
@@ -52563,17 +53543,28 @@ function createWorkspaceEngine(opts) {
52563
53543
  }
52564
53544
  };
52565
53545
  const harvestTurns = async (sessionId, transcriptPath) => {
53546
+ const known = turnOffset.get(sessionId);
52566
53547
  let turns;
53548
+ let nextOffset;
52567
53549
  try {
52568
- 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
+ }
52569
53556
  } catch {
52570
53557
  return;
52571
53558
  }
52572
53559
  const fresh = turnsSince(turns, lastTurnUuid.get(sessionId));
52573
- if (fresh.length === 0) return;
53560
+ turnOffset.set(sessionId, nextOffset);
53561
+ if (fresh.length === 0) {
53562
+ saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
53563
+ return;
53564
+ }
52574
53565
  lastTurnUuid.set(sessionId, turns[turns.length - 1].uuid);
52575
53566
  await harvestTexts(sessionId, fresh);
52576
- saveTurnCursors(turnCursorPath, lastTurnUuid);
53567
+ saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
52577
53568
  };
52578
53569
  const seenMessageIds = /* @__PURE__ */ new Set();
52579
53570
  const onMessage = (e) => {
@@ -52699,9 +53690,24 @@ function createWorkspaceEngine(opts) {
52699
53690
  return typeof cloudId === "string" && cloudId !== p.id ? [p.id, cloudId] : [p.id];
52700
53691
  });
52701
53692
  const pins = profile?.projectId ? await cloud.getSkillPins(profile.projectId).then((r) => r.pins).catch(() => []) : [];
52702
- 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);
52703
53707
  try {
52704
- 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));
52705
53711
  } catch (err2) {
52706
53712
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
52707
53713
  }
@@ -52888,7 +53894,7 @@ function createWorkspaceEngine(opts) {
52888
53894
  console.log(
52889
53895
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
52890
53896
  );
52891
- 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;
52892
53898
  return { uploaded: 0, failed: 0, remaining: pending };
52893
53899
  }
52894
53900
  try {
@@ -52975,7 +53981,7 @@ async function startDaemon(opts) {
52975
53981
  reviewUrl: () => webUiUrl + "/review"
52976
53982
  });
52977
53983
  const writeLockFile = (url2) => {
52978
- writeFileSync15(
53984
+ writeFileSync17(
52979
53985
  engine.paths.daemonLock,
52980
53986
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
52981
53987
  "utf8"
@@ -53018,7 +54024,7 @@ async function startDaemon(opts) {
53018
54024
  );
53019
54025
  await engine.stop();
53020
54026
  try {
53021
- if (existsSync19(engine.paths.daemonLock)) {
54027
+ if (existsSync20(engine.paths.daemonLock)) {
53022
54028
  }
53023
54029
  } catch {
53024
54030
  }
@@ -53035,16 +54041,16 @@ async function listenServer(fetchFn, port) {
53035
54041
 
53036
54042
  // src/registry.ts
53037
54043
  init_paths();
53038
- import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
53039
- 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";
53040
54046
  function registryPath() {
53041
- return process.env["ERRATA_REGISTRY_PATH"] ?? join23(globalDir(), "workspaces.json");
54047
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join25(globalDir(), "workspaces.json");
53042
54048
  }
53043
54049
  function read() {
53044
54050
  const p = registryPath();
53045
- if (!existsSync20(p)) return { version: 1, workspaces: {} };
54051
+ if (!existsSync21(p)) return { version: 1, workspaces: {} };
53046
54052
  try {
53047
- const parsed = JSON.parse(readFileSync18(p, "utf8"));
54053
+ const parsed = JSON.parse(readFileSync20(p, "utf8"));
53048
54054
  return { version: 1, workspaces: parsed.workspaces ?? {} };
53049
54055
  } catch {
53050
54056
  return { version: 1, workspaces: {} };
@@ -53052,7 +54058,7 @@ function read() {
53052
54058
  }
53053
54059
  function write(reg) {
53054
54060
  ensureDir(globalDir());
53055
- writeFileSync16(registryPath(), JSON.stringify(reg, null, 2), "utf8");
54061
+ writeFileSync18(registryPath(), JSON.stringify(reg, null, 2), "utf8");
53056
54062
  }
53057
54063
  function registerWorkspace(profile, root, now = Date.now()) {
53058
54064
  const reg = read();
@@ -53069,7 +54075,7 @@ function pruneMissingWorkspaces() {
53069
54075
  const reg = read();
53070
54076
  const removed = [];
53071
54077
  for (const [id, entry] of Object.entries(reg.workspaces)) {
53072
- if (!existsSync20(entry.path)) {
54078
+ if (!existsSync21(entry.path)) {
53073
54079
  removed.push(entry);
53074
54080
  delete reg.workspaces[id];
53075
54081
  }
@@ -53078,13 +54084,13 @@ function pruneMissingWorkspaces() {
53078
54084
  return removed;
53079
54085
  }
53080
54086
  function workspaceStatus(entry) {
53081
- const missing = !existsSync20(entry.path);
54087
+ const missing = !existsSync21(entry.path);
53082
54088
  const lockPath = workspacePaths(entry.path).daemonLock;
53083
54089
  let running = false;
53084
54090
  let webUiUrl = null;
53085
- if (existsSync20(lockPath)) {
54091
+ if (existsSync21(lockPath)) {
53086
54092
  try {
53087
- const lock = JSON.parse(readFileSync18(lockPath, "utf8"));
54093
+ const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
53088
54094
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
53089
54095
  running = true;
53090
54096
  webUiUrl = lock.webUiUrl;
@@ -53112,7 +54118,7 @@ function pidAlive(pid) {
53112
54118
  // src/multi.ts
53113
54119
  init_dist();
53114
54120
  init_src4();
53115
- 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";
53116
54122
 
53117
54123
  // src/principle-sync.ts
53118
54124
  init_src4();
@@ -53140,8 +54146,8 @@ init_reconcile();
53140
54146
 
53141
54147
  // src/lockfile-auto.ts
53142
54148
  init_src();
53143
- import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
53144
- 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";
53145
54151
 
53146
54152
  // src/package-index.ts
53147
54153
  init_src();
@@ -53227,10 +54233,12 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53227
54233
  const ignored = (s) => ignorePatterns.length > 0 && ignorePatterns.some((p) => s.toLowerCase().includes(p));
53228
54234
  const nodes = [];
53229
54235
  const seen = /* @__PURE__ */ new Set();
54236
+ const localIdByWireId = {};
53230
54237
  for (const label of labels) {
53231
54238
  for (const n of store.findNodesByLabel(label)) {
53232
54239
  if (n.attrs["source"] === "cloud") continue;
53233
54240
  if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
54241
+ if (typeof n.attrs["contributedAtSeq"] === "number") continue;
53234
54242
  nodes.push({
53235
54243
  ...n,
53236
54244
  // Language wire id = the shared `languageCanonicalId` (warming-spine
@@ -53238,15 +54246,24 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53238
54246
  // Package ids are already the purl (= the cross-stratum canonicalId).
53239
54247
  id: label === "Language" ? languageCanonicalId(String(n.attrs["name"] ?? "").trim() || n.description) : n.id,
53240
54248
  embedding: [],
54249
+ // No `version` attr on the wire: the purl already encodes the resolved
54250
+ // version, and the door's temporal guard rejects ANY `attrs.version` as
54251
+ // bi-temporal bookkeeping (ingest-temporal-guard.ts) — shipping it 422s
54252
+ // the whole batch. `resolved` still travels (range-vs-lockfile signal).
53241
54253
  attrs: label === "Package" ? {
53242
54254
  purl: n.attrs["purl"],
53243
54255
  name: n.attrs["name"],
53244
- version: n.attrs["version"],
53245
54256
  ecosystem: n.attrs["ecosystem"],
53246
54257
  resolved: n.attrs["resolved"]
53247
- } : { name: n.attrs["name"] }
54258
+ } : { name: n.attrs["name"] },
54259
+ // OM-anchor-tag: context stubs are self-anchored — the wire id IS the
54260
+ // spine key (purl / languageCanonicalId). The door re-confirms on the
54261
+ // public spine; a private-registry or workspace package never confirms
54262
+ // and fails closed to org, exactly as an untagged one would.
54263
+ anchorVisibility: "public"
53248
54264
  });
53249
54265
  seen.add(n.id);
54266
+ localIdByWireId[nodes[nodes.length - 1].id] = n.id;
53250
54267
  }
53251
54268
  }
53252
54269
  if (nodes.length === 0) return null;
@@ -53258,11 +54275,15 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53258
54275
  }
53259
54276
  const base = {
53260
54277
  daemonVersion,
54278
+ // Origin key (= project, the salted `wp_…`) — the door stamps it onto created
54279
+ // nodes as `authoringProject`, arming the evidence channels' cross-origin gate
54280
+ // (EE-corrob-live: without it every node is fail-open to self-corroboration).
54281
+ ...profile.id ? { originProject: profile.id } : {},
53261
54282
  profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
53262
54283
  nodes,
53263
54284
  edges
53264
54285
  };
53265
- return { ...base, payloadDigest: digest(base) };
54286
+ return { ...base, payloadDigest: digest(base), localIds: [...seen], localIdByWireId };
53266
54287
  }
53267
54288
 
53268
54289
  // src/lockfile-auto.ts
@@ -53275,11 +54296,11 @@ function runLockfilePass(opts) {
53275
54296
  { file: "package-lock.json", parse: parsePackageLockJson }
53276
54297
  ];
53277
54298
  for (const c of candidates) {
53278
- const p = join24(opts.root, c.file);
53279
- if (!existsSync21(p)) continue;
54299
+ const p = join26(opts.root, c.file);
54300
+ if (!existsSync22(p)) continue;
53280
54301
  let sbom;
53281
54302
  try {
53282
- sbom = c.parse(readFileSync19(p, "utf8"));
54303
+ sbom = c.parse(readFileSync21(p, "utf8"));
53283
54304
  } catch {
53284
54305
  continue;
53285
54306
  }
@@ -53294,6 +54315,7 @@ function runLockfilePass(opts) {
53294
54315
  // src/instance-ingest.ts
53295
54316
  init_src();
53296
54317
  init_src2();
54318
+ init_src4();
53297
54319
  init_src8();
53298
54320
  init_src();
53299
54321
 
@@ -53337,13 +54359,17 @@ function noveltyAgainst(node2, neighbors, opts = {}) {
53337
54359
  // src/instance-ingest.ts
53338
54360
  var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
53339
54361
  var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
53340
- var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO"];
53341
- var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain"];
54362
+ var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
54363
+ var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
53342
54364
  function stripCodebaseScope(scope) {
53343
54365
  const s = { ...scope ?? {} };
53344
54366
  delete s["codebase"];
53345
54367
  return s;
53346
54368
  }
54369
+ function contributedWireId(n) {
54370
+ const cloudId = n.attrs["cloudNodeId"];
54371
+ return isMembraneSaltedId(cloudId) ? cloudId : n.id;
54372
+ }
53347
54373
  function wireContextId(n) {
53348
54374
  if (n.label === "Language" && n.attrs["source"] !== "cloud") {
53349
54375
  const name2 = String(n.attrs["name"] ?? "").trim() || n.description.trim() || n.id.replace(/^lang:/, "");
@@ -53354,24 +54380,45 @@ function wireContextId(n) {
53354
54380
  }
53355
54381
  return n.id;
53356
54382
  }
54383
+ function langAnchor(t) {
54384
+ const name2 = String(t.attrs["name"] ?? "").trim() || t.description.trim() || t.id.replace(/^lang:/, "").trim();
54385
+ return name2 ? `lang:${name2.toLowerCase()}` : void 0;
54386
+ }
53357
54387
  function shareableContext(n, wireId) {
53358
54388
  const attrs = n.label === "Package" ? {
54389
+ // No `version` attr: the purl encodes it, and the door's temporal
54390
+ // guard 422s any `attrs.version` (mirrors buildContextIngest).
53359
54391
  purl: n.attrs["purl"],
53360
54392
  name: n.attrs["name"],
53361
- version: n.attrs["version"],
53362
54393
  ecosystem: n.attrs["ecosystem"],
53363
54394
  resolved: n.attrs["resolved"]
53364
54395
  } : n.label === "Domain" ? { name: n.description, canonicalId: n.attrs["canonicalId"] } : { name: n.attrs["name"] };
53365
- return { ...n, id: wireId, embedding: [], attrs };
54396
+ return {
54397
+ ...n,
54398
+ id: wireId,
54399
+ embedding: [],
54400
+ attrs,
54401
+ ...n.label === "Package" ? { anchorVisibility: "public" } : {},
54402
+ ...n.label === "Language" && langAnchor(n) ? { anchorVisibility: "public", anchor: langAnchor(n) } : {},
54403
+ // A Component stub claims itself by slug (OM-agent-anchors) — the door
54404
+ // confirms only against an EXISTING public Component of that slug, so an
54405
+ // org-internal component name never crosses (fails closed to org).
54406
+ ...n.label === "Component" ? { anchorVisibility: "public", anchor: `component:${wireId}` } : {}
54407
+ };
53366
54408
  }
53367
54409
  function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [], opts = {}) {
53368
54410
  const level = opts.level ?? 1;
53369
54411
  const noveltyOpts = opts.dedupCosine != null ? { dedupCosine: opts.dedupCosine } : {};
53370
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
+ };
53371
54417
  const shareable = (n) => ({
53372
54418
  ...n,
53373
54419
  description: generalize(n.description, { level }).text,
53374
54420
  embedding: [],
54421
+ ...originSessionOf(n),
53375
54422
  attrs: {
53376
54423
  scope: stripCodebaseScope(n.attrs["scope"]),
53377
54424
  ...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
@@ -53380,7 +54427,9 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53380
54427
  });
53381
54428
  const nodes = [];
53382
54429
  const seen = /* @__PURE__ */ new Set();
54430
+ const shippedById = /* @__PURE__ */ new Map();
53383
54431
  const anchorSources = /* @__PURE__ */ new Set();
54432
+ const semanticEndpoints = /* @__PURE__ */ new Map();
53384
54433
  const includedByLabel = /* @__PURE__ */ new Map();
53385
54434
  for (const label of INSTANCE_LABELS) {
53386
54435
  const ref = [];
@@ -53393,13 +54442,24 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53393
54442
  const contributedAtSeq = n.attrs["contributedAtSeq"];
53394
54443
  if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
53395
54444
  anchorSources.add(n.id);
54445
+ semanticEndpoints.set(n.id, contributedWireId(n));
53396
54446
  continue;
53397
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
+ }
53398
54455
  if (!noveltyAgainst(n, ref, noveltyOpts).ready) continue;
53399
54456
  ref.push(n);
53400
- nodes.push(shareable(n));
54457
+ const wireNode = shareable(n);
54458
+ nodes.push(wireNode);
54459
+ shippedById.set(n.id, wireNode);
53401
54460
  seen.add(n.id);
53402
54461
  anchorSources.add(n.id);
54462
+ semanticEndpoints.set(n.id, n.id);
53403
54463
  }
53404
54464
  }
53405
54465
  for (const n of nodes) {
@@ -53419,27 +54479,66 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53419
54479
  if (e.type === "OCCURS_IN" && t.label !== "Language") continue;
53420
54480
  if (e.type === "DEPENDS_ON" && t.label !== "Package") continue;
53421
54481
  if (e.type === "PERTAIN_TO" && t.label !== "Domain") continue;
53422
- if (t.label === "Package" && opts.includePackages !== true) continue;
54482
+ if (e.type === "CONCERNS" && t.label !== "Component") continue;
54483
+ if ((t.label === "Package" || t.label === "Component") && opts.includePackages !== true) continue;
53423
54484
  if (ignored(t.description) || ignored(String(t.attrs["name"] ?? ""))) continue;
53424
54485
  targets.push({ e, t, wireId: wireContextId(t) });
53425
54486
  }
53426
54487
  if (targets.length === 0) continue;
54488
+ const shipped = shippedById.get(sourceId);
54489
+ if (shipped) {
54490
+ const anchors = (label) => targets.filter((x) => x.t.label === label).map(
54491
+ (x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
54492
+ ).filter((a) => a != null).sort();
54493
+ const anchor = anchors("Package")[0] ?? anchors("Component")[0] ?? anchors("Language")[0];
54494
+ if (anchor) {
54495
+ shipped.anchorVisibility = "public";
54496
+ shipped.anchor = anchor;
54497
+ }
54498
+ }
53427
54499
  const dg = digest(targets.map(({ e, wireId }) => `${e.type}>${wireId}`).sort());
53428
- if (store.getNode(sourceId)?.attrs["anchorsContributedDigest"] === dg) continue;
54500
+ const sourceNode = store.getNode(sourceId);
54501
+ if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
53429
54502
  anchorDigests[sourceId] = dg;
54503
+ const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
53430
54504
  for (const { e, t, wireId } of targets) {
53431
- anchorEdges.push({ ...e, to: wireId, attrs: {} });
54505
+ anchorEdges.push({ ...e, from: wireFrom, to: wireId, attrs: {} });
53432
54506
  if (t.attrs["source"] === "cloud" || contextSeen.has(wireId)) continue;
53433
54507
  contextSeen.add(wireId);
53434
54508
  contextNodes.push(shareableContext(t, wireId));
53435
54509
  }
53436
54510
  }
54511
+ const primaryLang = (profile.languages ?? []).map((l) => String(l).trim().toLowerCase()).filter(Boolean)[0];
54512
+ if (primaryLang) {
54513
+ for (const shipped of shippedById.values()) {
54514
+ if (!shipped.anchorVisibility) {
54515
+ shipped.anchorVisibility = "public";
54516
+ shipped.anchor = `lang:${primaryLang}`;
54517
+ }
54518
+ }
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
+ }
53437
54535
  const project = opts.project;
53438
54536
  if (project) {
53439
54537
  const now = Date.now();
53440
54538
  for (const sourceId of anchorSources) {
53441
54539
  const source = store.getNode(sourceId);
53442
54540
  if (!source) continue;
54541
+ const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
53443
54542
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
53444
54543
  const target = store.getNode(e.to);
53445
54544
  if (!target) continue;
@@ -53474,11 +54573,21 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53474
54573
  stability: "unstable"
53475
54574
  });
53476
54575
  }
53477
- anchorEdges.push({ ...e, to: symId, attrs: {} });
54576
+ anchorEdges.push({ ...e, from: wireFrom, to: symId, attrs: {} });
53478
54577
  }
53479
54578
  }
53480
54579
  }
53481
- 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;
53482
54591
  let symbolSummaries;
53483
54592
  if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
53484
54593
  const lexicon = opts.lexicon;
@@ -53500,15 +54609,14 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53500
54609
  }
53501
54610
  if (count > 0) symbolSummaries = sidecar;
53502
54611
  }
53503
- const edges = [];
53504
- for (const id of seen) {
53505
- for (const e of store.outEdges(id, [...INSTANCE_EDGES])) {
53506
- if (seen.has(e.to)) edges.push({ ...e, attrs: {} });
53507
- }
53508
- }
53509
54612
  edges.push(...anchorEdges);
53510
54613
  const base = {
53511
54614
  daemonVersion,
54615
+ // Origin key (= project, the salted `wp_…`) — the door stamps it onto created
54616
+ // nodes as `authoringProject`, arming the evidence channels' cross-origin gate
54617
+ // (EE-corrob-live: this is the MAIN semantic drain; without the stamp every
54618
+ // Problem/Solution lands fail-open to self-corroboration).
54619
+ ...profile.id ? { originProject: profile.id } : {},
53512
54620
  profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
53513
54621
  // Context nodes FIRST: a chunked drain (cloud-client, 25-node chunks) then
53514
54622
  // co-locates the few Language/Package stubs with the first instance chunk,
@@ -53519,7 +54627,47 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53519
54627
  // payloadDigest — byte-identical to the pre-sidecar batch (backward compat).
53520
54628
  ...symbolSummaries ? { symbolSummaries } : {}
53521
54629
  };
53522
- return { ...base, payloadDigest: digest(base), anchorDigests };
54630
+ return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests };
54631
+ }
54632
+
54633
+ // src/backfill-edges.ts
54634
+ init_src();
54635
+ init_src2();
54636
+ var CAUSAL_LABELS = ["Problem", "Solution", "RootCause"];
54637
+ var CAUSAL_EDGES2 = ["SOLVED_BY", "CAUSED_BY", "FIXED_BY"];
54638
+ function cloudWireId(store, id) {
54639
+ const n = store.getNode(id);
54640
+ if (!n || !CAUSAL_LABELS.includes(n.label)) return null;
54641
+ if (n.label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) return null;
54642
+ const cloudNodeId = n.attrs["cloudNodeId"];
54643
+ if (typeof cloudNodeId === "string" && cloudNodeId.length > 0) return cloudNodeId;
54644
+ if (n.attrs["source"] === "cloud") return n.id;
54645
+ return typeof n.attrs["contributedAtSeq"] === "number" ? n.id : null;
54646
+ }
54647
+ function buildCausalEdgeBackfill(store, profile, daemonVersion) {
54648
+ const edges = [];
54649
+ const seenEdge = /* @__PURE__ */ new Set();
54650
+ for (const label of CAUSAL_LABELS) {
54651
+ for (const n of store.findNodesByLabel(label)) {
54652
+ for (const e of store.outEdges(n.id, [...CAUSAL_EDGES2])) {
54653
+ if (seenEdge.has(e.id)) continue;
54654
+ seenEdge.add(e.id);
54655
+ const from = cloudWireId(store, e.from);
54656
+ const to = cloudWireId(store, e.to);
54657
+ if (!from || !to) continue;
54658
+ edges.push({ ...e, from, to, attrs: {} });
54659
+ }
54660
+ }
54661
+ }
54662
+ if (edges.length === 0) return null;
54663
+ const base = {
54664
+ daemonVersion,
54665
+ profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
54666
+ originProject: profile.id,
54667
+ nodes: [],
54668
+ edges
54669
+ };
54670
+ return { ...base, payloadDigest: digest(base) };
53523
54671
  }
53524
54672
 
53525
54673
  // src/multi.ts
@@ -53600,7 +54748,7 @@ var ConsolidateWorker = class {
53600
54748
  init_paths();
53601
54749
 
53602
54750
  // src/lock.ts
53603
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "node:fs";
54751
+ import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
53604
54752
  function isProcessAlive(pid) {
53605
54753
  if (!pid || pid <= 0) return false;
53606
54754
  try {
@@ -53611,9 +54759,9 @@ function isProcessAlive(pid) {
53611
54759
  }
53612
54760
  }
53613
54761
  function readDaemonLock(lockPath) {
53614
- if (!existsSync22(lockPath)) return null;
54762
+ if (!existsSync23(lockPath)) return null;
53615
54763
  try {
53616
- const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
54764
+ const lock = JSON.parse(readFileSync22(lockPath, "utf8"));
53617
54765
  return typeof lock.pid === "number" ? lock : null;
53618
54766
  } catch {
53619
54767
  return null;
@@ -53855,13 +55003,13 @@ async function reanchorProject(opts) {
53855
55003
  }
53856
55004
 
53857
55005
  // src/adopt.ts
53858
- import { existsSync as existsSync23 } from "node:fs";
53859
- 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";
53860
55008
  function findGitRoot(absPath) {
53861
55009
  let dir = absPath;
53862
55010
  for (let depth = 0; depth < 64; depth++) {
53863
- if (existsSync23(join25(dir, ".git"))) return dir;
53864
- const parent = dirname9(dir);
55011
+ if (existsSync24(join27(dir, ".git"))) return dir;
55012
+ const parent = dirname10(dir);
53865
55013
  if (parent === dir) return null;
53866
55014
  dir = parent;
53867
55015
  }
@@ -53969,6 +55117,18 @@ async function startMultiDaemon(opts = {}) {
53969
55117
  startLoopLagMonitor();
53970
55118
  let baseUrl = "";
53971
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
+ };
53972
55132
  const updatePoller = opts.updateCheck ? startUpdatePoller({
53973
55133
  channel: loadConfig().updateChannel,
53974
55134
  onChange: (pending) => {
@@ -53981,6 +55141,47 @@ async function startMultiDaemon(opts = {}) {
53981
55141
  let serverPending = null;
53982
55142
  const boundaryListeners = [];
53983
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
+ };
53984
55185
  const fireBoundary = () => {
53985
55186
  for (const l of boundaryListeners) {
53986
55187
  try {
@@ -54056,7 +55257,7 @@ async function startMultiDaemon(opts = {}) {
54056
55257
  void ambientLinkAll();
54057
55258
  app.route(`/ws/${rec.id}`, rec.webApp);
54058
55259
  try {
54059
- writeFileSync17(
55260
+ writeFileSync19(
54060
55261
  rec.engine.paths.daemonLock,
54061
55262
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
54062
55263
  "utf8"
@@ -54095,8 +55296,10 @@ async function startMultiDaemon(opts = {}) {
54095
55296
  path: r.root,
54096
55297
  stack: r.entry.stack,
54097
55298
  nodes: r.engine.store.nodeCount(),
54098
- problems: r.engine.store.findNodesByLabel("Problem").length,
54099
- solutions: r.engine.store.findNodesByLabel("Solution").length,
55299
+ // COUNT(*) — the materializing findNodesByLabel(...).length form held
55300
+ // the event loop for seconds per poll once stores grew (HZ-index-hydrate).
55301
+ problems: r.engine.store.countNodesByLabel("Problem"),
55302
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54100
55303
  endpoints: `/ws/${r.id}/`
54101
55304
  })),
54102
55305
  humanView: "run `errata report` \u2014 the dashboard was retired (GRAFT 4e)"
@@ -54152,8 +55355,9 @@ async function startMultiDaemon(opts = {}) {
54152
55355
  name: r.entry.name,
54153
55356
  path: r.root,
54154
55357
  nodes: r.engine.store.nodeCount(),
54155
- problems: r.engine.store.findNodesByLabel("Problem").length,
54156
- solutions: r.engine.store.findNodesByLabel("Solution").length,
55358
+ // COUNT(*) — same hydration hazard as `/` (HZ-index-hydrate).
55359
+ problems: r.engine.store.countNodesByLabel("Problem"),
55360
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54157
55361
  stranded: strandedCount(r)
54158
55362
  }))
54159
55363
  })
@@ -54242,7 +55446,7 @@ async function startMultiDaemon(opts = {}) {
54242
55446
  baseUrl = `http://127.0.0.1:${port}`;
54243
55447
  try {
54244
55448
  ensureDir(globalDir());
54245
- writeFileSync17(
55449
+ writeFileSync19(
54246
55450
  lockPath,
54247
55451
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
54248
55452
  "utf8"
@@ -54251,7 +55455,7 @@ async function startMultiDaemon(opts = {}) {
54251
55455
  }
54252
55456
  for (const r of records) {
54253
55457
  try {
54254
- writeFileSync17(
55458
+ writeFileSync19(
54255
55459
  r.engine.paths.daemonLock,
54256
55460
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
54257
55461
  "utf8"
@@ -54514,15 +55718,33 @@ async function startMultiDaemon(opts = {}) {
54514
55718
  async syncPrinciplesPublic() {
54515
55719
  if (!loadConfig().consent.sync) return { uploaded: 0, skipped: "consent-off" };
54516
55720
  const ignore = loadClaimIgnorePatterns(globalDir());
54517
- const payload = buildPrincipleIngest(sharedStore, DAEMON_VERSION, ignore);
55721
+ const primaryLanguage = machineDominantLanguage();
55722
+ const payload = buildPrincipleIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
55723
+ ...primaryLanguage ? { primaryLanguage } : {}
55724
+ });
54518
55725
  if (!payload) return { uploaded: 0 };
54519
55726
  const res = await cloudNow().ingest(payload);
54520
55727
  return { uploaded: res.accepted };
54521
55728
  },
54522
55729
  async syncTriagePublic() {
54523
- 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" };
54524
55732
  const ignore = loadClaimIgnorePatterns(globalDir());
54525
- const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore);
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);
55742
+ const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
55743
+ ...primaryLanguage ? { primaryLanguage } : {},
55744
+ tokenAnchors,
55745
+ // Scoped bucket keys carry package names — same consent as the purls.
55746
+ includeScopedBuckets: cfg2.consent.contributePackages
55747
+ });
54526
55748
  if (!payload) return { uploaded: 0 };
54527
55749
  const res = await cloudNow().ingest(payload);
54528
55750
  return { uploaded: res.accepted };
@@ -54533,14 +55755,44 @@ async function startMultiDaemon(opts = {}) {
54533
55755
  const ignore = loadClaimIgnorePatterns(globalDir());
54534
55756
  const client = cloudNow();
54535
55757
  let uploaded = 0;
55758
+ const errors = [];
55759
+ const lane = async (name2, projectName, run3) => {
55760
+ try {
55761
+ await run3();
55762
+ } catch (err2) {
55763
+ const msg = `[${projectName}] ${name2}: ${err2 instanceof Error ? err2.message : String(err2)}`;
55764
+ errors.push(msg);
55765
+ console.error(`[sync\u2192cloud] ${msg}`);
55766
+ }
55767
+ };
54536
55768
  for (const r of records) {
54537
55769
  const store = r.engine.store;
55770
+ const projectName = r.engine.profile.name ?? r.engine.profile.id;
54538
55771
  const context = buildContextIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
54539
55772
  includePackages: cfg2.consent.contributePackages
54540
55773
  });
54541
55774
  if (context) {
54542
- const res = await client.ingest(context);
54543
- uploaded += res.accepted;
55775
+ await lane("context", projectName, async () => {
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
+ });
55793
+ uploaded += res.accepted;
55794
+ return res.accepted;
55795
+ });
54544
55796
  }
54545
55797
  let lexicon;
54546
55798
  try {
@@ -54563,34 +55815,78 @@ async function startMultiDaemon(opts = {}) {
54563
55815
  });
54564
55816
  if (instances) {
54565
55817
  if (project) instances.projectId = project.projectId;
54566
- const res = await client.ingest(instances);
54567
- uploaded += res.accepted;
54568
- const seq = store.currentIngestSeq();
54569
- const cloudIdByLocal = new Map(
54570
- res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
54571
- );
54572
- for (const n of instances.nodes) {
54573
- const local = store.getNode(n.id);
54574
- if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
54575
- const cloudNodeId = cloudIdByLocal.get(n.id);
54576
- store.updateNode(n.id, {
54577
- attrs: {
54578
- ...local.attrs,
54579
- contributedAtSeq: seq,
54580
- ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
55818
+ await lane("instances", projectName, async () => {
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}`);
54581
55824
  }
54582
55825
  });
54583
- }
54584
- for (const [localId, dg] of Object.entries(instances.anchorDigests)) {
54585
- const local = store.getNode(localId);
54586
- if (!local) continue;
54587
- store.updateNode(localId, {
54588
- attrs: { ...local.attrs, anchorsContributedDigest: dg }
54589
- });
54590
- }
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
+ }
55831
+ uploaded += res.accepted;
55832
+ const seq = store.currentIngestSeq();
55833
+ const cloudIdByLocal = new Map(
55834
+ res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
55835
+ );
55836
+ for (const n of instances.nodes) {
55837
+ const local = store.getNode(n.id);
55838
+ if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
55839
+ if (blocked.has(n.id)) continue;
55840
+ const cloudNodeId = cloudIdByLocal.get(n.id);
55841
+ store.updateNode(n.id, {
55842
+ attrs: {
55843
+ ...local.attrs,
55844
+ contributedAtSeq: seq,
55845
+ ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
55846
+ }
55847
+ });
55848
+ }
55849
+ for (const [localId, dg] of Object.entries(instances.anchorDigests)) {
55850
+ const local = store.getNode(localId);
55851
+ if (!local) continue;
55852
+ store.updateNode(localId, {
55853
+ attrs: { ...local.attrs, anchorsContributedDigest: dg }
55854
+ });
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;
55864
+ });
54591
55865
  }
54592
55866
  }
54593
- return { uploaded };
55867
+ return { uploaded, ...errors.length > 0 ? { errors } : {} };
55868
+ },
55869
+ async backfillCausalEdges() {
55870
+ if (!loadConfig().consent.sync) return { accepted: 0, rejected: 0, skipped: "consent-off" };
55871
+ const client = cloudNow();
55872
+ let accepted = 0;
55873
+ let rejected = 0;
55874
+ const errors = [];
55875
+ for (const r of records) {
55876
+ const payload = buildCausalEdgeBackfill(r.engine.store, r.engine.profile, DAEMON_VERSION);
55877
+ if (!payload) continue;
55878
+ try {
55879
+ const res = await client.ingest(payload);
55880
+ accepted += res.accepted;
55881
+ rejected += res.rejected;
55882
+ if (res.rejected > 0) errors.push(...res.violations.slice(0, 5));
55883
+ } catch (err2) {
55884
+ errors.push(
55885
+ `[${r.engine.profile.name ?? r.engine.profile.id}] backfill: ${err2 instanceof Error ? err2.message : String(err2)}`
55886
+ );
55887
+ }
55888
+ }
55889
+ return { accepted, rejected, ...errors.length > 0 ? { errors } : {} };
54594
55890
  },
54595
55891
  async pullTriagePublic() {
54596
55892
  if (!loadConfig().consent.sync) return { merged: 0, skipped: "consent-off" };
@@ -54633,16 +55929,23 @@ async function startMultiDaemon(opts = {}) {
54633
55929
  },
54634
55930
  async sessionBoundaryFlush() {
54635
55931
  if (boundaryFlushing) return { uploaded: 0, written: 0, pruned: 0, skipped: "in-flight" };
54636
- boundaryFlushing = true;
55932
+ beginFlush("embed");
54637
55933
  try {
54638
55934
  for (const r of records) {
54639
55935
  await r.engine.embedSettled();
54640
55936
  }
55937
+ flushStage("instances");
54641
55938
  const inst = await daemon.syncInstancesPublic();
55939
+ flushStage("skills");
54642
55940
  const skills = await daemon.syncSkillsAll();
54643
- return { uploaded: inst.uploaded, written: skills.written, pruned: skills.pruned };
55941
+ return {
55942
+ uploaded: inst.uploaded,
55943
+ written: skills.written,
55944
+ pruned: skills.pruned,
55945
+ ...inst.errors ? { errors: inst.errors } : {}
55946
+ };
54644
55947
  } finally {
54645
- boundaryFlushing = false;
55948
+ endFlush();
54646
55949
  }
54647
55950
  },
54648
55951
  onSessionBoundary(cb) {
@@ -54650,7 +55953,7 @@ async function startMultiDaemon(opts = {}) {
54650
55953
  },
54651
55954
  async stop() {
54652
55955
  try {
54653
- const cur = readFileSync21(lockPath, "utf8");
55956
+ const cur = readFileSync23(lockPath, "utf8");
54654
55957
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
54655
55958
  } catch {
54656
55959
  }
@@ -54667,15 +55970,24 @@ async function startMultiDaemon(opts = {}) {
54667
55970
  }
54668
55971
  };
54669
55972
  app.post("/api/sync", async (c) => {
55973
+ if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
55974
+ beginFlush("principles");
54670
55975
  try {
54671
55976
  const principles = await daemon.syncPrinciplesPublic();
55977
+ flushStage("triage");
54672
55978
  const triage = await daemon.syncTriagePublic();
55979
+ flushStage("instances");
54673
55980
  const instances = await daemon.syncInstancesPublic();
54674
- return c.json({ principles, triage, instances });
55981
+ flushStage("edge-backfill");
55982
+ const edgeBackfill = await daemon.backfillCausalEdges();
55983
+ return c.json({ principles, triage, instances, edgeBackfill });
54675
55984
  } catch (err2) {
54676
55985
  return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 500);
55986
+ } finally {
55987
+ endFlush();
54677
55988
  }
54678
55989
  });
55990
+ app.get("/api/sync", (c) => c.json(flushSnapshot()));
54679
55991
  app.post("/api/tick", async (c) => {
54680
55992
  try {
54681
55993
  return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
@@ -55658,21 +56970,21 @@ async function cmdInit() {
55658
56970
  if (!skipHooks) {
55659
56971
  console.log("");
55660
56972
  console.log("installing harness hooks...");
55661
- const { existsSync: existsSync25 } = await import("node:fs");
55662
- const { join: join27 } = await import("node:path");
56973
+ const { existsSync: existsSync26 } = await import("node:fs");
56974
+ const { join: join29 } = await import("node:path");
55663
56975
  try {
55664
56976
  await installClaudeHooks(port);
55665
56977
  } catch (err2) {
55666
56978
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
55667
56979
  }
55668
- if (existsSync25(join27(ROOT, ".cursor"))) {
56980
+ if (existsSync26(join29(ROOT, ".cursor"))) {
55669
56981
  try {
55670
56982
  await installCursorMcpConfig();
55671
56983
  } catch (err2) {
55672
56984
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
55673
56985
  }
55674
56986
  }
55675
- if (existsSync25(join27(ROOT, ".codex"))) {
56987
+ if (existsSync26(join29(ROOT, ".codex"))) {
55676
56988
  try {
55677
56989
  await installCodexHooks(port);
55678
56990
  } catch (err2) {
@@ -55829,8 +57141,30 @@ async function cmdStatus() {
55829
57141
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
55830
57142
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
55831
57143
  }
55832
- console.log(` graph db: ${existsSync24(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
55833
- 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
+ }
55834
57168
  const lockPath = globalDaemonLock();
55835
57169
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
55836
57170
  console.log(
@@ -56460,11 +57794,11 @@ function cmdInstallationProfile(args2) {
56460
57794
  }
56461
57795
  async function cmdReview() {
56462
57796
  const paths = workspacePaths(ROOT);
56463
- if (!existsSync24(paths.reviewQueue)) {
57797
+ if (!existsSync25(paths.reviewQueue)) {
56464
57798
  console.log("(review queue empty)");
56465
57799
  return;
56466
57800
  }
56467
- const queue = JSON.parse(readFileSync22(paths.reviewQueue, "utf8"));
57801
+ const queue = JSON.parse(readFileSync24(paths.reviewQueue, "utf8"));
56468
57802
  if (queue.length === 0) {
56469
57803
  console.log("(review queue empty)");
56470
57804
  return;
@@ -56883,8 +58217,8 @@ async function cmdSimilar(args2) {
56883
58217
  const limitIdx = args2.indexOf("--limit");
56884
58218
  const limit = limitIdx >= 0 && args2[limitIdx + 1] ? Number(args2[limitIdx + 1]) : 5;
56885
58219
  const { runTool: runTool2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
56886
- await withStore((store) => {
56887
- 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);
56888
58222
  if (!r.found) {
56889
58223
  console.log("seed not found");
56890
58224
  return;
@@ -57135,7 +58469,7 @@ async function gatherRepo(store, ws) {
57135
58469
  };
57136
58470
  }
57137
58471
  async function gatherReportData(generatedAt) {
57138
- const { existsSync: existsSync25 } = await import("node:fs");
58472
+ const { existsSync: existsSync26 } = await import("node:fs");
57139
58473
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57140
58474
  const cfg = loadConfig();
57141
58475
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -57143,7 +58477,7 @@ async function gatherReportData(generatedAt) {
57143
58477
  for (const ws of listWorkspaces()) {
57144
58478
  if (ws.missing) continue;
57145
58479
  const dbPath = workspacePaths(ws.path).castalia;
57146
- if (!existsSync25(dbPath)) continue;
58480
+ if (!existsSync26(dbPath)) continue;
57147
58481
  let store = null;
57148
58482
  try {
57149
58483
  store = openGraphStore2({ path: dbPath });
@@ -57174,7 +58508,7 @@ async function gatherReportData(generatedAt) {
57174
58508
  };
57175
58509
  }
57176
58510
  async function cmdReport(args2) {
57177
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync18 } = await import("node:fs");
58511
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync20 } = await import("node:fs");
57178
58512
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
57179
58513
  const includeFutureVerbs = args2.includes("--future-verbs");
57180
58514
  const now = /* @__PURE__ */ new Date();
@@ -57187,8 +58521,8 @@ async function cmdReport(args2) {
57187
58521
  const outDir = workspacePaths(ROOT).configDir;
57188
58522
  mkdirSync8(outDir, { recursive: true });
57189
58523
  const files = renderReport2(data, { includeFutureVerbs });
57190
- for (const f of files) writeFileSync18(join26(outDir, f.name), f.html, "utf8");
57191
- 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");
57192
58526
  console.log(`report \u2192 ${indexPath}`);
57193
58527
  console.log(
57194
58528
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -57240,7 +58574,7 @@ function spawnDaemonDetached() {
57240
58574
  try {
57241
58575
  const logPath = daemonLogPath();
57242
58576
  try {
57243
- if (statSync6(logPath).size > 5 * 1024 * 1024) renameSync3(logPath, `${logPath}.1`);
58577
+ if (statSync6(logPath).size > 5 * 1024 * 1024) renameSync4(logPath, `${logPath}.1`);
57244
58578
  } catch {
57245
58579
  }
57246
58580
  out2 = openSync2(logPath, "a");
@@ -57306,15 +58640,15 @@ function hookRelayCommand(port, path2) {
57306
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 '{}'`;
57307
58641
  }
57308
58642
  async function installClaudeHooks(port) {
57309
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57310
- const { join: join27 } = await import("node:path");
57311
- const dir = join27(ROOT, ".claude");
57312
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57313
- 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");
57314
58648
  let settings = {};
57315
- if (existsSync25(file2)) {
58649
+ if (existsSync26(file2)) {
57316
58650
  try {
57317
- settings = JSON.parse(readFileSync23(file2, "utf8"));
58651
+ settings = JSON.parse(readFileSync25(file2, "utf8"));
57318
58652
  } catch {
57319
58653
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57320
58654
  process.exit(2);
@@ -57360,10 +58694,10 @@ async function installClaudeHooks(port) {
57360
58694
  dropErrata(list);
57361
58695
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
57362
58696
  }
57363
- writeFileSync18(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58697
+ writeFileSync20(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
57364
58698
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
57365
58699
  await installClaudeMcpConfig();
57366
- const claudeMd = join27(ROOT, "CLAUDE.md");
58700
+ const claudeMd = join29(ROOT, "CLAUDE.md");
57367
58701
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
57368
58702
  if (recall.kind === "collision") {
57369
58703
  console.warn(
@@ -57375,15 +58709,15 @@ async function installClaudeHooks(port) {
57375
58709
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
57376
58710
  }
57377
58711
  async function installClaudeMcpConfig() {
57378
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57379
- const { join: join27, dirname: dirname10 } = await import("node:path");
57380
- const file2 = join27(ROOT, ".mcp.json");
57381
- const dir = dirname10(file2);
57382
- 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 });
57383
58717
  let cfg = {};
57384
- if (existsSync25(file2)) {
58718
+ if (existsSync26(file2)) {
57385
58719
  try {
57386
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58720
+ cfg = JSON.parse(readFileSync25(file2, "utf8"));
57387
58721
  } catch {
57388
58722
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57389
58723
  process.exit(2);
@@ -57391,21 +58725,21 @@ async function installClaudeMcpConfig() {
57391
58725
  }
57392
58726
  cfg.mcpServers ??= {};
57393
58727
  cfg.mcpServers["errata"] = errataMcpInvocation();
57394
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58728
+ writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
57395
58729
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
57396
58730
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
57397
58731
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
57398
58732
  }
57399
58733
  async function installCursorMcpConfig() {
57400
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57401
- const { join: join27 } = await import("node:path");
57402
- const dir = join27(ROOT, ".cursor");
57403
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57404
- 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");
57405
58739
  let cfg = {};
57406
- if (existsSync25(file2)) {
58740
+ if (existsSync26(file2)) {
57407
58741
  try {
57408
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58742
+ cfg = JSON.parse(readFileSync25(file2, "utf8"));
57409
58743
  } catch {
57410
58744
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
57411
58745
  process.exit(2);
@@ -57413,7 +58747,7 @@ async function installCursorMcpConfig() {
57413
58747
  }
57414
58748
  cfg.mcpServers ??= {};
57415
58749
  cfg.mcpServers["errata"] = errataMcpInvocation();
57416
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58750
+ writeFileSync20(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
57417
58751
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
57418
58752
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
57419
58753
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -57421,16 +58755,16 @@ async function installCursorMcpConfig() {
57421
58755
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
57422
58756
  }
57423
58757
  async function installCodexHooks(port) {
57424
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57425
- const { join: join27 } = await import("node:path");
57426
- const dir = join27(ROOT, ".codex");
57427
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57428
- 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");
57429
58763
  const BEGIN = `# >>> errata hooks (errata-managed)`;
57430
58764
  const END = `# <<< errata hooks`;
57431
58765
  let existing = "";
57432
- if (existsSync25(file2)) {
57433
- existing = readFileSync23(file2, "utf8");
58766
+ if (existsSync26(file2)) {
58767
+ existing = readFileSync25(file2, "utf8");
57434
58768
  const beginIdx = existing.indexOf(BEGIN);
57435
58769
  const endIdx = existing.indexOf(END);
57436
58770
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -57459,7 +58793,7 @@ ${END}
57459
58793
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
57460
58794
 
57461
58795
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
57462
- writeFileSync18(file2, final, "utf8");
58796
+ writeFileSync20(file2, final, "utf8");
57463
58797
  console.log(`installed Codex hooks \u2192 ${file2}`);
57464
58798
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
57465
58799
  console.log("");
@@ -57497,8 +58831,35 @@ async function cmdSync(arg) {
57497
58831
  try {
57498
58832
  const res = await fetch(`${lock.webUiUrl}/api/sync`, {
57499
58833
  method: "POST",
57500
- signal: AbortSignal.timeout(3e4)
58834
+ signal: AbortSignal.timeout(15 * 6e4)
57501
58835
  });
58836
+ if (res.status === 409) {
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
+ }
58861
+ return;
58862
+ }
57502
58863
  if (!res.ok) {
57503
58864
  const detail = await res.text().catch(() => "");
57504
58865
  console.error(`sync failed: daemon at ${lock.webUiUrl} returned ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
@@ -57509,7 +58870,7 @@ async function cmdSync(arg) {
57509
58870
  } catch (err2) {
57510
58871
  const timedOut = err2 instanceof Error && err2.name === "TimeoutError";
57511
58872
  console.error(
57512
- timedOut ? `sync failed: the running daemon at ${lock.webUiUrl} didn't respond within 30s \u2014 it may be mid-reindex. Retry shortly.` : `sync failed: could not reach the running daemon at ${lock.webUiUrl} (${err2 instanceof Error ? err2.message : err2})`
58873
+ timedOut ? `sync gave up waiting after 15 min \u2014 the daemon's pass may STILL be running (aborting this request does not cancel it). Check daemon.log before retrying; a retry while it runs answers 409.` : `sync failed: could not reach the running daemon at ${lock.webUiUrl} (${err2 instanceof Error ? err2.message : err2})`
57513
58874
  );
57514
58875
  process.exit(1);
57515
58876
  }
@@ -57572,7 +58933,19 @@ async function cmdConsent(args2) {
57572
58933
  console.log(" note: run `errata login` to actually upload (no cloud credential yet).");
57573
58934
  }
57574
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
+ }
57575
58947
  async function cmdDash(args2) {
58948
+ installLogTimestamps();
57576
58949
  const portIdx = args2.indexOf("--port");
57577
58950
  const port = portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : 7891;
57578
58951
  const reindexOnStart = !args2.includes("--no-reindex");
@@ -57734,7 +59107,7 @@ async function cmdDash(args2) {
57734
59107
  await yieldToLoop2();
57735
59108
  try {
57736
59109
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
57737
- const res = bleedRules(join26(r.root, ".claude", "rules"), items);
59110
+ const res = bleedRules(join28(r.root, ".claude", "rules"), items);
57738
59111
  if (res.written || res.pruned) {
57739
59112
  console.log(
57740
59113
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")