@inerrata-corporation/errata 2.0.2-dev.192 → 2.0.2-dev.201

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 +407 -198
  2. package/package.json +1 -1
  3. package/pass-worker.mjs +18 -6
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();
@@ -47126,12 +47172,12 @@ var init_report_render = __esm({
47126
47172
 
47127
47173
  // src/cli.ts
47128
47174
  init_src5();
47129
- import { closeSync as closeSync2, existsSync as existsSync24, openSync as openSync2, readFileSync as readFileSync22, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
47130
- import { join as join26 } from "node:path";
47175
+ import { closeSync as closeSync2, existsSync as existsSync25, openSync as openSync2, readFileSync as readFileSync23, renameSync as renameSync3, statSync as statSync6 } from "node:fs";
47176
+ import { join as join27 } from "node:path";
47131
47177
  import { spawn as spawn3 } from "node:child_process";
47132
47178
 
47133
47179
  // src/daemon.ts
47134
- import { existsSync as existsSync19, writeFileSync as writeFileSync15 } from "node:fs";
47180
+ import { existsSync as existsSync20, writeFileSync as writeFileSync16 } from "node:fs";
47135
47181
 
47136
47182
  // ../../node_modules/.pnpm/@hono+node-server@1.19.11_hono@4.12.8/node_modules/@hono/node-server/dist/index.mjs
47137
47183
  import { createServer as createServerHTTP } from "http";
@@ -47711,8 +47757,8 @@ init_config();
47711
47757
 
47712
47758
  // src/engine.ts
47713
47759
  import { execFileSync as execFileSync3 } from "node:child_process";
47714
- 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";
47715
- import { join as join22, relative as relative6, sep as sep4 } from "node:path";
47760
+ import { existsSync as existsSync19, statSync as statSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync9, renameSync as renameSync2, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "node:fs";
47761
+ import { join as join23, relative as relative6, sep as sep4 } from "node:path";
47716
47762
 
47717
47763
  // ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
47718
47764
  import { stat as statcb } from "fs";
@@ -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);
@@ -50492,6 +50541,147 @@ ${conversation}` }]
50492
50541
  };
50493
50542
  }
50494
50543
 
50544
+ // src/constraint-backfill.ts
50545
+ init_src4();
50546
+ init_src2();
50547
+ import { existsSync as existsSync13, readFileSync as readFileSync11, readdirSync as readdirSync6, writeFileSync as writeFileSync11 } from "node:fs";
50548
+ import { join as join16 } from "node:path";
50549
+ import { homedir as homedir4 } from "node:os";
50550
+ var BACKFILL_VERSION = 1;
50551
+ var EMPTY = {
50552
+ skipped: true,
50553
+ stamped: 0,
50554
+ detached: 0,
50555
+ reopened: 0,
50556
+ witnessed: 0,
50557
+ cloudTwins: []
50558
+ };
50559
+ function markerPath(configDir) {
50560
+ return join16(configDir, "constraint-backfill.json");
50561
+ }
50562
+ function alreadyDone(configDir) {
50563
+ const p = markerPath(configDir);
50564
+ if (!existsSync13(p)) return false;
50565
+ try {
50566
+ const raw2 = JSON.parse(readFileSync11(p, "utf8"));
50567
+ return raw2?.version === BACKFILL_VERSION;
50568
+ } catch {
50569
+ return false;
50570
+ }
50571
+ }
50572
+ function replay(root) {
50573
+ const statements = /* @__PURE__ */ new Set();
50574
+ const citedByFix = /* @__PURE__ */ new Set();
50575
+ const dir = claudeProjectDir(root, homedir4());
50576
+ if (!existsSync13(dir)) return { statements, citedByFix };
50577
+ let names;
50578
+ try {
50579
+ names = readdirSync6(dir).filter((n) => n.endsWith(".jsonl"));
50580
+ } catch {
50581
+ return { statements, citedByFix };
50582
+ }
50583
+ for (const f of names) {
50584
+ let lines;
50585
+ try {
50586
+ lines = readFileSync11(join16(dir, f), "utf8").split("\n");
50587
+ } catch {
50588
+ continue;
50589
+ }
50590
+ for (const line of lines) {
50591
+ if (!line.trim()) continue;
50592
+ let rec;
50593
+ try {
50594
+ rec = JSON.parse(line);
50595
+ } catch {
50596
+ continue;
50597
+ }
50598
+ if (rec.type !== "assistant" || !Array.isArray(rec.message?.content)) continue;
50599
+ for (const b of rec.message.content) {
50600
+ if (b?.type !== "text" || typeof b.text !== "string") continue;
50601
+ for (const tag of parseInlineTags(b.text)) {
50602
+ if (tag.kind === "constraint" && tag.statement) statements.add(tag.statement);
50603
+ if (tag.kind === "fix") {
50604
+ const t = tag;
50605
+ if (t.handle) citedByFix.add(t.handle);
50606
+ if (t.threadId) citedByFix.add(t.threadId);
50607
+ }
50608
+ }
50609
+ }
50610
+ }
50611
+ }
50612
+ return { statements, citedByFix };
50613
+ }
50614
+ function backfillConstraintKind(store, opts) {
50615
+ if (!opts.force && !opts.dryRun && alreadyDone(opts.configDir)) return EMPTY;
50616
+ const { statements, citedByFix } = replay(opts.root);
50617
+ const report = {
50618
+ skipped: false,
50619
+ stamped: 0,
50620
+ detached: 0,
50621
+ reopened: 0,
50622
+ witnessed: 0,
50623
+ cloudTwins: []
50624
+ };
50625
+ const seen = /* @__PURE__ */ new Set();
50626
+ const work = [];
50627
+ for (const statement of statements) {
50628
+ const id = designProblemId(statement);
50629
+ if (seen.has(id)) continue;
50630
+ seen.add(id);
50631
+ const node2 = store.getNode(id);
50632
+ if (!node2 || node2.label !== "Problem") continue;
50633
+ const isWitnessed = citedByFix.has(priorHandle(node2)) || citedByFix.has(id) || [...citedByFix].some((c) => c && id.startsWith(c));
50634
+ const fabricated = [];
50635
+ for (const e of store.outEdges(id, ["SOLVED_BY"])) {
50636
+ const sol = store.getNode(e.to);
50637
+ if (!sol) continue;
50638
+ const auto = sol.description.startsWith(AUTO_MINT_PREFIX);
50639
+ if (!auto && isWitnessed) report.witnessed++;
50640
+ else fabricated.push(sol.id);
50641
+ }
50642
+ const stamp = node2.attrs["kind"] !== "constraint";
50643
+ if (stamp) report.stamped++;
50644
+ if (fabricated.length > 0) {
50645
+ report.detached += fabricated.length;
50646
+ report.reopened++;
50647
+ }
50648
+ const cloudNodeId = node2.attrs["cloudNodeId"];
50649
+ if (typeof cloudNodeId === "string" && (stamp || fabricated.length > 0)) {
50650
+ report.cloudTwins.push(cloudNodeId);
50651
+ }
50652
+ if (stamp || fabricated.length > 0) work.push({ id, fabricated, stamp });
50653
+ }
50654
+ if (opts.dryRun) return report;
50655
+ store.transaction(() => {
50656
+ for (const w of work) {
50657
+ const node2 = store.getNode(w.id);
50658
+ if (!node2) continue;
50659
+ const attrs = { ...node2.attrs, kind: "constraint" };
50660
+ if (w.fabricated.length > 0) {
50661
+ delete attrs["resolvedAt"];
50662
+ delete attrs["resolvedReason"];
50663
+ }
50664
+ store.updateNode(w.id, { attrs, lastUpdatedAt: opts.now });
50665
+ for (const solId of w.fabricated) {
50666
+ for (const e of store.outEdges(w.id, ["SOLVED_BY"])) {
50667
+ if (e.to === solId) store.closeEdge(e.id, opts.now);
50668
+ }
50669
+ const sol = store.getNode(solId);
50670
+ if (sol?.description.startsWith(AUTO_MINT_PREFIX)) store.closeNode(solId, opts.now);
50671
+ }
50672
+ }
50673
+ });
50674
+ try {
50675
+ writeFileSync11(
50676
+ markerPath(opts.configDir),
50677
+ JSON.stringify({ version: BACKFILL_VERSION, at: opts.now, ...report, cloudTwins: report.cloudTwins.length }, null, 2),
50678
+ "utf8"
50679
+ );
50680
+ } catch {
50681
+ }
50682
+ return report;
50683
+ }
50684
+
50495
50685
  // src/engine.ts
50496
50686
  init_symbol_summaries();
50497
50687
  init_reconcile();
@@ -50690,11 +50880,11 @@ init_outbox();
50690
50880
  init_src8();
50691
50881
  init_src();
50692
50882
  init_src2();
50693
- import { readFileSync as readFileSync11 } from "node:fs";
50694
- import { join as join16 } from "node:path";
50883
+ import { readFileSync as readFileSync12 } from "node:fs";
50884
+ import { join as join17 } from "node:path";
50695
50885
  function loadClaimIgnorePatterns(workspaceRoot) {
50696
50886
  try {
50697
- return readFileSync11(join16(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
50887
+ return readFileSync12(join17(workspaceRoot, ".errataignore"), "utf8").split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#")).map((l) => l.toLowerCase());
50698
50888
  } catch {
50699
50889
  return [];
50700
50890
  }
@@ -51055,22 +51245,22 @@ async function pullCloudTriage(shared, cloud, profile, limit = 50) {
51055
51245
  }
51056
51246
 
51057
51247
  // src/git-sensor.ts
51058
- import { existsSync as existsSync13, readFileSync as readFileSync12, watch as fsWatch } from "node:fs";
51059
- import { join as join17 } from "node:path";
51248
+ import { existsSync as existsSync14, readFileSync as readFileSync13, watch as fsWatch } from "node:fs";
51249
+ import { join as join18 } from "node:path";
51060
51250
  function readFirstLine(path2) {
51061
51251
  try {
51062
- return readFileSync12(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51252
+ return readFileSync13(path2, "utf8").split(/\r?\n/, 1)[0].trim();
51063
51253
  } catch {
51064
51254
  return null;
51065
51255
  }
51066
51256
  }
51067
51257
  function readGitRefState(gitDir) {
51068
- const head2 = readFirstLine(join17(gitDir, "HEAD"));
51258
+ const head2 = readFirstLine(join18(gitDir, "HEAD"));
51069
51259
  const m = head2 ? /^ref:\s*refs\/heads\/(.+)$/.exec(head2) : null;
51070
51260
  const branch = m ? m[1] : null;
51071
51261
  let sha2 = null;
51072
51262
  if (branch) {
51073
- sha2 = readFirstLine(join17(gitDir, "refs", "heads", branch));
51263
+ sha2 = readFirstLine(join18(gitDir, "refs", "heads", branch));
51074
51264
  if (!sha2) sha2 = shaFromPackedRefs(gitDir, `refs/heads/${branch}`);
51075
51265
  } else if (head2 && /^[0-9a-f]{7,40}$/i.test(head2)) {
51076
51266
  sha2 = head2;
@@ -51078,13 +51268,13 @@ function readGitRefState(gitDir) {
51078
51268
  return {
51079
51269
  branch,
51080
51270
  sha: sha2,
51081
- mergeHeadExists: existsSync13(join17(gitDir, "MERGE_HEAD")),
51082
- origHeadExists: existsSync13(join17(gitDir, "ORIG_HEAD"))
51271
+ mergeHeadExists: existsSync14(join18(gitDir, "MERGE_HEAD")),
51272
+ origHeadExists: existsSync14(join18(gitDir, "ORIG_HEAD"))
51083
51273
  };
51084
51274
  }
51085
51275
  function shaFromPackedRefs(gitDir, ref) {
51086
51276
  try {
51087
- for (const line of readFileSync12(join17(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
51277
+ for (const line of readFileSync13(join18(gitDir, "packed-refs"), "utf8").split(/\r?\n/)) {
51088
51278
  const [sha2, name2] = line.split(/\s+/);
51089
51279
  if (name2 === ref && sha2) return sha2;
51090
51280
  }
@@ -51118,7 +51308,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
51118
51308
  const settle = () => {
51119
51309
  if (timer) clearTimeout(timer);
51120
51310
  timer = setTimeout(() => {
51121
- if (existsSync13(join17(gitDir, "index.lock"))) {
51311
+ if (existsSync14(join18(gitDir, "index.lock"))) {
51122
51312
  settle();
51123
51313
  return;
51124
51314
  }
@@ -51129,7 +51319,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
51129
51319
  }, debounceMs);
51130
51320
  };
51131
51321
  for (const sub of ["HEAD", "logs/HEAD", "MERGE_HEAD", "ORIG_HEAD"]) {
51132
- const p = join17(gitDir, sub);
51322
+ const p = join18(gitDir, sub);
51133
51323
  try {
51134
51324
  watchers.push(fsWatch(p, settle));
51135
51325
  } catch {
@@ -51354,21 +51544,21 @@ var TelemetryRecorder = class {
51354
51544
 
51355
51545
  // src/skills.ts
51356
51546
  import {
51357
- existsSync as existsSync14,
51547
+ existsSync as existsSync15,
51358
51548
  mkdirSync as mkdirSync6,
51359
- readFileSync as readFileSync13,
51360
- readdirSync as readdirSync6,
51549
+ readFileSync as readFileSync14,
51550
+ readdirSync as readdirSync7,
51361
51551
  unlinkSync as unlinkSync2,
51362
- writeFileSync as writeFileSync11
51552
+ writeFileSync as writeFileSync12
51363
51553
  } from "node:fs";
51364
- import { basename as basename4, join as join18 } from "node:path";
51554
+ import { basename as basename4, join as join19 } from "node:path";
51365
51555
  function skillFileName(id) {
51366
51556
  return `${id.replace(/[^A-Za-z0-9_.-]/g, "_")}.md`;
51367
51557
  }
51368
51558
  function readSkillManifest(manifestPath) {
51369
- if (!existsSync14(manifestPath)) return [];
51559
+ if (!existsSync15(manifestPath)) return [];
51370
51560
  try {
51371
- const parsed = JSON.parse(readFileSync13(manifestPath, "utf8"));
51561
+ const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51372
51562
  return (parsed.skills ?? []).map((s) => ({
51373
51563
  title: s.title ?? "",
51374
51564
  layer: s.layer ?? "technique",
@@ -51384,7 +51574,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51384
51574
  const staleDaemon = res.staleDaemon && res.latestDaemonVersion ? { latest: res.latestDaemonVersion } : void 0;
51385
51575
  mkdirSync6(paths.skillsDir, { recursive: true });
51386
51576
  if (res.skills.length === 0 && pins.length === 0) {
51387
- const existing = readdirSync6(paths.skillsDir).filter((f) => f.endsWith(".md"));
51577
+ const existing = readdirSync7(paths.skillsDir).filter((f) => f.endsWith(".md"));
51388
51578
  if (existing.length > 0) return { written: 0, pruned: 0, ...staleDaemon ? { staleDaemon } : {} };
51389
51579
  }
51390
51580
  const rows = [];
@@ -51392,7 +51582,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51392
51582
  for (const s of res.skills) {
51393
51583
  const fileName = skillFileName(s.id);
51394
51584
  keep.add(fileName);
51395
- writeFileSync11(join18(paths.skillsDir, fileName), s.markdown, "utf8");
51585
+ writeFileSync12(join19(paths.skillsDir, fileName), s.markdown, "utf8");
51396
51586
  rows.push({
51397
51587
  id: s.id,
51398
51588
  title: s.title,
@@ -51405,7 +51595,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51405
51595
  const fileName = skillFileName(p.id);
51406
51596
  if (keep.has(fileName)) continue;
51407
51597
  keep.add(fileName);
51408
- writeFileSync11(join18(paths.skillsDir, fileName), p.markdown, "utf8");
51598
+ writeFileSync12(join19(paths.skillsDir, fileName), p.markdown, "utf8");
51409
51599
  rows.push({
51410
51600
  id: p.id,
51411
51601
  title: p.title,
@@ -51415,17 +51605,17 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51415
51605
  });
51416
51606
  }
51417
51607
  let pruned = 0;
51418
- for (const f of readdirSync6(paths.skillsDir)) {
51608
+ for (const f of readdirSync7(paths.skillsDir)) {
51419
51609
  if (!f.endsWith(".md")) continue;
51420
51610
  if (keep.has(basename4(f))) continue;
51421
51611
  try {
51422
- unlinkSync2(join18(paths.skillsDir, f));
51612
+ unlinkSync2(join19(paths.skillsDir, f));
51423
51613
  pruned++;
51424
51614
  } catch {
51425
51615
  }
51426
51616
  }
51427
51617
  rows.sort((a, b) => a.id.localeCompare(b.id));
51428
- writeFileSync11(
51618
+ writeFileSync12(
51429
51619
  paths.skillsManifest,
51430
51620
  JSON.stringify({ generatedAt: Date.now(), skills: rows }, null, 2),
51431
51621
  "utf8"
@@ -51437,22 +51627,22 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
51437
51627
  init_src2();
51438
51628
  import {
51439
51629
  cpSync,
51440
- existsSync as existsSync15,
51630
+ existsSync as existsSync16,
51441
51631
  lstatSync,
51442
51632
  mkdirSync as mkdirSync7,
51443
- readFileSync as readFileSync14,
51444
- readdirSync as readdirSync7,
51633
+ readFileSync as readFileSync15,
51634
+ readdirSync as readdirSync8,
51445
51635
  rmSync as rmSync2,
51446
51636
  symlinkSync,
51447
- writeFileSync as writeFileSync12
51637
+ writeFileSync as writeFileSync13
51448
51638
  } from "node:fs";
51449
- import { join as join19 } from "node:path";
51639
+ import { join as join20 } from "node:path";
51450
51640
  var SKILL_NS = "errata-";
51451
51641
  var HARNESS_SKILL_DIRS = [
51452
- { configDir: ".claude", skillsDir: join19(".claude", "skills") },
51642
+ { configDir: ".claude", skillsDir: join20(".claude", "skills") },
51453
51643
  // Cursor adopted the standard; its exact project dir is still moving — kept
51454
51644
  // best-effort and gated on `.cursor/` presence so we never create it blind.
51455
- { configDir: ".cursor", skillsDir: join19(".cursor", "skills") }
51645
+ { configDir: ".cursor", skillsDir: join20(".cursor", "skills") }
51456
51646
  ];
51457
51647
  function skillSlug(title, id) {
51458
51648
  const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
@@ -51497,12 +51687,12 @@ function skillCiteHandle(s) {
51497
51687
  return priorHandle({ id: s.id, description: s.title });
51498
51688
  }
51499
51689
  function reconcileNamespaced(dir, keep) {
51500
- if (!existsSync15(dir)) return 0;
51690
+ if (!existsSync16(dir)) return 0;
51501
51691
  let pruned = 0;
51502
- for (const name2 of readdirSync7(dir)) {
51692
+ for (const name2 of readdirSync8(dir)) {
51503
51693
  if (!name2.startsWith(SKILL_NS) || keep.has(name2)) continue;
51504
51694
  try {
51505
- rmSync2(join19(dir, name2), { recursive: true, force: true });
51695
+ rmSync2(join20(dir, name2), { recursive: true, force: true });
51506
51696
  pruned++;
51507
51697
  } catch {
51508
51698
  }
@@ -51511,7 +51701,7 @@ function reconcileNamespaced(dir, keep) {
51511
51701
  }
51512
51702
  function linkOrCopy(linkPath, target) {
51513
51703
  try {
51514
- if (existsSync15(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51704
+ if (existsSync16(linkPath) || safeLstat(linkPath)) rmSync2(linkPath, { recursive: true, force: true });
51515
51705
  } catch {
51516
51706
  }
51517
51707
  try {
@@ -51532,7 +51722,7 @@ function safeLstat(p) {
51532
51722
  }
51533
51723
  }
51534
51724
  function emitAndProjectSkills(root, skills) {
51535
- const agentsSkillsDir = join19(root, ".agents", "skills");
51725
+ const agentsSkillsDir = join20(root, ".agents", "skills");
51536
51726
  mkdirSync7(agentsSkillsDir, { recursive: true });
51537
51727
  const slugs = [];
51538
51728
  const keep = /* @__PURE__ */ new Set();
@@ -51540,7 +51730,7 @@ function emitAndProjectSkills(root, skills) {
51540
51730
  for (const s of skills) {
51541
51731
  let body2;
51542
51732
  try {
51543
- body2 = readFileSync14(s.bodyPath, "utf8");
51733
+ body2 = readFileSync15(s.bodyPath, "utf8");
51544
51734
  } catch {
51545
51735
  continue;
51546
51736
  }
@@ -51549,9 +51739,9 @@ function emitAndProjectSkills(root, skills) {
51549
51739
  keep.add(slug2);
51550
51740
  slugs.push(slug2);
51551
51741
  const description = deriveDescription(s.title, s.layer, body2);
51552
- mkdirSync7(join19(agentsSkillsDir, slug2), { recursive: true });
51553
- writeFileSync12(
51554
- join19(agentsSkillsDir, slug2, "SKILL.md"),
51742
+ mkdirSync7(join20(agentsSkillsDir, slug2), { recursive: true });
51743
+ writeFileSync13(
51744
+ join20(agentsSkillsDir, slug2, "SKILL.md"),
51555
51745
  renderSkillMd(slug2, description, body2, skillCiteHandle(s)),
51556
51746
  "utf8"
51557
51747
  );
@@ -51560,11 +51750,11 @@ function emitAndProjectSkills(root, skills) {
51560
51750
  reconcileNamespaced(agentsSkillsDir, keep);
51561
51751
  let projected = 0;
51562
51752
  for (const h of HARNESS_SKILL_DIRS) {
51563
- if (!existsSync15(join19(root, h.configDir))) continue;
51564
- const dir = join19(root, h.skillsDir);
51753
+ if (!existsSync16(join20(root, h.configDir))) continue;
51754
+ const dir = join20(root, h.skillsDir);
51565
51755
  mkdirSync7(dir, { recursive: true });
51566
51756
  for (const slug2 of slugs) {
51567
- linkOrCopy(join19(dir, slug2), join19(agentsSkillsDir, slug2));
51757
+ linkOrCopy(join20(dir, slug2), join20(agentsSkillsDir, slug2));
51568
51758
  projected++;
51569
51759
  }
51570
51760
  reconcileNamespaced(dir, keep);
@@ -51573,15 +51763,15 @@ function emitAndProjectSkills(root, skills) {
51573
51763
  return { slugs, emitted, projected };
51574
51764
  }
51575
51765
  function emitInputsFromManifest(erretaDir, manifestPath) {
51576
- if (!existsSync15(manifestPath)) return [];
51766
+ if (!existsSync16(manifestPath)) return [];
51577
51767
  try {
51578
- const parsed = JSON.parse(readFileSync14(manifestPath, "utf8"));
51768
+ const parsed = JSON.parse(readFileSync15(manifestPath, "utf8"));
51579
51769
  return (parsed.skills ?? []).filter((s) => Boolean(s.id && s.file)).map((s) => ({
51580
51770
  id: s.id,
51581
51771
  title: s.title ?? s.id,
51582
51772
  layer: s.layer ?? "technique",
51583
51773
  confidence: s.confidence ?? 0,
51584
- bodyPath: join19(erretaDir, s.file)
51774
+ bodyPath: join20(erretaDir, s.file)
51585
51775
  }));
51586
51776
  } catch {
51587
51777
  return [];
@@ -51595,17 +51785,17 @@ var GITIGNORE_LINES = [
51595
51785
  ".cursor/skills/errata-*/"
51596
51786
  ];
51597
51787
  function ensureSkillGitignore(root) {
51598
- const path2 = join19(root, ".gitignore");
51788
+ const path2 = join20(root, ".gitignore");
51599
51789
  let current = "";
51600
51790
  try {
51601
- current = existsSync15(path2) ? readFileSync14(path2, "utf8") : "";
51791
+ current = existsSync16(path2) ? readFileSync15(path2, "utf8") : "";
51602
51792
  } catch {
51603
51793
  return;
51604
51794
  }
51605
51795
  if (current.includes(GITIGNORE_MARK)) return;
51606
51796
  const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
51607
51797
  try {
51608
- writeFileSync12(path2, `${current}${prefix}
51798
+ writeFileSync13(path2, `${current}${prefix}
51609
51799
  ${GITIGNORE_LINES.join("\n")}
51610
51800
  `, "utf8");
51611
51801
  } catch {
@@ -51769,20 +51959,20 @@ var CausalBuffer = class {
51769
51959
  // src/profile.ts
51770
51960
  init_src2();
51771
51961
  init_paths();
51772
- import { existsSync as existsSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
51962
+ import { existsSync as existsSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "node:fs";
51773
51963
  import { createHash as createHash12 } from "node:crypto";
51774
- import { join as join21 } from "node:path";
51964
+ import { join as join22 } from "node:path";
51775
51965
 
51776
51966
  // src/git-remote.ts
51777
51967
  init_src();
51778
- import { existsSync as existsSync16, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
51779
- import { isAbsolute as isAbsolute3, join as join20, resolve as resolve5 } from "node:path";
51968
+ import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync4 } from "node:fs";
51969
+ import { isAbsolute as isAbsolute3, join as join21, resolve as resolve5 } from "node:path";
51780
51970
  function resolveGitDir(root) {
51781
- const dotGit = join20(root, ".git");
51971
+ const dotGit = join21(root, ".git");
51782
51972
  try {
51783
51973
  const st = statSync4(dotGit);
51784
51974
  if (st.isDirectory()) return dotGit;
51785
- const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync15(dotGit, "utf8"));
51975
+ const m = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync16(dotGit, "utf8"));
51786
51976
  if (!m) return null;
51787
51977
  const dir = m[1];
51788
51978
  return isAbsolute3(dir) ? dir : resolve5(root, dir);
@@ -51791,22 +51981,22 @@ function resolveGitDir(root) {
51791
51981
  }
51792
51982
  }
51793
51983
  function gitConfigPath(gitDir) {
51794
- const commondirFile = join20(gitDir, "commondir");
51795
- if (existsSync16(commondirFile)) {
51796
- const common = readFileSync15(commondirFile, "utf8").trim();
51984
+ const commondirFile = join21(gitDir, "commondir");
51985
+ if (existsSync17(commondirFile)) {
51986
+ const common = readFileSync16(commondirFile, "utf8").trim();
51797
51987
  const commonDir = isAbsolute3(common) ? common : resolve5(gitDir, common);
51798
- return join20(commonDir, "config");
51988
+ return join21(commonDir, "config");
51799
51989
  }
51800
- return join20(gitDir, "config");
51990
+ return join21(gitDir, "config");
51801
51991
  }
51802
51992
  function readRemotes(root) {
51803
51993
  const gitDir = resolveGitDir(root);
51804
51994
  if (!gitDir) return [];
51805
51995
  const cfgPath = gitConfigPath(gitDir);
51806
- if (!existsSync16(cfgPath)) return [];
51996
+ if (!existsSync17(cfgPath)) return [];
51807
51997
  let txt;
51808
51998
  try {
51809
- txt = readFileSync15(cfgPath, "utf8");
51999
+ txt = readFileSync16(cfgPath, "utf8");
51810
52000
  } catch {
51811
52001
  return [];
51812
52002
  }
@@ -51838,13 +52028,13 @@ function refreshRepoLocator(root, profile) {
51838
52028
  }
51839
52029
  function loadProfile(root) {
51840
52030
  const p = workspacePaths(root);
51841
- if (!existsSync17(p.workspaceJson)) return null;
51842
- return JSON.parse(readFileSync16(p.workspaceJson, "utf8"));
52031
+ if (!existsSync18(p.workspaceJson)) return null;
52032
+ return JSON.parse(readFileSync17(p.workspaceJson, "utf8"));
51843
52033
  }
51844
52034
  function saveProfile(root, profile) {
51845
52035
  const p = workspacePaths(root);
51846
52036
  ensureDir(p.configDir);
51847
- writeFileSync13(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
52037
+ writeFileSync14(p.workspaceJson, JSON.stringify(profile, null, 2), "utf8");
51848
52038
  }
51849
52039
  function autodetectProfile(root) {
51850
52040
  const id = workspaceId(root);
@@ -51852,10 +52042,10 @@ function autodetectProfile(root) {
51852
52042
  const p = emptyProfile(id, name2);
51853
52043
  const locator = detectRepoLocator(root);
51854
52044
  if (locator) p.repoLocator = locator;
51855
- const pkgPath = join21(root, "package.json");
51856
- if (existsSync17(pkgPath)) {
52045
+ const pkgPath = join22(root, "package.json");
52046
+ if (existsSync18(pkgPath)) {
51857
52047
  try {
51858
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
52048
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
51859
52049
  p.languages.push("typescript", "javascript");
51860
52050
  const nodeVer = pkg.engines?.node ?? "node";
51861
52051
  p.stack.push(`node@${nodeVer}`);
@@ -51876,10 +52066,10 @@ function autodetectProfile(root) {
51876
52066
  } catch {
51877
52067
  }
51878
52068
  }
51879
- const pyproject = join21(root, "pyproject.toml");
51880
- if (existsSync17(pyproject)) {
52069
+ const pyproject = join22(root, "pyproject.toml");
52070
+ if (existsSync18(pyproject)) {
51881
52071
  try {
51882
- const txt = readFileSync16(pyproject, "utf8");
52072
+ const txt = readFileSync17(pyproject, "utf8");
51883
52073
  const py = /python\s*=\s*"([^"]+)"/.exec(txt)?.[1];
51884
52074
  p.languages.push("python");
51885
52075
  p.stack.push(`python@${py ?? "3"}`);
@@ -51890,16 +52080,16 @@ function autodetectProfile(root) {
51890
52080
  } catch {
51891
52081
  }
51892
52082
  }
51893
- const reqs = join21(root, "requirements.txt");
51894
- if (existsSync17(reqs)) {
52083
+ const reqs = join22(root, "requirements.txt");
52084
+ if (existsSync18(reqs)) {
51895
52085
  if (!p.languages.includes("python")) p.languages.push("python");
51896
52086
  if (!p.stack.includes("python@3")) p.stack.push("python@3");
51897
52087
  }
51898
- if (existsSync17(join21(root, "go.mod"))) {
52088
+ if (existsSync18(join22(root, "go.mod"))) {
51899
52089
  p.languages.push("go");
51900
52090
  p.stack.push("go");
51901
52091
  }
51902
- if (existsSync17(join21(root, "Cargo.toml"))) {
52092
+ if (existsSync18(join22(root, "Cargo.toml"))) {
51903
52093
  p.languages.push("rust");
51904
52094
  p.stack.push("rust");
51905
52095
  }
@@ -52057,7 +52247,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52057
52247
  }
52058
52248
 
52059
52249
  // src/engine.ts
52060
- var DAEMON_VERSION = true ? "2.0.2-dev.192" : "2.0.0-alpha.0";
52250
+ var DAEMON_VERSION = true ? "2.0.2-dev.201" : "2.0.0-alpha.0";
52061
52251
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52062
52252
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52063
52253
  var GIT_OP_MUTE_MS = 4e3;
@@ -52067,7 +52257,7 @@ var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
52067
52257
  function appendIdentityAudit(path2, record2, line) {
52068
52258
  if (!record2.accepted && record2.score <= 0) return;
52069
52259
  try {
52070
- if (existsSync18(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52260
+ if (existsSync19(path2) && statSync5(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
52071
52261
  renameSync2(path2, `${path2}.1`);
52072
52262
  }
52073
52263
  appendFileSync2(path2, line);
@@ -52077,14 +52267,14 @@ function appendIdentityAudit(path2, record2, line) {
52077
52267
  var yieldToLoop = () => new Promise((r) => setImmediate(r));
52078
52268
  function loadTurnCursors(path2) {
52079
52269
  try {
52080
- return new Map(Object.entries(JSON.parse(readFileSync17(path2, "utf8"))));
52270
+ return new Map(Object.entries(JSON.parse(readFileSync18(path2, "utf8"))));
52081
52271
  } catch {
52082
52272
  return /* @__PURE__ */ new Map();
52083
52273
  }
52084
52274
  }
52085
52275
  function saveTurnCursors(path2, cursors) {
52086
52276
  try {
52087
- writeFileSync14(path2, JSON.stringify(Object.fromEntries(cursors)), "utf8");
52277
+ writeFileSync15(path2, JSON.stringify(Object.fromEntries(cursors)), "utf8");
52088
52278
  } catch {
52089
52279
  }
52090
52280
  }
@@ -52106,7 +52296,7 @@ function gitSourceWatchTargets(root) {
52106
52296
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
52107
52297
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
52108
52298
  );
52109
- ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join22(root, d) + sep4));
52299
+ ignoredDirs = igOut.split("\0").filter(Boolean).map((d) => d.replace(/\/+$/, "")).filter((d) => !IGNORED_PATH.test(join23(root, d) + sep4));
52110
52300
  } catch {
52111
52301
  }
52112
52302
  const hasIgnoredChild = (dir) => ignoredDirs.some((ig) => ig.startsWith(dir + "/"));
@@ -52118,19 +52308,19 @@ function gitSourceWatchTargets(root) {
52118
52308
  if (!f.startsWith(prefix)) continue;
52119
52309
  const rest2 = f.slice(prefix.length);
52120
52310
  if (rest2.includes("/")) children.add(dir === "" ? rest2.slice(0, rest2.indexOf("/")) : dir + "/" + rest2.slice(0, rest2.indexOf("/")));
52121
- else targets.add(join22(root, f));
52311
+ else targets.add(join23(root, f));
52122
52312
  }
52123
52313
  for (const c of children) {
52124
- if (IGNORED_PATH.test(join22(root, c) + sep4)) continue;
52314
+ if (IGNORED_PATH.test(join23(root, c) + sep4)) continue;
52125
52315
  if (hasIgnoredChild(c)) addUnder(c);
52126
- else targets.add(join22(root, c));
52316
+ else targets.add(join23(root, c));
52127
52317
  }
52128
52318
  };
52129
52319
  addUnder("");
52130
52320
  if (targets.size > 0) return [...targets];
52131
52321
  } catch {
52132
52322
  }
52133
- 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)));
52323
+ return readdirSync9(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !IGNORED_PATH.test(join23(root, String(e.name)) + sep4)).map((e) => join23(root, String(e.name)));
52134
52324
  }
52135
52325
  function createWorkspaceEngine(opts) {
52136
52326
  const paths = workspacePaths(opts.workspaceRoot);
@@ -52284,7 +52474,7 @@ function createWorkspaceEngine(opts) {
52284
52474
  const srcPaths = diff.changedPaths.filter((p) => /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(p));
52285
52475
  let episodeId2;
52286
52476
  if (srcPaths.length > 0) {
52287
- const abs = srcPaths.map((p) => join22(opts.workspaceRoot, p));
52477
+ const abs = srcPaths.map((p) => join23(opts.workspaceRoot, p));
52288
52478
  try {
52289
52479
  const r = await runReindexPass(
52290
52480
  `git-reindex:${profile.name} (${abs.length} files)`,
@@ -52320,8 +52510,8 @@ function createWorkspaceEngine(opts) {
52320
52510
  `[errata] git: ${ev.kind} ${ev.newSha.slice(0, 7)} by ${meta3.authorName} \u2014 ${diff.changedPaths.length} file(s), ${diff.renames.length} rename(s)`
52321
52511
  );
52322
52512
  };
52323
- const gitDir = join22(opts.workspaceRoot, ".git");
52324
- if (existsSync18(gitDir)) {
52513
+ const gitDir = join23(opts.workspaceRoot, ".git");
52514
+ if (existsSync19(gitDir)) {
52325
52515
  stopGit = startGitSensor(gitDir, (ev) => {
52326
52516
  void handleGitEvent(ev).catch((err2) => {
52327
52517
  console.warn("[errata] git event handler failed:", err2);
@@ -52391,10 +52581,10 @@ function createWorkspaceEngine(opts) {
52391
52581
  });
52392
52582
  doneRender?.();
52393
52583
  writeContextFile(opts.workspaceRoot, body2);
52394
- const target = join22(opts.workspaceRoot, "AGENTS.md");
52584
+ const target = join23(opts.workspaceRoot, "AGENTS.md");
52395
52585
  writeManagedBlock(target, { body: AGENTS_POINTER_BODY, stable: true, force: true });
52396
52586
  if (elicit) {
52397
- writePrimingHandles(join22(paths.configDir, "priming-handles.json"), [
52587
+ writePrimingHandles(join23(paths.configDir, "priming-handles.json"), [
52398
52588
  ...snapshot.recentProblems.map((r) => r.node),
52399
52589
  // Resolved-band handles: the ✓ problem AND its Solution are citable
52400
52590
  // (a fix tag on an already-resolved problem no-ops idempotently; the
@@ -52485,6 +52675,21 @@ function createWorkspaceEngine(opts) {
52485
52675
  } catch (err2) {
52486
52676
  console.warn("[errata] anchor backfill failed:", err2);
52487
52677
  }
52678
+ try {
52679
+ const c = backfillConstraintKind(store, {
52680
+ root: opts.workspaceRoot,
52681
+ configDir: paths.configDir,
52682
+ now: Date.now()
52683
+ });
52684
+ if (!c.skipped && (c.stamped > 0 || c.detached > 0)) {
52685
+ console.log(
52686
+ `[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` : "")
52687
+ );
52688
+ refreshContextNow();
52689
+ }
52690
+ } catch (err2) {
52691
+ console.warn("[errata] constraint backfill failed:", err2);
52692
+ }
52488
52693
  const report = runNightlyPipeline(store);
52489
52694
  try {
52490
52695
  const m = mergeDuplicateProblems(store, { ts: Date.now() });
@@ -52584,7 +52789,7 @@ function createWorkspaceEngine(opts) {
52584
52789
  resultSummary: { exitCode: e.exitCode ?? 1, errorTokens: e.errorTokens }
52585
52790
  });
52586
52791
  };
52587
- const turnCursorPath = join22(paths.configDir, "turn-cursors.json");
52792
+ const turnCursorPath = join23(paths.configDir, "turn-cursors.json");
52588
52793
  const lastTurnUuid = loadTurnCursors(turnCursorPath);
52589
52794
  const sessionLastProblem = /* @__PURE__ */ new Map();
52590
52795
  const sessionThreads = /* @__PURE__ */ new Map();
@@ -52607,7 +52812,7 @@ function createWorkspaceEngine(opts) {
52607
52812
  const t = Date.now();
52608
52813
  let processedTurns = 0;
52609
52814
  const elicit = isEdgeElicitationEnabled();
52610
- const handleMap = elicit ? readPrimingHandles(join22(paths.configDir, "priming-handles.json")) : {};
52815
+ const handleMap = elicit ? readPrimingHandles(join23(paths.configDir, "priming-handles.json")) : {};
52611
52816
  const wsRoot = (opts.workspaceRoot ?? "").replace(/\\/g, "/");
52612
52817
  const toRel = (abs) => {
52613
52818
  const p = abs.replace(/\\/g, "/");
@@ -52649,7 +52854,7 @@ function createWorkspaceEngine(opts) {
52649
52854
  ts: t
52650
52855
  });
52651
52856
  if (r.created || r.corroborated) minted++;
52652
- sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52857
+ if (flag.kind !== "constraint") sessionLastProblem.set(sessionId, designProblemId(flag.problem));
52653
52858
  if (r.created || r.corroborated) {
52654
52859
  try {
52655
52860
  if (editedTurnFile) {
@@ -52729,11 +52934,13 @@ function createWorkspaceEngine(opts) {
52729
52934
  const dedupPath = anchorPath ?? hintPath;
52730
52935
  if (dedupPath) {
52731
52936
  try {
52732
- const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t);
52937
+ const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t, p.kind);
52733
52938
  if (dupId) {
52734
52939
  minted++;
52735
- sessionLastProblem.set(sessionId, dupId);
52736
- inScopeProblemId = dupId;
52940
+ if (p.kind !== "constraint") {
52941
+ sessionLastProblem.set(sessionId, dupId);
52942
+ inScopeProblemId = dupId;
52943
+ }
52737
52944
  if (p.threadId) threads.set(p.threadId, dupId);
52738
52945
  continue;
52739
52946
  }
@@ -52747,8 +52954,10 @@ function createWorkspaceEngine(opts) {
52747
52954
  });
52748
52955
  if (r.created || r.corroborated) {
52749
52956
  minted++;
52750
- sessionLastProblem.set(sessionId, designProblemId(p.statement));
52751
- inScopeProblemId = designProblemId(p.statement);
52957
+ if (p.kind !== "constraint") {
52958
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
52959
+ inScopeProblemId = designProblemId(p.statement);
52960
+ }
52752
52961
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
52753
52962
  try {
52754
52963
  if (anchorPath) {
@@ -53138,7 +53347,7 @@ function createWorkspaceEngine(opts) {
53138
53347
  try {
53139
53348
  const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
53140
53349
  emitAndProjectSkills(opts.workspaceRoot, inputs);
53141
- writePrimingHandles(join22(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
53350
+ writePrimingHandles(join23(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
53142
53351
  } catch (err2) {
53143
53352
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
53144
53353
  }
@@ -53325,7 +53534,7 @@ function createWorkspaceEngine(opts) {
53325
53534
  console.log(
53326
53535
  "[errata] sync skipped \u2014 cloud sync consent is off (enable with `errata consent sync on`)"
53327
53536
  );
53328
- const pending = existsSync18(paths.outbox) ? readdirSync8(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
53537
+ const pending = existsSync19(paths.outbox) ? readdirSync9(paths.outbox).filter((f) => f.endsWith(".json")).length : 0;
53329
53538
  return { uploaded: 0, failed: 0, remaining: pending };
53330
53539
  }
53331
53540
  try {
@@ -53412,7 +53621,7 @@ async function startDaemon(opts) {
53412
53621
  reviewUrl: () => webUiUrl + "/review"
53413
53622
  });
53414
53623
  const writeLockFile = (url2) => {
53415
- writeFileSync15(
53624
+ writeFileSync16(
53416
53625
  engine.paths.daemonLock,
53417
53626
  JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
53418
53627
  "utf8"
@@ -53455,7 +53664,7 @@ async function startDaemon(opts) {
53455
53664
  );
53456
53665
  await engine.stop();
53457
53666
  try {
53458
- if (existsSync19(engine.paths.daemonLock)) {
53667
+ if (existsSync20(engine.paths.daemonLock)) {
53459
53668
  }
53460
53669
  } catch {
53461
53670
  }
@@ -53472,16 +53681,16 @@ async function listenServer(fetchFn, port) {
53472
53681
 
53473
53682
  // src/registry.ts
53474
53683
  init_paths();
53475
- import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
53476
- import { join as join23 } from "node:path";
53684
+ import { existsSync as existsSync21, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "node:fs";
53685
+ import { join as join24 } from "node:path";
53477
53686
  function registryPath() {
53478
- return process.env["ERRATA_REGISTRY_PATH"] ?? join23(globalDir(), "workspaces.json");
53687
+ return process.env["ERRATA_REGISTRY_PATH"] ?? join24(globalDir(), "workspaces.json");
53479
53688
  }
53480
53689
  function read() {
53481
53690
  const p = registryPath();
53482
- if (!existsSync20(p)) return { version: 1, workspaces: {} };
53691
+ if (!existsSync21(p)) return { version: 1, workspaces: {} };
53483
53692
  try {
53484
- const parsed = JSON.parse(readFileSync18(p, "utf8"));
53693
+ const parsed = JSON.parse(readFileSync19(p, "utf8"));
53485
53694
  return { version: 1, workspaces: parsed.workspaces ?? {} };
53486
53695
  } catch {
53487
53696
  return { version: 1, workspaces: {} };
@@ -53489,7 +53698,7 @@ function read() {
53489
53698
  }
53490
53699
  function write(reg) {
53491
53700
  ensureDir(globalDir());
53492
- writeFileSync16(registryPath(), JSON.stringify(reg, null, 2), "utf8");
53701
+ writeFileSync17(registryPath(), JSON.stringify(reg, null, 2), "utf8");
53493
53702
  }
53494
53703
  function registerWorkspace(profile, root, now = Date.now()) {
53495
53704
  const reg = read();
@@ -53506,7 +53715,7 @@ function pruneMissingWorkspaces() {
53506
53715
  const reg = read();
53507
53716
  const removed = [];
53508
53717
  for (const [id, entry] of Object.entries(reg.workspaces)) {
53509
- if (!existsSync20(entry.path)) {
53718
+ if (!existsSync21(entry.path)) {
53510
53719
  removed.push(entry);
53511
53720
  delete reg.workspaces[id];
53512
53721
  }
@@ -53515,13 +53724,13 @@ function pruneMissingWorkspaces() {
53515
53724
  return removed;
53516
53725
  }
53517
53726
  function workspaceStatus(entry) {
53518
- const missing = !existsSync20(entry.path);
53727
+ const missing = !existsSync21(entry.path);
53519
53728
  const lockPath = workspacePaths(entry.path).daemonLock;
53520
53729
  let running = false;
53521
53730
  let webUiUrl = null;
53522
- if (existsSync20(lockPath)) {
53731
+ if (existsSync21(lockPath)) {
53523
53732
  try {
53524
- const lock = JSON.parse(readFileSync18(lockPath, "utf8"));
53733
+ const lock = JSON.parse(readFileSync19(lockPath, "utf8"));
53525
53734
  if (lock.pid && lock.webUiUrl && pidAlive(lock.pid)) {
53526
53735
  running = true;
53527
53736
  webUiUrl = lock.webUiUrl;
@@ -53549,7 +53758,7 @@ function pidAlive(pid) {
53549
53758
  // src/multi.ts
53550
53759
  init_dist();
53551
53760
  init_src4();
53552
- import { readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync17 } from "node:fs";
53761
+ import { readFileSync as readFileSync22, unlinkSync as unlinkSync3, writeFileSync as writeFileSync18 } from "node:fs";
53553
53762
 
53554
53763
  // src/principle-sync.ts
53555
53764
  init_src4();
@@ -53577,8 +53786,8 @@ init_reconcile();
53577
53786
 
53578
53787
  // src/lockfile-auto.ts
53579
53788
  init_src();
53580
- import { existsSync as existsSync21, readFileSync as readFileSync19 } from "node:fs";
53581
- import { join as join24 } from "node:path";
53789
+ import { existsSync as existsSync22, readFileSync as readFileSync20 } from "node:fs";
53790
+ import { join as join25 } from "node:path";
53582
53791
 
53583
53792
  // src/package-index.ts
53584
53793
  init_src();
@@ -53724,11 +53933,11 @@ function runLockfilePass(opts) {
53724
53933
  { file: "package-lock.json", parse: parsePackageLockJson }
53725
53934
  ];
53726
53935
  for (const c of candidates) {
53727
- const p = join24(opts.root, c.file);
53728
- if (!existsSync21(p)) continue;
53936
+ const p = join25(opts.root, c.file);
53937
+ if (!existsSync22(p)) continue;
53729
53938
  let sbom;
53730
53939
  try {
53731
- sbom = c.parse(readFileSync19(p, "utf8"));
53940
+ sbom = c.parse(readFileSync20(p, "utf8"));
53732
53941
  } catch {
53733
53942
  continue;
53734
53943
  }
@@ -54171,7 +54380,7 @@ var ConsolidateWorker = class {
54171
54380
  init_paths();
54172
54381
 
54173
54382
  // src/lock.ts
54174
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "node:fs";
54383
+ import { existsSync as existsSync23, readFileSync as readFileSync21 } from "node:fs";
54175
54384
  function isProcessAlive(pid) {
54176
54385
  if (!pid || pid <= 0) return false;
54177
54386
  try {
@@ -54182,9 +54391,9 @@ function isProcessAlive(pid) {
54182
54391
  }
54183
54392
  }
54184
54393
  function readDaemonLock(lockPath) {
54185
- if (!existsSync22(lockPath)) return null;
54394
+ if (!existsSync23(lockPath)) return null;
54186
54395
  try {
54187
- const lock = JSON.parse(readFileSync20(lockPath, "utf8"));
54396
+ const lock = JSON.parse(readFileSync21(lockPath, "utf8"));
54188
54397
  return typeof lock.pid === "number" ? lock : null;
54189
54398
  } catch {
54190
54399
  return null;
@@ -54426,12 +54635,12 @@ async function reanchorProject(opts) {
54426
54635
  }
54427
54636
 
54428
54637
  // src/adopt.ts
54429
- import { existsSync as existsSync23 } from "node:fs";
54430
- import { dirname as dirname9, join as join25 } from "node:path";
54638
+ import { existsSync as existsSync24 } from "node:fs";
54639
+ import { dirname as dirname9, join as join26 } from "node:path";
54431
54640
  function findGitRoot(absPath) {
54432
54641
  let dir = absPath;
54433
54642
  for (let depth = 0; depth < 64; depth++) {
54434
- if (existsSync23(join25(dir, ".git"))) return dir;
54643
+ if (existsSync24(join26(dir, ".git"))) return dir;
54435
54644
  const parent = dirname9(dir);
54436
54645
  if (parent === dir) return null;
54437
54646
  dir = parent;
@@ -54653,7 +54862,7 @@ async function startMultiDaemon(opts = {}) {
54653
54862
  void ambientLinkAll();
54654
54863
  app.route(`/ws/${rec.id}`, rec.webApp);
54655
54864
  try {
54656
- writeFileSync17(
54865
+ writeFileSync18(
54657
54866
  rec.engine.paths.daemonLock,
54658
54867
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${rec.id}`, startedAt: Date.now() }),
54659
54868
  "utf8"
@@ -54842,7 +55051,7 @@ async function startMultiDaemon(opts = {}) {
54842
55051
  baseUrl = `http://127.0.0.1:${port}`;
54843
55052
  try {
54844
55053
  ensureDir(globalDir());
54845
- writeFileSync17(
55054
+ writeFileSync18(
54846
55055
  lockPath,
54847
55056
  JSON.stringify({ pid: process.pid, webUiUrl: baseUrl, startedAt: Date.now() }),
54848
55057
  "utf8"
@@ -54851,7 +55060,7 @@ async function startMultiDaemon(opts = {}) {
54851
55060
  }
54852
55061
  for (const r of records) {
54853
55062
  try {
54854
- writeFileSync17(
55063
+ writeFileSync18(
54855
55064
  r.engine.paths.daemonLock,
54856
55065
  JSON.stringify({ pid: process.pid, webUiUrl: `${baseUrl}/ws/${r.id}`, startedAt: Date.now() }),
54857
55066
  "utf8"
@@ -55319,7 +55528,7 @@ async function startMultiDaemon(opts = {}) {
55319
55528
  },
55320
55529
  async stop() {
55321
55530
  try {
55322
- const cur = readFileSync21(lockPath, "utf8");
55531
+ const cur = readFileSync22(lockPath, "utf8");
55323
55532
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
55324
55533
  } catch {
55325
55534
  }
@@ -56339,21 +56548,21 @@ async function cmdInit() {
56339
56548
  if (!skipHooks) {
56340
56549
  console.log("");
56341
56550
  console.log("installing harness hooks...");
56342
- const { existsSync: existsSync25 } = await import("node:fs");
56343
- const { join: join27 } = await import("node:path");
56551
+ const { existsSync: existsSync26 } = await import("node:fs");
56552
+ const { join: join28 } = await import("node:path");
56344
56553
  try {
56345
56554
  await installClaudeHooks(port);
56346
56555
  } catch (err2) {
56347
56556
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
56348
56557
  }
56349
- if (existsSync25(join27(ROOT, ".cursor"))) {
56558
+ if (existsSync26(join28(ROOT, ".cursor"))) {
56350
56559
  try {
56351
56560
  await installCursorMcpConfig();
56352
56561
  } catch (err2) {
56353
56562
  console.warn(` \u26A0\uFE0F Cursor MCP config failed: ${err2 instanceof Error ? err2.message : err2}`);
56354
56563
  }
56355
56564
  }
56356
- if (existsSync25(join27(ROOT, ".codex"))) {
56565
+ if (existsSync26(join28(ROOT, ".codex"))) {
56357
56566
  try {
56358
56567
  await installCodexHooks(port);
56359
56568
  } catch (err2) {
@@ -56510,8 +56719,8 @@ async function cmdStatus() {
56510
56719
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
56511
56720
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
56512
56721
  }
56513
- console.log(` graph db: ${existsSync24(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
56514
- console.log(` event log: ${existsSync24(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
56722
+ console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
56723
+ console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
56515
56724
  const lockPath = globalDaemonLock();
56516
56725
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
56517
56726
  console.log(
@@ -57141,11 +57350,11 @@ function cmdInstallationProfile(args2) {
57141
57350
  }
57142
57351
  async function cmdReview() {
57143
57352
  const paths = workspacePaths(ROOT);
57144
- if (!existsSync24(paths.reviewQueue)) {
57353
+ if (!existsSync25(paths.reviewQueue)) {
57145
57354
  console.log("(review queue empty)");
57146
57355
  return;
57147
57356
  }
57148
- const queue = JSON.parse(readFileSync22(paths.reviewQueue, "utf8"));
57357
+ const queue = JSON.parse(readFileSync23(paths.reviewQueue, "utf8"));
57149
57358
  if (queue.length === 0) {
57150
57359
  console.log("(review queue empty)");
57151
57360
  return;
@@ -57816,7 +58025,7 @@ async function gatherRepo(store, ws) {
57816
58025
  };
57817
58026
  }
57818
58027
  async function gatherReportData(generatedAt) {
57819
- const { existsSync: existsSync25 } = await import("node:fs");
58028
+ const { existsSync: existsSync26 } = await import("node:fs");
57820
58029
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
57821
58030
  const cfg = loadConfig();
57822
58031
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -57824,7 +58033,7 @@ async function gatherReportData(generatedAt) {
57824
58033
  for (const ws of listWorkspaces()) {
57825
58034
  if (ws.missing) continue;
57826
58035
  const dbPath = workspacePaths(ws.path).castalia;
57827
- if (!existsSync25(dbPath)) continue;
58036
+ if (!existsSync26(dbPath)) continue;
57828
58037
  let store = null;
57829
58038
  try {
57830
58039
  store = openGraphStore2({ path: dbPath });
@@ -57855,7 +58064,7 @@ async function gatherReportData(generatedAt) {
57855
58064
  };
57856
58065
  }
57857
58066
  async function cmdReport(args2) {
57858
- const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync18 } = await import("node:fs");
58067
+ const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync19 } = await import("node:fs");
57859
58068
  const { renderReport: renderReport2 } = await Promise.resolve().then(() => (init_report_render(), report_render_exports));
57860
58069
  const includeFutureVerbs = args2.includes("--future-verbs");
57861
58070
  const now = /* @__PURE__ */ new Date();
@@ -57868,8 +58077,8 @@ async function cmdReport(args2) {
57868
58077
  const outDir = workspacePaths(ROOT).configDir;
57869
58078
  mkdirSync8(outDir, { recursive: true });
57870
58079
  const files = renderReport2(data, { includeFutureVerbs });
57871
- for (const f of files) writeFileSync18(join26(outDir, f.name), f.html, "utf8");
57872
- const indexPath = join26(outDir, "report.html");
58080
+ for (const f of files) writeFileSync19(join27(outDir, f.name), f.html, "utf8");
58081
+ const indexPath = join27(outDir, "report.html");
57873
58082
  console.log(`report \u2192 ${indexPath}`);
57874
58083
  console.log(
57875
58084
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -57987,15 +58196,15 @@ function hookRelayCommand(port, path2) {
57987
58196
  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 '{}'`;
57988
58197
  }
57989
58198
  async function installClaudeHooks(port) {
57990
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
57991
- const { join: join27 } = await import("node:path");
57992
- const dir = join27(ROOT, ".claude");
57993
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
57994
- const file2 = join27(dir, "settings.json");
58199
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync24, writeFileSync: writeFileSync19 } = await import("node:fs");
58200
+ const { join: join28 } = await import("node:path");
58201
+ const dir = join28(ROOT, ".claude");
58202
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58203
+ const file2 = join28(dir, "settings.json");
57995
58204
  let settings = {};
57996
- if (existsSync25(file2)) {
58205
+ if (existsSync26(file2)) {
57997
58206
  try {
57998
- settings = JSON.parse(readFileSync23(file2, "utf8"));
58207
+ settings = JSON.parse(readFileSync24(file2, "utf8"));
57999
58208
  } catch {
58000
58209
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58001
58210
  process.exit(2);
@@ -58041,10 +58250,10 @@ async function installClaudeHooks(port) {
58041
58250
  dropErrata(list);
58042
58251
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
58043
58252
  }
58044
- writeFileSync18(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58253
+ writeFileSync19(file2, JSON.stringify(settings, null, 2) + "\n", "utf8");
58045
58254
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
58046
58255
  await installClaudeMcpConfig();
58047
- const claudeMd = join27(ROOT, "CLAUDE.md");
58256
+ const claudeMd = join28(ROOT, "CLAUDE.md");
58048
58257
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
58049
58258
  if (recall.kind === "collision") {
58050
58259
  console.warn(
@@ -58056,15 +58265,15 @@ async function installClaudeHooks(port) {
58056
58265
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
58057
58266
  }
58058
58267
  async function installClaudeMcpConfig() {
58059
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
58060
- const { join: join27, dirname: dirname10 } = await import("node:path");
58061
- const file2 = join27(ROOT, ".mcp.json");
58268
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync24, writeFileSync: writeFileSync19 } = await import("node:fs");
58269
+ const { join: join28, dirname: dirname10 } = await import("node:path");
58270
+ const file2 = join28(ROOT, ".mcp.json");
58062
58271
  const dir = dirname10(file2);
58063
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
58272
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58064
58273
  let cfg = {};
58065
- if (existsSync25(file2)) {
58274
+ if (existsSync26(file2)) {
58066
58275
  try {
58067
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58276
+ cfg = JSON.parse(readFileSync24(file2, "utf8"));
58068
58277
  } catch {
58069
58278
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58070
58279
  process.exit(2);
@@ -58072,21 +58281,21 @@ async function installClaudeMcpConfig() {
58072
58281
  }
58073
58282
  cfg.mcpServers ??= {};
58074
58283
  cfg.mcpServers["errata"] = errataMcpInvocation();
58075
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58284
+ writeFileSync19(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58076
58285
  console.log(`installed Claude Code MCP server config \u2192 ${file2}`);
58077
58286
  console.log(` Claude Code will spawn \`errata mcp\` on workspace open.`);
58078
58287
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
58079
58288
  }
58080
58289
  async function installCursorMcpConfig() {
58081
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
58082
- const { join: join27 } = await import("node:path");
58083
- const dir = join27(ROOT, ".cursor");
58084
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
58085
- const file2 = join27(dir, "mcp.json");
58290
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync24, writeFileSync: writeFileSync19 } = await import("node:fs");
58291
+ const { join: join28 } = await import("node:path");
58292
+ const dir = join28(ROOT, ".cursor");
58293
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58294
+ const file2 = join28(dir, "mcp.json");
58086
58295
  let cfg = {};
58087
- if (existsSync25(file2)) {
58296
+ if (existsSync26(file2)) {
58088
58297
  try {
58089
- cfg = JSON.parse(readFileSync23(file2, "utf8"));
58298
+ cfg = JSON.parse(readFileSync24(file2, "utf8"));
58090
58299
  } catch {
58091
58300
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
58092
58301
  process.exit(2);
@@ -58094,7 +58303,7 @@ async function installCursorMcpConfig() {
58094
58303
  }
58095
58304
  cfg.mcpServers ??= {};
58096
58305
  cfg.mcpServers["errata"] = errataMcpInvocation();
58097
- writeFileSync18(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58306
+ writeFileSync19(file2, JSON.stringify(cfg, null, 2) + "\n", "utf8");
58098
58307
  console.log(`installed Cursor MCP server config \u2192 ${file2}`);
58099
58308
  console.log(` Cursor will spawn \`errata mcp\` on workspace open.`);
58100
58309
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses`);
@@ -58102,16 +58311,16 @@ async function installCursorMcpConfig() {
58102
58311
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
58103
58312
  }
58104
58313
  async function installCodexHooks(port) {
58105
- const { mkdirSync: mkdirSync8, existsSync: existsSync25, readFileSync: readFileSync23, writeFileSync: writeFileSync18 } = await import("node:fs");
58106
- const { join: join27 } = await import("node:path");
58107
- const dir = join27(ROOT, ".codex");
58108
- if (!existsSync25(dir)) mkdirSync8(dir, { recursive: true });
58109
- const file2 = join27(dir, "config.toml");
58314
+ const { mkdirSync: mkdirSync8, existsSync: existsSync26, readFileSync: readFileSync24, writeFileSync: writeFileSync19 } = await import("node:fs");
58315
+ const { join: join28 } = await import("node:path");
58316
+ const dir = join28(ROOT, ".codex");
58317
+ if (!existsSync26(dir)) mkdirSync8(dir, { recursive: true });
58318
+ const file2 = join28(dir, "config.toml");
58110
58319
  const BEGIN = `# >>> errata hooks (errata-managed)`;
58111
58320
  const END = `# <<< errata hooks`;
58112
58321
  let existing = "";
58113
- if (existsSync25(file2)) {
58114
- existing = readFileSync23(file2, "utf8");
58322
+ if (existsSync26(file2)) {
58323
+ existing = readFileSync24(file2, "utf8");
58115
58324
  const beginIdx = existing.indexOf(BEGIN);
58116
58325
  const endIdx = existing.indexOf(END);
58117
58326
  if (beginIdx >= 0 && endIdx > beginIdx) {
@@ -58140,7 +58349,7 @@ ${END}
58140
58349
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
58141
58350
 
58142
58351
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
58143
- writeFileSync18(file2, final, "utf8");
58352
+ writeFileSync19(file2, final, "utf8");
58144
58353
  console.log(`installed Codex hooks \u2192 ${file2}`);
58145
58354
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
58146
58355
  console.log("");
@@ -58430,7 +58639,7 @@ async function cmdDash(args2) {
58430
58639
  await yieldToLoop2();
58431
58640
  try {
58432
58641
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile);
58433
- const res = bleedRules(join26(r.root, ".claude", "rules"), items);
58642
+ const res = bleedRules(join27(r.root, ".claude", "rules"), items);
58434
58643
  if (res.written || res.pruned) {
58435
58644
  console.log(
58436
58645
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")