@inerrata-corporation/errata 2.0.2-dev.135 → 2.0.2-dev.151

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 (2) hide show
  1. package/errata.mjs +146 -16
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -49707,10 +49707,13 @@ init_src4();
49707
49707
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
49708
49708
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
49709
49709
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
49710
- var HANDLE_RE = /\[([a-z0-9][\w-]{0,40})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
49710
+ var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
49711
49711
  var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
49712
49712
  var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
49713
49713
  var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
49714
+ var NEST2_UNIT = String.raw`(?:[^()\n]|\((?:[^()\n]|\([^()\n]*\))*\))`;
49715
+ var FIX_LONG_RE = new RegExp(String.raw`\(\s*fix:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{12,}?)\s*\)`, "gi");
49716
+ var CAUSE_LONG_RE = new RegExp(String.raw`\(\s*cause:(?:#([a-z][\w-]{0,63}))?\s*(${NEST2_UNIT}{8,}?)\s*\)`, "gi");
49714
49717
  var CAUSE_ARROW = /\s+(?:<-|←)\s+/;
49715
49718
  var ATTEMPT_RE = /\(\s*(tried|failed):(?:#([a-z][\w-]{0,63}))?\s*((?:[^()\n]|\([^()\n]*\)){8,}?)\s*\)/gi;
49716
49719
  var AIDS_PREFIX = /^\s*aids:(?:#([a-z][\w-]{0,63}))?\s*/i;
@@ -49723,7 +49726,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
49723
49726
  var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
49724
49727
  var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
49725
49728
  var CAUSE_TEXT_MIN = 8;
49726
- var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49729
+ var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n]){8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49727
49730
  var CONSTRAINT_MIN = 8;
49728
49731
  function clauseBefore(sentence, idx) {
49729
49732
  const win = sentence.slice(Math.max(0, idx - 80), idx);
@@ -49738,6 +49741,17 @@ function fixRationaleBefore(sentence, idx) {
49738
49741
  const clause = m ? win.slice(m.index + 1) : win;
49739
49742
  return clause.replace(/\s+/g, " ").trim();
49740
49743
  }
49744
+ function fixRationaleAfter(sentence, endIdx) {
49745
+ const win = sentence.slice(endIdx, endIdx + FIX_RATIONALE_WINDOW).replace(new RegExp(CITE_GROUP_RE.source, "g"), " ").replace(new RegExp(FLAG_RE.source, "g"), " ");
49746
+ const stripped = win.replace(/^[\s—:–,-]+/, "");
49747
+ const m = /^[^.;!?—]*/.exec(stripped);
49748
+ return (m ? m[0] : stripped).replace(/\s+/g, " ").trim();
49749
+ }
49750
+ function deriveFixRationale(sentence, startIdx, endIdx) {
49751
+ const before = fixRationaleBefore(sentence, startIdx);
49752
+ if (before.length >= FIX_RATIONALE_MIN) return before;
49753
+ return fixRationaleAfter(sentence, endIdx);
49754
+ }
49741
49755
  function parseInlineTags(text) {
49742
49756
  const out2 = [];
49743
49757
  const deFenced = text.replace(/```[\s\S]*?```/g, " ");
@@ -49751,9 +49765,11 @@ function parseInlineTags(text) {
49751
49765
  (m) => /\[[!?]|\((?:fix|cause|constraint|tried|failed):|\(\[/.test(m) ? " " : m
49752
49766
  );
49753
49767
  const sfield = sentence.replace(/\s+/g, " ").trim().slice(0, 160);
49768
+ const citeMatched = /* @__PURE__ */ new Set();
49754
49769
  CITE_GROUP_RE.lastIndex = 0;
49755
49770
  let g;
49756
49771
  while ((g = CITE_GROUP_RE.exec(sentence)) !== null) {
49772
+ citeMatched.add(g.index);
49757
49773
  let content = g[1];
49758
49774
  if (/^\s*pattern:/i.test(content)) continue;
49759
49775
  const fixM = FIX_PREFIX.exec(content);
@@ -49807,7 +49823,7 @@ function parseInlineTags(text) {
49807
49823
  if (!isFix && NEG.test(clauseBefore(sentence, g.index))) continue;
49808
49824
  let fixRationale;
49809
49825
  if (isFix) {
49810
- const r = fixRationaleBefore(sentence, g.index);
49826
+ const r = deriveFixRationale(sentence, g.index, g.index + g[0].length);
49811
49827
  if (r.length >= FIX_RATIONALE_MIN) fixRationale = r;
49812
49828
  }
49813
49829
  HANDLE_RE.lastIndex = 0;
@@ -49832,6 +49848,55 @@ function parseInlineTags(text) {
49832
49848
  out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49833
49849
  }
49834
49850
  }
49851
+ for (const pass of [
49852
+ { re: FIX_LONG_RE, isFix: true },
49853
+ { re: CAUSE_LONG_RE, isFix: false }
49854
+ ]) {
49855
+ pass.re.lastIndex = 0;
49856
+ let lm;
49857
+ while ((lm = pass.re.exec(sentence)) !== null) {
49858
+ if (citeMatched.has(lm.index)) continue;
49859
+ const verbThread = lm[1] ?? void 0;
49860
+ const content = lm[2];
49861
+ HANDLE_RE.lastIndex = 0;
49862
+ const h = HANDLE_RE.exec(content);
49863
+ const handle2 = h ? h[1] ?? h[2] : void 0;
49864
+ if (!pass.isFix) {
49865
+ if (handle2) {
49866
+ out2.push({ kind: "triage", handle: handle2, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49867
+ } else {
49868
+ const causeText = content.replace(/\s+/g, " ").trim();
49869
+ if (causeText.length >= CAUSE_TEXT_MIN)
49870
+ out2.push({ kind: "triage", causeText, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49871
+ }
49872
+ continue;
49873
+ }
49874
+ if (handle2) {
49875
+ const inGroup = content.replace(new RegExp(HANDLE_RE.source, "gi"), " ").replace(/\s+/g, " ").trim();
49876
+ const r = inGroup.length >= FIX_RATIONALE_MIN ? inGroup : deriveFixRationale(sentence, lm.index, lm.index + lm[0].length);
49877
+ let emitted = false;
49878
+ HANDLE_RE.lastIndex = 0;
49879
+ let hh;
49880
+ while ((hh = HANDLE_RE.exec(content)) !== null) {
49881
+ const hcap = hh[1] ?? hh[2];
49882
+ if (hcap) {
49883
+ emitted = true;
49884
+ out2.push({
49885
+ kind: "fix",
49886
+ handle: hcap,
49887
+ sentence: sfield,
49888
+ ...r.length >= FIX_RATIONALE_MIN ? { fixRationale: r } : {},
49889
+ ...verbThread ? { threadId: verbThread } : {}
49890
+ });
49891
+ }
49892
+ }
49893
+ if (emitted) continue;
49894
+ }
49895
+ const note = content.replace(/\s+/g, " ").trim();
49896
+ if (note.length >= FIX_RATIONALE_MIN)
49897
+ out2.push({ kind: "fix", fixRationale: note, sentence: sfield, ...verbThread ? { threadId: verbThread } : {} });
49898
+ }
49899
+ }
49835
49900
  FLAG_RE.lastIndex = 0;
49836
49901
  let f;
49837
49902
  while ((f = FLAG_RE.exec(sentence)) !== null) {
@@ -50260,7 +50325,15 @@ function harvestInlineTags(store, text, opts) {
50260
50325
  }
50261
50326
  return plan;
50262
50327
  }
50263
- var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
50328
+ var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
50329
+ "Problem",
50330
+ "Solution",
50331
+ "RootCause",
50332
+ "Pattern",
50333
+ "AntiPattern",
50334
+ "Technique",
50335
+ "Weakness"
50336
+ ]);
50264
50337
  function stampWitnessOrigin(items, origin) {
50265
50338
  if (!origin) return [...items];
50266
50339
  const o = digest({ p: origin }).slice(0, 12);
@@ -51238,6 +51311,7 @@ async function syncSkills(paths, client, seed, pins = []) {
51238
51311
  }
51239
51312
 
51240
51313
  // src/agent-skills.ts
51314
+ init_src2();
51241
51315
  import {
51242
51316
  cpSync,
51243
51317
  existsSync as existsSync15,
@@ -51270,14 +51344,34 @@ function deriveDescription(title, layer, body2) {
51270
51344
  const when = ` Apply when working on ${layer === "antipattern" ? "code that risks this pitfall" : "problems in this area"}.`;
51271
51345
  return `${lead}${lead.endsWith(".") ? "" : "."}${when}`.slice(0, 1024);
51272
51346
  }
51273
- function renderSkillMd(name2, description, body2) {
51347
+ function renderSkillMd(name2, description, body2, citeHandle) {
51348
+ const footer = citeHandle ? `
51349
+
51350
+ ---
51351
+ When this skill actually helps you, witness it inline in your reply: \`([${citeHandle}])\` \u2014 the citation is recorded and feeds this skill's reliability, so it keeps being served. Untagged help is invisible.
51352
+ ` : "\n";
51274
51353
  return `---
51275
51354
  name: ${name2}
51276
51355
  description: ${yamlScalar(description)}
51277
51356
  ---
51278
51357
 
51279
- ${body2.trimEnd()}
51280
- `;
51358
+ ${body2.trimEnd()}${footer}`;
51359
+ }
51360
+ var LAYER_TO_LABEL = {
51361
+ pattern: "Pattern",
51362
+ antipattern: "AntiPattern",
51363
+ technique: "Technique",
51364
+ weakness: "Weakness"
51365
+ };
51366
+ function skillHandleNodes(skills) {
51367
+ return skills.map((s) => ({
51368
+ id: s.id,
51369
+ label: LAYER_TO_LABEL[s.layer.toLowerCase()] ?? "Pattern",
51370
+ description: s.title
51371
+ }));
51372
+ }
51373
+ function skillCiteHandle(s) {
51374
+ return priorHandle({ id: s.id, description: s.title });
51281
51375
  }
51282
51376
  function reconcileNamespaced(dir, keep) {
51283
51377
  if (!existsSync15(dir)) return 0;
@@ -51335,7 +51429,7 @@ function emitAndProjectSkills(root, skills) {
51335
51429
  mkdirSync7(join19(agentsSkillsDir, slug2), { recursive: true });
51336
51430
  writeFileSync12(
51337
51431
  join19(agentsSkillsDir, slug2, "SKILL.md"),
51338
- renderSkillMd(slug2, description, body2),
51432
+ renderSkillMd(slug2, description, body2, skillCiteHandle(s)),
51339
51433
  "utf8"
51340
51434
  );
51341
51435
  emitted++;
@@ -51840,7 +51934,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51840
51934
  }
51841
51935
 
51842
51936
  // src/engine.ts
51843
- var DAEMON_VERSION = true ? "2.0.2-dev.135" : "2.0.0-alpha.0";
51937
+ var DAEMON_VERSION = true ? "2.0.2-dev.151" : "2.0.0-alpha.0";
51844
51938
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51845
51939
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51846
51940
  var GIT_OP_MUTE_MS = 4e3;
@@ -52906,7 +53000,9 @@ function createWorkspaceEngine(opts) {
52906
53000
  const pins = profile?.projectId ? await cloud.getSkillPins(profile.projectId).then((r) => r.pins).catch(() => []) : [];
52907
53001
  const result = await syncSkills(paths, cloud, skillSeed, pins);
52908
53002
  try {
52909
- emitAndProjectSkills(opts.workspaceRoot, emitInputsFromManifest(paths.configDir, paths.skillsManifest));
53003
+ const inputs = emitInputsFromManifest(paths.configDir, paths.skillsManifest);
53004
+ emitAndProjectSkills(opts.workspaceRoot, inputs);
53005
+ writePrimingHandles(join22(paths.configDir, "priming-handles.json"), skillHandleNodes(inputs));
52910
53006
  } catch (err2) {
52911
53007
  console.warn("[skills] agent-skills projection failed (non-fatal):", err2 instanceof Error ? err2.message : err2);
52912
53008
  }
@@ -54288,6 +54384,20 @@ async function startMultiDaemon(opts = {}) {
54288
54384
  let serverPending = null;
54289
54385
  const boundaryListeners = [];
54290
54386
  let boundaryFlushing = false;
54387
+ let boundaryFlushStartedAt = 0;
54388
+ let boundaryFlushStage = "";
54389
+ const beginFlush = (stage) => {
54390
+ boundaryFlushing = true;
54391
+ boundaryFlushStartedAt = Date.now();
54392
+ boundaryFlushStage = stage;
54393
+ };
54394
+ const flushStage = (stage) => {
54395
+ boundaryFlushStage = stage;
54396
+ };
54397
+ const endFlush = () => {
54398
+ boundaryFlushing = false;
54399
+ boundaryFlushStage = "";
54400
+ };
54291
54401
  const fireBoundary = () => {
54292
54402
  for (const l of boundaryListeners) {
54293
54403
  try {
@@ -54992,12 +55102,14 @@ async function startMultiDaemon(opts = {}) {
54992
55102
  },
54993
55103
  async sessionBoundaryFlush() {
54994
55104
  if (boundaryFlushing) return { uploaded: 0, written: 0, pruned: 0, skipped: "in-flight" };
54995
- boundaryFlushing = true;
55105
+ beginFlush("embed");
54996
55106
  try {
54997
55107
  for (const r of records) {
54998
55108
  await r.engine.embedSettled();
54999
55109
  }
55110
+ flushStage("instances");
55000
55111
  const inst = await daemon.syncInstancesPublic();
55112
+ flushStage("skills");
55001
55113
  const skills = await daemon.syncSkillsAll();
55002
55114
  return {
55003
55115
  uploaded: inst.uploaded,
@@ -55006,7 +55118,7 @@ async function startMultiDaemon(opts = {}) {
55006
55118
  ...inst.errors ? { errors: inst.errors } : {}
55007
55119
  };
55008
55120
  } finally {
55009
- boundaryFlushing = false;
55121
+ endFlush();
55010
55122
  }
55011
55123
  },
55012
55124
  onSessionBoundary(cb) {
@@ -55031,18 +55143,25 @@ async function startMultiDaemon(opts = {}) {
55031
55143
  }
55032
55144
  };
55033
55145
  app.post("/api/sync", async (c) => {
55034
- if (boundaryFlushing) return c.json({ skipped: "in-flight" }, 409);
55035
- boundaryFlushing = true;
55146
+ if (boundaryFlushing)
55147
+ return c.json(
55148
+ { skipped: "in-flight", stage: boundaryFlushStage, ageMs: Date.now() - boundaryFlushStartedAt },
55149
+ 409
55150
+ );
55151
+ beginFlush("principles");
55036
55152
  try {
55037
55153
  const principles = await daemon.syncPrinciplesPublic();
55154
+ flushStage("triage");
55038
55155
  const triage = await daemon.syncTriagePublic();
55156
+ flushStage("instances");
55039
55157
  const instances = await daemon.syncInstancesPublic();
55158
+ flushStage("edge-backfill");
55040
55159
  const edgeBackfill = await daemon.backfillCausalEdges();
55041
55160
  return c.json({ principles, triage, instances, edgeBackfill });
55042
55161
  } catch (err2) {
55043
55162
  return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 500);
55044
55163
  } finally {
55045
- boundaryFlushing = false;
55164
+ endFlush();
55046
55165
  }
55047
55166
  });
55048
55167
  app.post("/api/tick", async (c) => {
@@ -57869,7 +57988,18 @@ async function cmdSync(arg) {
57869
57988
  signal: AbortSignal.timeout(15 * 6e4)
57870
57989
  });
57871
57990
  if (res.status === 409) {
57872
- console.log("sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started.");
57991
+ const body2 = await res.json().catch(() => null);
57992
+ const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
57993
+ const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
57994
+ const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
57995
+ console.log(
57996
+ ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}. It will finish on its own; nothing new was started.` : "sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started."
57997
+ );
57998
+ if (ageMs > 30 * 6e4) {
57999
+ console.log(
58000
+ " \u26A0 this pass has run past 30 min \u2014 if daemon.log shows no upload progress it may be stranded (a sleep/resume can do this). Restart with: errata stop && errata start"
58001
+ );
58002
+ }
57873
58003
  return;
57874
58004
  }
57875
58005
  if (!res.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.135",
3
+ "version": "2.0.2-dev.151",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {