@inerrata-corporation/errata 2.0.2-dev.174 → 2.0.2-dev.196

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/errata.mjs +140 -42
  2. package/package.json +1 -1
  3. package/pass-worker.mjs +20 -8
package/errata.mjs CHANGED
@@ -15731,6 +15731,9 @@ function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
15731
15731
  const path2 = normalizeSymbolPath(relPath);
15732
15732
  return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
15733
15733
  }
15734
+ function isMembraneSaltedId(id) {
15735
+ return typeof id === "string" && /^(orgn_|teamn_|projn_)/.test(id);
15736
+ }
15734
15737
  var init_project_symbol = __esm({
15735
15738
  "../../packages/shared/src/project-symbol.ts"() {
15736
15739
  "use strict";
@@ -15980,6 +15983,7 @@ __export(src_exports, {
15980
15983
  isCodeLabel: () => isCodeLabel,
15981
15984
  isContextLabel: () => isContextLabel,
15982
15985
  isInferredSource: () => isInferredSource,
15986
+ isMembraneSaltedId: () => isMembraneSaltedId,
15983
15987
  isProjectOnlyEdgeType: () => isProjectOnlyEdgeType,
15984
15988
  isProjectOnlyNodeLabel: () => isProjectOnlyNodeLabel,
15985
15989
  isQuarantinedExtractionSource: () => isQuarantinedExtractionSource,
@@ -18179,6 +18183,9 @@ var init_problem_package_link = __esm({
18179
18183
  });
18180
18184
 
18181
18185
  // ../../packages/local-graph/src/design-problem.ts
18186
+ function isConstraintProblem(node2) {
18187
+ return node2.attrs["kind"] === "constraint";
18188
+ }
18182
18189
  function isPlaceholderStatement(statement) {
18183
18190
  const s = statement.trim();
18184
18191
  if (s.length < 8) return true;
@@ -18428,7 +18435,7 @@ function tokenJaccard(a, b) {
18428
18435
  for (const t of sa) if (sb.has(t)) inter++;
18429
18436
  return inter / (sa.size + sb.size - inter);
18430
18437
  }
18431
- function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts) {
18438
+ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
18432
18439
  const stmt = statement.trim();
18433
18440
  if (!ANCHORABLE_CODE.test(relPath)) return null;
18434
18441
  const file2 = resolveFileNode(store, relPath, workspaceId2);
@@ -18439,6 +18446,8 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
18439
18446
  const cand = store.getNode(e.from);
18440
18447
  if (!cand || cand.label !== "Problem" || cand.attrs["resolvedAt"]) continue;
18441
18448
  if (cand.id === selfId) continue;
18449
+ const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
18450
+ if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
18442
18451
  const score2 = tokenJaccard(stmt, cand.description);
18443
18452
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
18444
18453
  }
@@ -18473,6 +18482,7 @@ function priorsForFile(store, relPath) {
18473
18482
  }
18474
18483
  if (!file2) return null;
18475
18484
  const openProblems = [];
18485
+ const constraints = [];
18476
18486
  const seenProblem = /* @__PURE__ */ new Set();
18477
18487
  const related = /* @__PURE__ */ new Map();
18478
18488
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
@@ -18481,14 +18491,14 @@ function priorsForFile(store, relPath) {
18481
18491
  if (n.label === "Problem") {
18482
18492
  if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
18483
18493
  seenProblem.add(n.id);
18484
- openProblems.push(n);
18494
+ (isConstraintProblem(n) ? constraints : openProblems).push(n);
18485
18495
  }
18486
18496
  } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
18487
18497
  related.set(n.id, n);
18488
18498
  }
18489
18499
  }
18490
18500
  const solutionsByProblem = /* @__PURE__ */ new Map();
18491
- for (const p of openProblems) {
18501
+ for (const p of [...openProblems, ...constraints]) {
18492
18502
  for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
18493
18503
  const n = store.getNode(e.to);
18494
18504
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
@@ -18499,9 +18509,11 @@ function priorsForFile(store, relPath) {
18499
18509
  }
18500
18510
  }
18501
18511
  }
18502
- if (openProblems.length === 0 && related.size === 0) return null;
18503
- openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
18504
- return { file: file2, openProblems, related: [...related.values()], solutionsByProblem };
18512
+ if (openProblems.length === 0 && constraints.length === 0 && related.size === 0) return null;
18513
+ const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
18514
+ openProblems.sort(recentFirst);
18515
+ constraints.sort(recentFirst);
18516
+ return { file: file2, openProblems, constraints, related: [...related.values()], solutionsByProblem };
18505
18517
  }
18506
18518
  function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
18507
18519
  return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
@@ -18622,6 +18634,7 @@ function resolveDesignProblems(store, t) {
18622
18634
  for (const p of store.findNodesByLabel("Problem")) {
18623
18635
  if (!p.id.startsWith("dprob_")) continue;
18624
18636
  if (p.attrs["resolvedAt"]) continue;
18637
+ if (isConstraintProblem(p)) continue;
18625
18638
  let symName = "";
18626
18639
  let symRelPath;
18627
18640
  let edited = false;
@@ -18639,7 +18652,7 @@ function resolveDesignProblems(store, t) {
18639
18652
  if (store.outEdges(p.id, ["SOLVED_BY"]).length === 0) {
18640
18653
  const solId = `dfix_${digest({ problemId: p.id, edit: t })}`.slice(0, 56);
18641
18654
  store.mergeNode(
18642
- buildNode(solId, "Solution", `addressed by an edit to ${symName}`, t, {
18655
+ buildNode(solId, "Solution", `${AUTO_MINT_PREFIX}${symName}`, t, {
18643
18656
  source: "convo",
18644
18657
  provisional: false,
18645
18658
  // AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
@@ -18660,7 +18673,7 @@ function resolveDesignProblems(store, t) {
18660
18673
  }
18661
18674
  return resolved;
18662
18675
  }
18663
- var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES;
18676
+ var DESIGN_PROMOTE_AT, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, ANCHORABLE_CODE, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
18664
18677
  var init_design_problem = __esm({
18665
18678
  "../../packages/local-graph/src/design-problem.ts"() {
18666
18679
  "use strict";
@@ -18702,6 +18715,7 @@ var init_design_problem = __esm({
18702
18715
  ]);
18703
18716
  ANCHORABLE_CODE = /\.(ts|tsx|js|mjs|cjs|py|go|rs|java|rb|c|cpp|h)$/;
18704
18717
  MAX_ANCHOR_FILES = 5;
18718
+ AUTO_MINT_PREFIX = "addressed by an edit to ";
18705
18719
  }
18706
18720
  });
18707
18721
 
@@ -18766,13 +18780,11 @@ function backfillLegacyAnchors(store, ts) {
18766
18780
  }
18767
18781
  return report;
18768
18782
  }
18769
- var AUTO_MINT_PREFIX;
18770
18783
  var init_anchor_backfill = __esm({
18771
18784
  "../../packages/local-graph/src/anchor-backfill.ts"() {
18772
18785
  "use strict";
18773
18786
  init_justification();
18774
18787
  init_design_problem();
18775
- AUTO_MINT_PREFIX = "addressed by an edit to ";
18776
18788
  }
18777
18789
  });
18778
18790
 
@@ -20533,6 +20545,7 @@ function mergeDuplicateProblems(store, opts) {
20533
20545
  if (consumed.has(b.id)) continue;
20534
20546
  const tb = tokens.get(b.id);
20535
20547
  if (Math.min(ta.size, tb.size) < minTokens) continue;
20548
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
20536
20549
  if (overlap(ta, tb) >= minOverlap) {
20537
20550
  cluster.push(b);
20538
20551
  consumed.add(b.id);
@@ -20589,6 +20602,7 @@ var init_problem_dedup = __esm({
20589
20602
  "use strict";
20590
20603
  init_src();
20591
20604
  init_src();
20605
+ init_design_problem();
20592
20606
  REDIRECT_EDGES = [
20593
20607
  "CAUSED_BY",
20594
20608
  "SOLVED_BY",
@@ -20795,6 +20809,7 @@ var init_principle_sync = __esm({
20795
20809
  // ../../packages/local-graph/src/index.ts
20796
20810
  var src_exports2 = {};
20797
20811
  __export(src_exports2, {
20812
+ AUTO_MINT_PREFIX: () => AUTO_MINT_PREFIX,
20798
20813
  CAUSAL_FAMILY: () => CAUSAL_FAMILY,
20799
20814
  CITABLE_PRIOR_LABELS: () => CITABLE_PRIOR_LABELS,
20800
20815
  CODE_REACH_EDGES: () => CODE_REACH_EDGES,
@@ -20846,6 +20861,7 @@ __export(src_exports2, {
20846
20861
  induceAbstractions: () => induceAbstractions,
20847
20862
  induceTriage: () => induceTriage,
20848
20863
  ingestDesignProblem: () => ingestDesignProblem,
20864
+ isConstraintProblem: () => isConstraintProblem,
20849
20865
  isPlaceholderStatement: () => isPlaceholderStatement,
20850
20866
  linkProblemToLanguages: () => linkProblemToLanguages,
20851
20867
  linkProblemToPackages: () => linkProblemToPackages,
@@ -20931,12 +20947,14 @@ function isPassiveInjectable(node2) {
20931
20947
  }
20932
20948
  function buildSnapshot(opts) {
20933
20949
  const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
20934
- const allProblems = problems.filter((p) => p.attrs["resolvedAt"] == null);
20950
+ const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
20951
+ const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
20935
20952
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
20936
20953
  const recent = allProblems.slice(0, 8).map((p) => ({
20937
20954
  node: p,
20938
20955
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
20939
20956
  }));
20957
+ const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
20940
20958
  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) => {
20941
20959
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
20942
20960
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
@@ -21003,6 +21021,7 @@ function buildSnapshot(opts) {
21003
21021
  profileContext,
21004
21022
  recentProblems: recent,
21005
21023
  recentResolved,
21024
+ recentConstraints,
21006
21025
  ...causalNudge ? { causalNudge } : {},
21007
21026
  ...domainNudge ? { domainNudge } : {},
21008
21027
  motifs,
@@ -21055,6 +21074,7 @@ function sliceForFile(store, relPath) {
21055
21074
  const fp = priorsForFile(store, relPath);
21056
21075
  const priors = fp ? {
21057
21076
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
21077
+ constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
21058
21078
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
21059
21079
  solutionsByProblem: Object.fromEntries(
21060
21080
  fp.openProblems.slice(0, 3).map((p) => [
@@ -21149,7 +21169,7 @@ function renderSnapshot(s) {
21149
21169
  }
21150
21170
  const tagOf = (n) => s.edgeElicitation ? ` \`[${priorHandle(n)}]\`` : "";
21151
21171
  lines.push("### Recently observed problems in this workspace");
21152
- if (s.recentProblems.length === 0 && s.recentResolved.length === 0) {
21172
+ if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
21153
21173
  lines.push("- _none yet \u2014 errata is still building its model_");
21154
21174
  } else {
21155
21175
  if (s.recentProblems.length > 0) {
@@ -21171,6 +21191,12 @@ function renderSnapshot(s) {
21171
21191
  );
21172
21192
  }
21173
21193
  }
21194
+ if (s.recentConstraints.length > 0) {
21195
+ lines.push("_Design tensions \u2014 constraints this work is shaped around, not defects to fix:_");
21196
+ for (const c of s.recentConstraints) {
21197
+ lines.push(`- \u2696 **${c.description}** \u2014 \`${c.id}\`${tagOf(c)}`);
21198
+ }
21199
+ }
21174
21200
  if (s.recentResolved.length > 0) {
21175
21201
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
21176
21202
  for (const r of s.recentResolved) {
@@ -21238,6 +21264,14 @@ function renderSnapshot(s) {
21238
21264
  }
21239
21265
  }
21240
21266
  }
21267
+ if (w.priors?.constraints.length) {
21268
+ lines.push(
21269
+ "- **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])`:"
21270
+ );
21271
+ for (const c of w.priors.constraints) {
21272
+ lines.push(` - ${c.description} \u2192 \`([${c.id}])\``);
21273
+ }
21274
+ }
21241
21275
  if (w.priors?.related.length) {
21242
21276
  lines.push("- **Related priors** (cite with `([id])` where you lean on one):");
21243
21277
  for (const n of w.priors.related) {
@@ -21275,6 +21309,9 @@ function dropLowestUnit(s) {
21275
21309
  case "pendingEnrichment":
21276
21310
  if (s.pendingEnrichment.length) return s.pendingEnrichment.pop(), true;
21277
21311
  break;
21312
+ case "recentConstraints":
21313
+ if (s.recentConstraints.length) return s.recentConstraints.pop(), true;
21314
+ break;
21278
21315
  case "recentProblems":
21279
21316
  if (s.recentProblems.length) return s.recentProblems.pop(), true;
21280
21317
  break;
@@ -21346,6 +21383,9 @@ ${RECALL_FIRST_BODY}`;
21346
21383
  // budget they drop before anything open/actionable.
21347
21384
  "recentResolved",
21348
21385
  "pendingEnrichment",
21386
+ // A standing design tension outranks an enrichment nudge (it prevents a wrong
21387
+ // decision) but yields to a live defect (which is actionable now).
21388
+ "recentConstraints",
21349
21389
  "recentProblems",
21350
21390
  "needsRevisit"
21351
21391
  ];
@@ -21981,7 +22021,9 @@ var init_client = __esm({
21981
22021
  merged.nodes.push(...r.nodes);
21982
22022
  merged.edges.push(...r.edges);
21983
22023
  for (const rn of r.nodes) {
21984
- if (rn.nodeId && rn.nodeId !== rn.canonicalId) echoedCloudId.set(rn.canonicalId, rn.nodeId);
22024
+ if (rn.nodeId && rn.nodeId !== rn.canonicalId && isMembraneSaltedId(rn.nodeId)) {
22025
+ echoedCloudId.set(rn.canonicalId, rn.nodeId);
22026
+ }
21985
22027
  }
21986
22028
  if (r.patternReconciliation) {
21987
22029
  merged.patternReconciliation = { ...merged.patternReconciliation, ...r.patternReconciliation };
@@ -26469,6 +26511,7 @@ function mergeProblemsByEmbedding(store, opts) {
26469
26511
  if (b.id === a.id || consumed.has(b.id)) continue;
26470
26512
  if (b.embedding.length !== a.embedding.length) continue;
26471
26513
  if ((a.attrs["embeddingVersion"] ?? "") !== (b.attrs["embeddingVersion"] ?? "")) continue;
26514
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
26472
26515
  if (cosine(a.embedding, b.embedding) >= minCosine) {
26473
26516
  cluster.push(b);
26474
26517
  consumed.add(b.id);
@@ -37705,11 +37748,11 @@ var init_mcp = __esm({
37705
37748
  },
37706
37749
  {
37707
37750
  name: "errata.problems",
37708
- 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.",
37751
+ 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.",
37709
37752
  inputSchema: {
37710
37753
  type: "object",
37711
37754
  properties: {
37712
- status: { type: "string", description: "open | resolved | retracted | all (default open)" },
37755
+ status: { type: "string", description: "open | resolved | retracted | constraint | all (default open)" },
37713
37756
  limit: { type: "number" }
37714
37757
  }
37715
37758
  },
@@ -37719,7 +37762,7 @@ var init_mcp = __esm({
37719
37762
  const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
37720
37763
  const rows = store.findAllVersionsByLabel("Problem").map((p) => {
37721
37764
  const ra = p.attrs["resolvedAs"];
37722
- const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : "open";
37765
+ const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
37723
37766
  const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
37724
37767
  const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
37725
37768
  return {
@@ -37727,17 +37770,21 @@ var init_mcp = __esm({
37727
37770
  problem: p.description,
37728
37771
  status,
37729
37772
  createdAt: p.createdAt,
37773
+ // Surfaced even when `status` is resolved/retracted, so a caller can tell
37774
+ // an explicitly-discharged TENSION from a fixed defect.
37775
+ ...isConstraintProblem(p) ? { kind: "constraint" } : {},
37730
37776
  ...p.attrs["provisional"] ? { provisional: true } : {},
37731
37777
  ...p.attrs["resolvedReason"] ? { reason: String(p.attrs["resolvedReason"]) } : {},
37732
37778
  ...anchor ? { anchor } : {}
37733
37779
  };
37734
37780
  });
37735
- const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : s === "open";
37781
+ const inScope = (s) => want === "all" ? true : want === "resolved" ? s === "resolved" : want === "retracted" ? RETRACTIONS.has(s) : want === "constraint" ? s === "constraint" : s === "open";
37736
37782
  const items = rows.filter((r) => inScope(r.status)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
37737
37783
  const countBy = (s) => rows.filter((r) => r.status === s).length;
37738
37784
  return {
37739
37785
  open: countBy("open"),
37740
37786
  resolved: countBy("resolved"),
37787
+ constraints: countBy("constraint"),
37741
37788
  retracted: {
37742
37789
  falsePositive: countBy("false_positive"),
37743
37790
  dissolution: countBy("invalid"),
@@ -37826,7 +37873,8 @@ var init_mcp = __esm({
37826
37873
  inputSchema: { type: "object", properties: {} },
37827
37874
  handler: (_args, store) => {
37828
37875
  const problems = store.findNodesByLabel("Problem");
37829
- const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null);
37876
+ const open2 = problems.filter((p) => p.attrs["resolvedAt"] == null && !isConstraintProblem(p));
37877
+ const constraints = problems.filter((p) => p.attrs["resolvedAt"] == null && isConstraintProblem(p));
37830
37878
  const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
37831
37879
  const allProblems = store.findAllVersionsByLabel("Problem");
37832
37880
  const ungroundedDerived = store.findNodesByLabel("Claim").filter((c) => c.attrs["crystallized"] === "derived" && Number(c.attrs["groundedSupport"] ?? 0) === 0);
@@ -37836,6 +37884,7 @@ var init_mcp = __esm({
37836
37884
  strandedSymbols: findStrandedSymbols(store).length,
37837
37885
  openProblems: open2.length,
37838
37886
  openProblemsWithoutFix: noFix.length,
37887
+ designConstraints: constraints.length,
37839
37888
  retractedFalsePositive: allProblems.filter((p) => p.attrs["resolvedAs"] === "false_positive").length,
37840
37889
  retractedDissolution: allProblems.filter((p) => p.attrs["resolvedAs"] === "invalid").length,
37841
37890
  ungroundedDerivedClaims: ungroundedDerived.length,
@@ -38407,7 +38456,7 @@ function formatProblemsMd(r) {
38407
38456
  const lines = [head("problems")];
38408
38457
  const ret = r.retracted ?? { falsePositive: 0, dissolution: 0, duplicate: 0 };
38409
38458
  lines.push(
38410
- `open ${r.open} \xB7 resolved ${r.resolved} \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
38459
+ `open ${r.open} \xB7 resolved ${r.resolved}` + (r.constraints ? ` \xB7 constraints ${r.constraints}` : "") + ` \xB7 retracted: ${ret.falsePositive} fp / ${ret.dissolution} dissolution / ${ret.duplicate} dup`
38411
38460
  );
38412
38461
  lines.push(`showing ${r.count}`);
38413
38462
  lines.push("");
@@ -46145,10 +46194,13 @@ function recallForFile(store, relPath) {
46145
46194
  recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
46146
46195
  );
46147
46196
  }
46197
+ for (const c of priors.constraints.slice(0, 2)) {
46198
+ lines.push(recallLine(c, `design tension \u2014 hold it, don't "fix" it \xB7 ${TAG_EXAMPLE.prior(c.id)} to cite`));
46199
+ }
46148
46200
  for (const n of priors.related.slice(0, 4)) {
46149
46201
  lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
46150
46202
  }
46151
- const total = priors.openProblems.length + priors.related.length;
46203
+ const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
46152
46204
  return `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
46153
46205
  ${lines.join("\n")}
46154
46206
  ` + buildFileRecallInstruction();
@@ -50198,7 +50250,7 @@ function harvestInlineTags(store, text, opts) {
50198
50250
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
50199
50251
  const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
50200
50252
  const tags = parseInlineTags(text);
50201
- 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);
50253
+ 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);
50202
50254
  const bindSymptom = (seq, threadId) => {
50203
50255
  if (threadId) {
50204
50256
  const hit = symptomSeqs.find((s) => s.threadId === threadId);
@@ -50228,7 +50280,7 @@ function harvestInlineTags(store, text, opts) {
50228
50280
  ...tag.threadId ? { threadId: tag.threadId } : {}
50229
50281
  });
50230
50282
  } else if (tag.kind === "constraint") {
50231
- plan.problems.push({ statement: tag.statement, kind: "problem" });
50283
+ plan.problems.push({ statement: tag.statement, kind: "constraint" });
50232
50284
  } else if (tag.kind === "fix") {
50233
50285
  if (tag.handle) {
50234
50286
  const problemId = resolveHandle(store, tag.handle, opts.handleMap);
@@ -50448,7 +50500,10 @@ function parseRollupJson(text) {
50448
50500
  ...str("cause") ? { cause: str("cause") } : {},
50449
50501
  ...str("fix") ? { fix: str("fix") } : {},
50450
50502
  ...str("anchor") ? { anchor: str("anchor") } : {},
50451
- kind: o["kind"] === "todo" ? "todo" : "problem"
50503
+ // Preserve `constraint` (GH-constraint-kind) collapsing it to `problem`
50504
+ // here would re-arm the auto-close and the inferred fix-binding on a design
50505
+ // tension that arrived through the rollup path instead of the inline tag.
50506
+ kind: o["kind"] === "todo" ? "todo" : o["kind"] === "constraint" ? "constraint" : "problem"
50452
50507
  });
50453
50508
  }
50454
50509
  return flags2.slice(0, 12);
@@ -52051,7 +52106,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52051
52106
  }
52052
52107
 
52053
52108
  // src/engine.ts
52054
- var DAEMON_VERSION = true ? "2.0.2-dev.174" : "2.0.0-alpha.0";
52109
+ var DAEMON_VERSION = true ? "2.0.2-dev.196" : "2.0.0-alpha.0";
52055
52110
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52056
52111
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52057
52112
  var GIT_OP_MUTE_MS = 4e3;
@@ -52643,7 +52698,7 @@ function createWorkspaceEngine(opts) {
52643
52698
  ts: t
52644
52699
  });
52645
52700
  if (r.created || r.corroborated) minted++;
52646
- sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52701
+ if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52647
52702
  if (r.created || r.corroborated) {
52648
52703
  try {
52649
52704
  if (editedTurnFile) {
@@ -52723,11 +52778,13 @@ function createWorkspaceEngine(opts) {
52723
52778
  const dedupPath = anchorPath ?? hintPath;
52724
52779
  if (dedupPath) {
52725
52780
  try {
52726
- const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t);
52781
+ const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t, p.kind);
52727
52782
  if (dupId) {
52728
52783
  minted++;
52729
- sessionLastProblem.set(sessionId, dupId);
52730
- inScopeProblemId = dupId;
52784
+ if (p.kind !== "constraint") {
52785
+ sessionLastProblem.set(sessionId, dupId);
52786
+ inScopeProblemId = dupId;
52787
+ }
52731
52788
  if (p.threadId) threads.set(p.threadId, dupId);
52732
52789
  continue;
52733
52790
  }
@@ -52741,8 +52798,10 @@ function createWorkspaceEngine(opts) {
52741
52798
  });
52742
52799
  if (r.created || r.corroborated) {
52743
52800
  minted++;
52744
- sessionLastProblem.set(sessionId, designProblemId(p.statement));
52745
- inScopeProblemId = designProblemId(p.statement);
52801
+ if (p.kind !== "constraint") {
52802
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
52803
+ inScopeProblemId = designProblemId(p.statement);
52804
+ }
52746
52805
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
52747
52806
  try {
52748
52807
  if (anchorPath) {
@@ -53737,6 +53796,7 @@ function runLockfilePass(opts) {
53737
53796
  // src/instance-ingest.ts
53738
53797
  init_src();
53739
53798
  init_src2();
53799
+ init_src4();
53740
53800
  init_src8();
53741
53801
  init_src();
53742
53802
 
@@ -53787,6 +53847,10 @@ function stripCodebaseScope(scope) {
53787
53847
  delete s["codebase"];
53788
53848
  return s;
53789
53849
  }
53850
+ function contributedWireId(n) {
53851
+ const cloudId = n.attrs["cloudNodeId"];
53852
+ return isMembraneSaltedId(cloudId) ? cloudId : n.id;
53853
+ }
53790
53854
  function wireContextId(n) {
53791
53855
  if (n.label === "Language" && n.attrs["source"] !== "cloud") {
53792
53856
  const name2 = String(n.attrs["name"] ?? "").trim() || n.description.trim() || n.id.replace(/^lang:/, "");
@@ -53841,6 +53905,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53841
53905
  const seen = /* @__PURE__ */ new Set();
53842
53906
  const shippedById = /* @__PURE__ */ new Map();
53843
53907
  const anchorSources = /* @__PURE__ */ new Set();
53908
+ const semanticEndpoints = /* @__PURE__ */ new Map();
53844
53909
  const includedByLabel = /* @__PURE__ */ new Map();
53845
53910
  for (const label of INSTANCE_LABELS) {
53846
53911
  const ref = [];
@@ -53853,8 +53918,16 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53853
53918
  const contributedAtSeq = n.attrs["contributedAtSeq"];
53854
53919
  if (typeof contributedAtSeq === "number" && (n.lastReinforcedAtSeq ?? 0) <= contributedAtSeq) {
53855
53920
  anchorSources.add(n.id);
53921
+ semanticEndpoints.set(n.id, contributedWireId(n));
53856
53922
  continue;
53857
53923
  }
53924
+ if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) {
53925
+ const problemShippable = store.inEdges(n.id, ["SOLVED_BY"]).some((e) => {
53926
+ const p = store.getNode(e.from);
53927
+ return p?.label === "Problem" && (seen.has(p.id) || typeof p.attrs["contributedAtSeq"] === "number");
53928
+ });
53929
+ if (!problemShippable) continue;
53930
+ }
53858
53931
  if (!noveltyAgainst(n, ref, noveltyOpts).ready) continue;
53859
53932
  ref.push(n);
53860
53933
  const wireNode = shareable(n);
@@ -53862,6 +53935,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53862
53935
  shippedById.set(n.id, wireNode);
53863
53936
  seen.add(n.id);
53864
53937
  anchorSources.add(n.id);
53938
+ semanticEndpoints.set(n.id, n.id);
53865
53939
  }
53866
53940
  }
53867
53941
  for (const n of nodes) {
@@ -53902,8 +53976,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53902
53976
  const sourceNode = store.getNode(sourceId);
53903
53977
  if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
53904
53978
  anchorDigests[sourceId] = dg;
53905
- const skippedCloudId = !seen.has(sourceId) ? sourceNode?.attrs["cloudNodeId"] : void 0;
53906
- const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
53979
+ const wireFrom = seen.has(sourceId) || !sourceNode ? sourceId : contributedWireId(sourceNode);
53907
53980
  for (const { e, t, wireId } of targets) {
53908
53981
  anchorEdges.push({ ...e, from: wireFrom, to: wireId, attrs: {} });
53909
53982
  if (t.attrs["source"] === "cloud" || contextSeen.has(wireId)) continue;
@@ -53920,14 +53993,28 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53920
53993
  }
53921
53994
  }
53922
53995
  }
53996
+ for (const [localId, shipped] of shippedById) {
53997
+ const local = store.getNode(localId);
53998
+ if (!local || local.label !== "Solution" || !local.description.startsWith(AUTO_MINT_PREFIX)) continue;
53999
+ const problem = store.inEdges(localId, ["SOLVED_BY"]).map((e) => store.getNode(e.from)).find((p) => p?.label === "Problem");
54000
+ if (!problem) continue;
54001
+ const inBatch = shippedById.get(problem.id);
54002
+ if (inBatch) {
54003
+ if (inBatch.anchorVisibility) shipped.anchorVisibility = inBatch.anchorVisibility;
54004
+ if (inBatch.anchor != null) shipped.anchor = inBatch.anchor;
54005
+ else delete shipped.anchor;
54006
+ } else if (isMembraneSaltedId(problem.attrs["cloudNodeId"])) {
54007
+ shipped.anchorVisibility = "private";
54008
+ delete shipped.anchor;
54009
+ }
54010
+ }
53923
54011
  const project = opts.project;
53924
54012
  if (project) {
53925
54013
  const now = Date.now();
53926
54014
  for (const sourceId of anchorSources) {
53927
54015
  const source = store.getNode(sourceId);
53928
54016
  if (!source) continue;
53929
- const skippedCloudId = !seen.has(sourceId) ? source.attrs["cloudNodeId"] : void 0;
53930
- const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
54017
+ const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
53931
54018
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
53932
54019
  const target = store.getNode(e.to);
53933
54020
  if (!target) continue;
@@ -53966,7 +54053,17 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53966
54053
  }
53967
54054
  }
53968
54055
  }
53969
- if (nodes.length === 0 && anchorEdges.length === 0) return null;
54056
+ const edges = [];
54057
+ const semanticEdgeDigests = {};
54058
+ for (const [sourceId, wireFrom] of semanticEndpoints) {
54059
+ const outs = store.outEdges(sourceId, [...INSTANCE_EDGES]).map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) => x.wireTo != null);
54060
+ if (outs.length === 0) continue;
54061
+ const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
54062
+ if (store.getNode(sourceId)?.attrs["semanticEdgesContributedDigest"] === dg) continue;
54063
+ semanticEdgeDigests[sourceId] = dg;
54064
+ for (const { e, wireTo } of outs) edges.push({ ...e, from: wireFrom, to: wireTo, attrs: {} });
54065
+ }
54066
+ if (nodes.length === 0 && anchorEdges.length === 0 && edges.length === 0) return null;
53970
54067
  let symbolSummaries;
53971
54068
  if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
53972
54069
  const lexicon = opts.lexicon;
@@ -53988,12 +54085,6 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53988
54085
  }
53989
54086
  if (count > 0) symbolSummaries = sidecar;
53990
54087
  }
53991
- const edges = [];
53992
- for (const id of seen) {
53993
- for (const e of store.outEdges(id, [...INSTANCE_EDGES])) {
53994
- if (seen.has(e.to)) edges.push({ ...e, attrs: {} });
53995
- }
53996
- }
53997
54088
  edges.push(...anchorEdges);
53998
54089
  const base = {
53999
54090
  daemonVersion,
@@ -54012,7 +54103,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
54012
54103
  // payloadDigest — byte-identical to the pre-sidecar batch (backward compat).
54013
54104
  ...symbolSummaries ? { symbolSummaries } : {}
54014
54105
  };
54015
- return { ...base, payloadDigest: digest(base), anchorDigests };
54106
+ return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests };
54016
54107
  }
54017
54108
 
54018
54109
  // src/backfill-edges.ts
@@ -55182,6 +55273,13 @@ async function startMultiDaemon(opts = {}) {
55182
55273
  attrs: { ...local.attrs, anchorsContributedDigest: dg }
55183
55274
  });
55184
55275
  }
55276
+ for (const [localId, dg] of Object.entries(instances.semanticEdgeDigests)) {
55277
+ const local = store.getNode(localId);
55278
+ if (!local) continue;
55279
+ store.updateNode(localId, {
55280
+ attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
55281
+ });
55282
+ }
55185
55283
  });
55186
55284
  }
55187
55285
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.174",
3
+ "version": "2.0.2-dev.196",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -25145,6 +25145,9 @@ function backfillProblemContext(store2, ts) {
25145
25145
  }
25146
25146
 
25147
25147
  // ../../packages/local-graph/src/design-problem.ts
25148
+ function isConstraintProblem(node) {
25149
+ return node.attrs["kind"] === "constraint";
25150
+ }
25148
25151
  function buildNode(id, label, description, ts, attrs) {
25149
25152
  return {
25150
25153
  id,
@@ -25200,6 +25203,7 @@ function priorsForFile(store2, relPath) {
25200
25203
  }
25201
25204
  if (!file2) return null;
25202
25205
  const openProblems = [];
25206
+ const constraints = [];
25203
25207
  const seenProblem = /* @__PURE__ */ new Set();
25204
25208
  const related = /* @__PURE__ */ new Map();
25205
25209
  for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
@@ -25208,14 +25212,14 @@ function priorsForFile(store2, relPath) {
25208
25212
  if (n.label === "Problem") {
25209
25213
  if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
25210
25214
  seenProblem.add(n.id);
25211
- openProblems.push(n);
25215
+ (isConstraintProblem(n) ? constraints : openProblems).push(n);
25212
25216
  }
25213
25217
  } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
25214
25218
  related.set(n.id, n);
25215
25219
  }
25216
25220
  }
25217
25221
  const solutionsByProblem = /* @__PURE__ */ new Map();
25218
- for (const p of openProblems) {
25222
+ for (const p of [...openProblems, ...constraints]) {
25219
25223
  for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
25220
25224
  const n = store2.getNode(e.to);
25221
25225
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
@@ -25226,9 +25230,11 @@ function priorsForFile(store2, relPath) {
25226
25230
  }
25227
25231
  }
25228
25232
  }
25229
- if (openProblems.length === 0 && related.size === 0) return null;
25230
- openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
25231
- return { file: file2, openProblems, related: [...related.values()], solutionsByProblem };
25233
+ if (openProblems.length === 0 && constraints.length === 0 && related.size === 0) return null;
25234
+ const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
25235
+ openProblems.sort(recentFirst);
25236
+ constraints.sort(recentFirst);
25237
+ return { file: file2, openProblems, constraints, related: [...related.values()], solutionsByProblem };
25232
25238
  }
25233
25239
  function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
25234
25240
  const p = store2.getNode(problemId);
@@ -25239,11 +25245,13 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
25239
25245
  });
25240
25246
  return true;
25241
25247
  }
25248
+ var AUTO_MINT_PREFIX = "addressed by an edit to ";
25242
25249
  function resolveDesignProblems(store2, t) {
25243
25250
  let resolved = 0;
25244
25251
  for (const p of store2.findNodesByLabel("Problem")) {
25245
25252
  if (!p.id.startsWith("dprob_")) continue;
25246
25253
  if (p.attrs["resolvedAt"]) continue;
25254
+ if (isConstraintProblem(p)) continue;
25247
25255
  let symName = "";
25248
25256
  let symRelPath;
25249
25257
  let edited = false;
@@ -25261,7 +25269,7 @@ function resolveDesignProblems(store2, t) {
25261
25269
  if (store2.outEdges(p.id, ["SOLVED_BY"]).length === 0) {
25262
25270
  const solId = `dfix_${digest({ problemId: p.id, edit: t })}`.slice(0, 56);
25263
25271
  store2.mergeNode(
25264
- buildNode(solId, "Solution", `addressed by an edit to ${symName}`, t, {
25272
+ buildNode(solId, "Solution", `${AUTO_MINT_PREFIX}${symName}`, t, {
25265
25273
  source: "convo",
25266
25274
  provisional: false,
25267
25275
  // AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
@@ -25284,7 +25292,6 @@ function resolveDesignProblems(store2, t) {
25284
25292
  }
25285
25293
 
25286
25294
  // ../../packages/local-graph/src/anchor-backfill.ts
25287
- var AUTO_MINT_PREFIX = "addressed by an edit to ";
25288
25295
  function isLegacyAnchor(attrs) {
25289
25296
  return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
25290
25297
  }
@@ -25731,6 +25738,7 @@ function mergeDuplicateProblems(store2, opts) {
25731
25738
  if (consumed.has(b.id)) continue;
25732
25739
  const tb = tokens.get(b.id);
25733
25740
  if (Math.min(ta.size, tb.size) < minTokens) continue;
25741
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
25734
25742
  if (overlap(ta, tb) >= minOverlap) {
25735
25743
  cluster.push(b);
25736
25744
  consumed.add(b.id);
@@ -26063,12 +26071,14 @@ var AGENTS_POINTER_BODY = [
26063
26071
  init_src2();
26064
26072
  function buildSnapshot(opts) {
26065
26073
  const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
26066
- const allProblems = problems.filter((p) => p.attrs["resolvedAt"] == null);
26074
+ const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
26075
+ const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
26067
26076
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
26068
26077
  const recent = allProblems.slice(0, 8).map((p) => ({
26069
26078
  node: p,
26070
26079
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
26071
26080
  }));
26081
+ const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
26072
26082
  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) => {
26073
26083
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
26074
26084
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
@@ -26135,6 +26145,7 @@ function buildSnapshot(opts) {
26135
26145
  profileContext,
26136
26146
  recentProblems: recent,
26137
26147
  recentResolved,
26148
+ recentConstraints,
26138
26149
  ...causalNudge ? { causalNudge } : {},
26139
26150
  ...domainNudge ? { domainNudge } : {},
26140
26151
  motifs,
@@ -26187,6 +26198,7 @@ function sliceForFile(store2, relPath) {
26187
26198
  const fp = priorsForFile(store2, relPath);
26188
26199
  const priors = fp ? {
26189
26200
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
26201
+ constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
26190
26202
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
26191
26203
  solutionsByProblem: Object.fromEntries(
26192
26204
  fp.openProblems.slice(0, 3).map((p) => [