@inerrata-corporation/errata 2.0.2-dev.192 → 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.
package/errata.mjs CHANGED
@@ -18183,6 +18183,9 @@ var init_problem_package_link = __esm({
18183
18183
  });
18184
18184
 
18185
18185
  // ../../packages/local-graph/src/design-problem.ts
18186
+ function isConstraintProblem(node2) {
18187
+ return node2.attrs["kind"] === "constraint";
18188
+ }
18186
18189
  function isPlaceholderStatement(statement) {
18187
18190
  const s = statement.trim();
18188
18191
  if (s.length < 8) return true;
@@ -18432,7 +18435,7 @@ function tokenJaccard(a, b) {
18432
18435
  for (const t of sa) if (sb.has(t)) inter++;
18433
18436
  return inter / (sa.size + sb.size - inter);
18434
18437
  }
18435
- function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts) {
18438
+ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, source, ts, kind = "problem") {
18436
18439
  const stmt = statement.trim();
18437
18440
  if (!ANCHORABLE_CODE.test(relPath)) return null;
18438
18441
  const file2 = resolveFileNode(store, relPath, workspaceId2);
@@ -18443,6 +18446,8 @@ function reinforceSameAnchorProblem(store, relPath, workspaceId2, statement, sou
18443
18446
  const cand = store.getNode(e.from);
18444
18447
  if (!cand || cand.label !== "Problem" || cand.attrs["resolvedAt"]) continue;
18445
18448
  if (cand.id === selfId) continue;
18449
+ const candKind = cand.attrs["kind"] === "constraint" ? "constraint" : "problem";
18450
+ if (candKind !== (kind === "constraint" ? "constraint" : "problem")) continue;
18446
18451
  const score2 = tokenJaccard(stmt, cand.description);
18447
18452
  if (score2 >= SAME_ANCHOR_DEDUP_JACCARD && (!best || score2 > best.score)) best = { node: cand, score: score2 };
18448
18453
  }
@@ -18477,6 +18482,7 @@ function priorsForFile(store, relPath) {
18477
18482
  }
18478
18483
  if (!file2) return null;
18479
18484
  const openProblems = [];
18485
+ const constraints = [];
18480
18486
  const seenProblem = /* @__PURE__ */ new Set();
18481
18487
  const related = /* @__PURE__ */ new Map();
18482
18488
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
@@ -18485,14 +18491,14 @@ function priorsForFile(store, relPath) {
18485
18491
  if (n.label === "Problem") {
18486
18492
  if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
18487
18493
  seenProblem.add(n.id);
18488
- openProblems.push(n);
18494
+ (isConstraintProblem(n) ? constraints : openProblems).push(n);
18489
18495
  }
18490
18496
  } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
18491
18497
  related.set(n.id, n);
18492
18498
  }
18493
18499
  }
18494
18500
  const solutionsByProblem = /* @__PURE__ */ new Map();
18495
- for (const p of openProblems) {
18501
+ for (const p of [...openProblems, ...constraints]) {
18496
18502
  for (const e of store.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
18497
18503
  const n = store.getNode(e.to);
18498
18504
  if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
@@ -18503,9 +18509,11 @@ function priorsForFile(store, relPath) {
18503
18509
  }
18504
18510
  }
18505
18511
  }
18506
- if (openProblems.length === 0 && related.size === 0) return null;
18507
- openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
18508
- 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 };
18509
18517
  }
18510
18518
  function anchorProblemToDiff(store, problemId, changedPaths, workspaceId2, t, opts = {}) {
18511
18519
  return anchorNodeToDiff(store, problemId, "Problem", changedPaths, workspaceId2, t, opts);
@@ -18626,6 +18634,7 @@ function resolveDesignProblems(store, t) {
18626
18634
  for (const p of store.findNodesByLabel("Problem")) {
18627
18635
  if (!p.id.startsWith("dprob_")) continue;
18628
18636
  if (p.attrs["resolvedAt"]) continue;
18637
+ if (isConstraintProblem(p)) continue;
18629
18638
  let symName = "";
18630
18639
  let symRelPath;
18631
18640
  let edited = false;
@@ -20536,6 +20545,7 @@ function mergeDuplicateProblems(store, opts) {
20536
20545
  if (consumed.has(b.id)) continue;
20537
20546
  const tb = tokens.get(b.id);
20538
20547
  if (Math.min(ta.size, tb.size) < minTokens) continue;
20548
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
20539
20549
  if (overlap(ta, tb) >= minOverlap) {
20540
20550
  cluster.push(b);
20541
20551
  consumed.add(b.id);
@@ -20592,6 +20602,7 @@ var init_problem_dedup = __esm({
20592
20602
  "use strict";
20593
20603
  init_src();
20594
20604
  init_src();
20605
+ init_design_problem();
20595
20606
  REDIRECT_EDGES = [
20596
20607
  "CAUSED_BY",
20597
20608
  "SOLVED_BY",
@@ -20850,6 +20861,7 @@ __export(src_exports2, {
20850
20861
  induceAbstractions: () => induceAbstractions,
20851
20862
  induceTriage: () => induceTriage,
20852
20863
  ingestDesignProblem: () => ingestDesignProblem,
20864
+ isConstraintProblem: () => isConstraintProblem,
20853
20865
  isPlaceholderStatement: () => isPlaceholderStatement,
20854
20866
  linkProblemToLanguages: () => linkProblemToLanguages,
20855
20867
  linkProblemToPackages: () => linkProblemToPackages,
@@ -20935,12 +20947,14 @@ function isPassiveInjectable(node2) {
20935
20947
  }
20936
20948
  function buildSnapshot(opts) {
20937
20949
  const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
20938
- 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));
20939
20952
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
20940
20953
  const recent = allProblems.slice(0, 8).map((p) => ({
20941
20954
  node: p,
20942
20955
  anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
20943
20956
  }));
20957
+ const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
20944
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) => {
20945
20959
  const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
20946
20960
  const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
@@ -21007,6 +21021,7 @@ function buildSnapshot(opts) {
21007
21021
  profileContext,
21008
21022
  recentProblems: recent,
21009
21023
  recentResolved,
21024
+ recentConstraints,
21010
21025
  ...causalNudge ? { causalNudge } : {},
21011
21026
  ...domainNudge ? { domainNudge } : {},
21012
21027
  motifs,
@@ -21059,6 +21074,7 @@ function sliceForFile(store, relPath) {
21059
21074
  const fp = priorsForFile(store, relPath);
21060
21075
  const priors = fp ? {
21061
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 })),
21062
21078
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
21063
21079
  solutionsByProblem: Object.fromEntries(
21064
21080
  fp.openProblems.slice(0, 3).map((p) => [
@@ -21153,7 +21169,7 @@ function renderSnapshot(s) {
21153
21169
  }
21154
21170
  const tagOf = (n) => s.edgeElicitation ? ` \`[${priorHandle(n)}]\`` : "";
21155
21171
  lines.push("### Recently observed problems in this workspace");
21156
- if (s.recentProblems.length === 0 && s.recentResolved.length === 0) {
21172
+ if (s.recentProblems.length === 0 && s.recentResolved.length === 0 && s.recentConstraints.length === 0) {
21157
21173
  lines.push("- _none yet \u2014 errata is still building its model_");
21158
21174
  } else {
21159
21175
  if (s.recentProblems.length > 0) {
@@ -21175,6 +21191,12 @@ function renderSnapshot(s) {
21175
21191
  );
21176
21192
  }
21177
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
+ }
21178
21200
  if (s.recentResolved.length > 0) {
21179
21201
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
21180
21202
  for (const r of s.recentResolved) {
@@ -21242,6 +21264,14 @@ function renderSnapshot(s) {
21242
21264
  }
21243
21265
  }
21244
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
+ }
21245
21275
  if (w.priors?.related.length) {
21246
21276
  lines.push("- **Related priors** (cite with `([id])` where you lean on one):");
21247
21277
  for (const n of w.priors.related) {
@@ -21279,6 +21309,9 @@ function dropLowestUnit(s) {
21279
21309
  case "pendingEnrichment":
21280
21310
  if (s.pendingEnrichment.length) return s.pendingEnrichment.pop(), true;
21281
21311
  break;
21312
+ case "recentConstraints":
21313
+ if (s.recentConstraints.length) return s.recentConstraints.pop(), true;
21314
+ break;
21282
21315
  case "recentProblems":
21283
21316
  if (s.recentProblems.length) return s.recentProblems.pop(), true;
21284
21317
  break;
@@ -21350,6 +21383,9 @@ ${RECALL_FIRST_BODY}`;
21350
21383
  // budget they drop before anything open/actionable.
21351
21384
  "recentResolved",
21352
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",
21353
21389
  "recentProblems",
21354
21390
  "needsRevisit"
21355
21391
  ];
@@ -26475,6 +26511,7 @@ function mergeProblemsByEmbedding(store, opts) {
26475
26511
  if (b.id === a.id || consumed.has(b.id)) continue;
26476
26512
  if (b.embedding.length !== a.embedding.length) continue;
26477
26513
  if ((a.attrs["embeddingVersion"] ?? "") !== (b.attrs["embeddingVersion"] ?? "")) continue;
26514
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
26478
26515
  if (cosine(a.embedding, b.embedding) >= minCosine) {
26479
26516
  cluster.push(b);
26480
26517
  consumed.add(b.id);
@@ -37711,11 +37748,11 @@ var init_mcp = __esm({
37711
37748
  },
37712
37749
  {
37713
37750
  name: "errata.problems",
37714
- 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.",
37715
37752
  inputSchema: {
37716
37753
  type: "object",
37717
37754
  properties: {
37718
- status: { type: "string", description: "open | resolved | retracted | all (default open)" },
37755
+ status: { type: "string", description: "open | resolved | retracted | constraint | all (default open)" },
37719
37756
  limit: { type: "number" }
37720
37757
  }
37721
37758
  },
@@ -37725,7 +37762,7 @@ var init_mcp = __esm({
37725
37762
  const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
37726
37763
  const rows = store.findAllVersionsByLabel("Problem").map((p) => {
37727
37764
  const ra = p.attrs["resolvedAs"];
37728
- 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";
37729
37766
  const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
37730
37767
  const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
37731
37768
  return {
@@ -37733,17 +37770,21 @@ var init_mcp = __esm({
37733
37770
  problem: p.description,
37734
37771
  status,
37735
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" } : {},
37736
37776
  ...p.attrs["provisional"] ? { provisional: true } : {},
37737
37777
  ...p.attrs["resolvedReason"] ? { reason: String(p.attrs["resolvedReason"]) } : {},
37738
37778
  ...anchor ? { anchor } : {}
37739
37779
  };
37740
37780
  });
37741
- 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";
37742
37782
  const items = rows.filter((r) => inScope(r.status)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
37743
37783
  const countBy = (s) => rows.filter((r) => r.status === s).length;
37744
37784
  return {
37745
37785
  open: countBy("open"),
37746
37786
  resolved: countBy("resolved"),
37787
+ constraints: countBy("constraint"),
37747
37788
  retracted: {
37748
37789
  falsePositive: countBy("false_positive"),
37749
37790
  dissolution: countBy("invalid"),
@@ -37832,7 +37873,8 @@ var init_mcp = __esm({
37832
37873
  inputSchema: { type: "object", properties: {} },
37833
37874
  handler: (_args, store) => {
37834
37875
  const problems = store.findNodesByLabel("Problem");
37835
- 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));
37836
37878
  const noFix = open2.filter((p) => store.outEdges(p.id, ["SOLVED_BY"]).length === 0);
37837
37879
  const allProblems = store.findAllVersionsByLabel("Problem");
37838
37880
  const ungroundedDerived = store.findNodesByLabel("Claim").filter((c) => c.attrs["crystallized"] === "derived" && Number(c.attrs["groundedSupport"] ?? 0) === 0);
@@ -37842,6 +37884,7 @@ var init_mcp = __esm({
37842
37884
  strandedSymbols: findStrandedSymbols(store).length,
37843
37885
  openProblems: open2.length,
37844
37886
  openProblemsWithoutFix: noFix.length,
37887
+ designConstraints: constraints.length,
37845
37888
  retractedFalsePositive: allProblems.filter((p) => p.attrs["resolvedAs"] === "false_positive").length,
37846
37889
  retractedDissolution: allProblems.filter((p) => p.attrs["resolvedAs"] === "invalid").length,
37847
37890
  ungroundedDerivedClaims: ungroundedDerived.length,
@@ -38413,7 +38456,7 @@ function formatProblemsMd(r) {
38413
38456
  const lines = [head("problems")];
38414
38457
  const ret = r.retracted ?? { falsePositive: 0, dissolution: 0, duplicate: 0 };
38415
38458
  lines.push(
38416
- `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`
38417
38460
  );
38418
38461
  lines.push(`showing ${r.count}`);
38419
38462
  lines.push("");
@@ -46151,10 +46194,13 @@ function recallForFile(store, relPath) {
46151
46194
  recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
46152
46195
  );
46153
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
+ }
46154
46200
  for (const n of priors.related.slice(0, 4)) {
46155
46201
  lines.push(recallLine(n, `cite with ${TAG_EXAMPLE.prior(n.id)}`));
46156
46202
  }
46157
- const total = priors.openProblems.length + priors.related.length;
46203
+ const total = priors.openProblems.length + priors.constraints.length + priors.related.length;
46158
46204
  return `errata \u2014 ${total} prior(s) recorded on this file (act only if relevant):
46159
46205
  ${lines.join("\n")}
46160
46206
  ` + buildFileRecallInstruction();
@@ -50204,7 +50250,7 @@ function harvestInlineTags(store, text, opts) {
50204
50250
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
50205
50251
  const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
50206
50252
  const tags = parseInlineTags(text);
50207
- 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);
50208
50254
  const bindSymptom = (seq, threadId) => {
50209
50255
  if (threadId) {
50210
50256
  const hit = symptomSeqs.find((s) => s.threadId === threadId);
@@ -50234,7 +50280,7 @@ function harvestInlineTags(store, text, opts) {
50234
50280
  ...tag.threadId ? { threadId: tag.threadId } : {}
50235
50281
  });
50236
50282
  } else if (tag.kind === "constraint") {
50237
- plan.problems.push({ statement: tag.statement, kind: "problem" });
50283
+ plan.problems.push({ statement: tag.statement, kind: "constraint" });
50238
50284
  } else if (tag.kind === "fix") {
50239
50285
  if (tag.handle) {
50240
50286
  const problemId = resolveHandle(store, tag.handle, opts.handleMap);
@@ -50454,7 +50500,10 @@ function parseRollupJson(text) {
50454
50500
  ...str("cause") ? { cause: str("cause") } : {},
50455
50501
  ...str("fix") ? { fix: str("fix") } : {},
50456
50502
  ...str("anchor") ? { anchor: str("anchor") } : {},
50457
- 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"
50458
50507
  });
50459
50508
  }
50460
50509
  return flags2.slice(0, 12);
@@ -52057,7 +52106,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52057
52106
  }
52058
52107
 
52059
52108
  // src/engine.ts
52060
- var DAEMON_VERSION = true ? "2.0.2-dev.192" : "2.0.0-alpha.0";
52109
+ var DAEMON_VERSION = true ? "2.0.2-dev.196" : "2.0.0-alpha.0";
52061
52110
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52062
52111
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52063
52112
  var GIT_OP_MUTE_MS = 4e3;
@@ -52649,7 +52698,7 @@ function createWorkspaceEngine(opts) {
52649
52698
  ts: t
52650
52699
  });
52651
52700
  if (r.created || r.corroborated) minted++;
52652
- sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52701
+ if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52653
52702
  if (r.created || r.corroborated) {
52654
52703
  try {
52655
52704
  if (editedTurnFile) {
@@ -52729,11 +52778,13 @@ function createWorkspaceEngine(opts) {
52729
52778
  const dedupPath = anchorPath ?? hintPath;
52730
52779
  if (dedupPath) {
52731
52780
  try {
52732
- 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);
52733
52782
  if (dupId) {
52734
52783
  minted++;
52735
- sessionLastProblem.set(sessionId, dupId);
52736
- inScopeProblemId = dupId;
52784
+ if (p.kind !== "constraint") {
52785
+ sessionLastProblem.set(sessionId, dupId);
52786
+ inScopeProblemId = dupId;
52787
+ }
52737
52788
  if (p.threadId) threads.set(p.threadId, dupId);
52738
52789
  continue;
52739
52790
  }
@@ -52747,8 +52798,10 @@ function createWorkspaceEngine(opts) {
52747
52798
  });
52748
52799
  if (r.created || r.corroborated) {
52749
52800
  minted++;
52750
- sessionLastProblem.set(sessionId, designProblemId(p.statement));
52751
- inScopeProblemId = designProblemId(p.statement);
52801
+ if (p.kind !== "constraint") {
52802
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
52803
+ inScopeProblemId = designProblemId(p.statement);
52804
+ }
52752
52805
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
52753
52806
  try {
52754
52807
  if (anchorPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.192",
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);
@@ -25245,6 +25251,7 @@ function resolveDesignProblems(store2, t) {
25245
25251
  for (const p of store2.findNodesByLabel("Problem")) {
25246
25252
  if (!p.id.startsWith("dprob_")) continue;
25247
25253
  if (p.attrs["resolvedAt"]) continue;
25254
+ if (isConstraintProblem(p)) continue;
25248
25255
  let symName = "";
25249
25256
  let symRelPath;
25250
25257
  let edited = false;
@@ -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) => [