@inerrata-corporation/errata 2.0.0-dev.84 → 2.0.0-dev.86

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.
@@ -54,6 +54,13 @@ var init_castalia = __esm({
54
54
  "Paradigm",
55
55
  "DataStructure",
56
56
  "AgentModel",
57
+ // An OS executable / CLI program (`powercfg`, `git`, `docker` — SP-tooltopic).
58
+ // NAME-keyed identity (`toolCanonicalId`), deliberately unversioned: `powercfg`
59
+ // is one node across Windows builds; OS scoping rides `RUNS_ON`, packaging rides
60
+ // `PROVIDED_BY → Package`. Registry-verified before public minting (tldr-pages
61
+ // is the deps.dev analog) — an unverifiable name (typo, org-internal CLI) is
62
+ // skipped, never minted, so the verifier doubles as the privacy membrane.
63
+ "Tool",
57
64
  // An OPAQUE anchor identity in a project's stratum (PJ-anchors · plan §6c).
58
65
  // Deliberately NOT one of the CODE_NODE_LABELS below — those are the local code
59
66
  // graph and never cross. A cloud `Symbol` carries the salted `sym_` id and a
@@ -98,6 +105,9 @@ var init_castalia = __esm({
98
105
  "PROVIDED_BY",
99
106
  "SUPPORTS",
100
107
  "RUNS_ON",
108
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor ("before calling X
109
+ // consider Y"). Hub-suppressed like OCCURS_IN — see EDGE_RULES for endpoints.
110
+ "CONCERNS",
101
111
  "IMPLEMENTATION_OF",
102
112
  "SUPERSEDED_BY",
103
113
  // canonical direction (older → newer) — Finding 1 fixed
@@ -248,6 +258,9 @@ var init_castalia = __esm({
248
258
  PROVIDED_BY: 0.4,
249
259
  SUPPORTS: 0.4,
250
260
  RUNS_ON: 0.4,
261
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor, hub-suppressed like
262
+ // OCCURS_IN — a `git` node collects hundreds of anchors like `typescript` does.
263
+ CONCERNS: 0.3,
251
264
  IMPLEMENTATION_OF: 0.5,
252
265
  SUPERSEDED_BY: 0,
253
266
  WRITTEN_IN: 0.3,
@@ -15173,6 +15186,15 @@ var init_edge_rules = __esm({
15173
15186
  OCCURS_IN: { to: ["Language"] },
15174
15187
  OCCURS_ON: { to: ["OperatingSystem"] },
15175
15188
  RUNS_ON: { to: ["OperatingSystem"] },
15189
+ // ── Tool spine (SP-tooltopic) ──
15190
+ // CONCERNS is the knowledge→executable anchor ("before calling X consider Y"):
15191
+ // a Claim/Problem/Solution about a Tool hangs off its Tool node so it can be
15192
+ // recalled at the moment of invocation. New type, so constraining it invents
15193
+ // nothing. NOTE: `PROVIDED_BY` (Tool → Package packaging join) is deliberately
15194
+ // NOT ruled here — the type pre-exists with broader extractor senses, and a
15195
+ // new rule on an old type would reject legitimate live flows (reject-never-flip
15196
+ // cuts both ways: only rule types you introduce or senses that are documented).
15197
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15176
15198
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15177
15199
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15178
15200
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -15247,6 +15269,9 @@ var init_wire = __esm({
15247
15269
  "Triage",
15248
15270
  "Claim",
15249
15271
  "AntiPattern",
15272
+ // Tool spine (SP-tooltopic): OS executables, minted by the stream-T importer
15273
+ // (tldr-verified) and referenced by agent CONCERNS anchors.
15274
+ "Tool",
15250
15275
  // PJ-anchors (§6c): the OPAQUE project anchor identity — salted `sym_` id +
15251
15276
  // coarse kind, no path, no qualified name. Writable ONLY on a batch with a
15252
15277
  // verified projectId; the ingest door rejects it outright otherwise, so its
package/errata.mjs CHANGED
@@ -104,6 +104,13 @@ var init_castalia = __esm({
104
104
  "Paradigm",
105
105
  "DataStructure",
106
106
  "AgentModel",
107
+ // An OS executable / CLI program (`powercfg`, `git`, `docker` — SP-tooltopic).
108
+ // NAME-keyed identity (`toolCanonicalId`), deliberately unversioned: `powercfg`
109
+ // is one node across Windows builds; OS scoping rides `RUNS_ON`, packaging rides
110
+ // `PROVIDED_BY → Package`. Registry-verified before public minting (tldr-pages
111
+ // is the deps.dev analog) — an unverifiable name (typo, org-internal CLI) is
112
+ // skipped, never minted, so the verifier doubles as the privacy membrane.
113
+ "Tool",
107
114
  // An OPAQUE anchor identity in a project's stratum (PJ-anchors · plan §6c).
108
115
  // Deliberately NOT one of the CODE_NODE_LABELS below — those are the local code
109
116
  // graph and never cross. A cloud `Symbol` carries the salted `sym_` id and a
@@ -148,6 +155,9 @@ var init_castalia = __esm({
148
155
  "PROVIDED_BY",
149
156
  "SUPPORTS",
150
157
  "RUNS_ON",
158
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor ("before calling X
159
+ // consider Y"). Hub-suppressed like OCCURS_IN — see EDGE_RULES for endpoints.
160
+ "CONCERNS",
151
161
  "IMPLEMENTATION_OF",
152
162
  "SUPERSEDED_BY",
153
163
  // canonical direction (older → newer) — Finding 1 fixed
@@ -338,6 +348,9 @@ var init_castalia = __esm({
338
348
  PROVIDED_BY: 0.4,
339
349
  SUPPORTS: 0.4,
340
350
  RUNS_ON: 0.4,
351
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor, hub-suppressed like
352
+ // OCCURS_IN — a `git` node collects hundreds of anchors like `typescript` does.
353
+ CONCERNS: 0.3,
341
354
  IMPLEMENTATION_OF: 0.5,
342
355
  SUPERSEDED_BY: 0,
343
356
  WRITTEN_IN: 0.3,
@@ -896,6 +909,12 @@ function genericCanonicalId(prefix, content) {
896
909
  function languageCanonicalId(name2) {
897
910
  return genericCanonicalId("Language", { name: name2.trim().toLowerCase() });
898
911
  }
912
+ function toolCanonicalId(name2) {
913
+ return genericCanonicalId("Tool", { name: name2.trim().toLowerCase() });
914
+ }
915
+ function operatingSystemCanonicalId(name2) {
916
+ return genericCanonicalId("OperatingSystem", { name: name2.trim().toLowerCase() });
917
+ }
899
918
  function packageCanonicalId(p) {
900
919
  const eco = p.ecosystem.toLowerCase();
901
920
  const name2 = eco === "npm" ? p.name.toLowerCase() : p.name;
@@ -15311,6 +15330,15 @@ var init_edge_rules = __esm({
15311
15330
  OCCURS_IN: { to: ["Language"] },
15312
15331
  OCCURS_ON: { to: ["OperatingSystem"] },
15313
15332
  RUNS_ON: { to: ["OperatingSystem"] },
15333
+ // ── Tool spine (SP-tooltopic) ──
15334
+ // CONCERNS is the knowledge→executable anchor ("before calling X consider Y"):
15335
+ // a Claim/Problem/Solution about a Tool hangs off its Tool node so it can be
15336
+ // recalled at the moment of invocation. New type, so constraining it invents
15337
+ // nothing. NOTE: `PROVIDED_BY` (Tool → Package packaging join) is deliberately
15338
+ // NOT ruled here — the type pre-exists with broader extractor senses, and a
15339
+ // new rule on an old type would reject legitimate live flows (reject-never-flip
15340
+ // cuts both ways: only rule types you introduce or senses that are documented).
15341
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15314
15342
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15315
15343
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15316
15344
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -15508,6 +15536,9 @@ var init_wire = __esm({
15508
15536
  "Triage",
15509
15537
  "Claim",
15510
15538
  "AntiPattern",
15539
+ // Tool spine (SP-tooltopic): OS executables, minted by the stream-T importer
15540
+ // (tldr-verified) and referenced by agent CONCERNS anchors.
15541
+ "Tool",
15511
15542
  // PJ-anchors (§6c): the OPAQUE project anchor identity — salted `sym_` id +
15512
15543
  // coarse kind, no path, no qualified name. Writable ONLY on a batch with a
15513
15544
  // verified projectId; the ingest door rejects it outright otherwise, so its
@@ -15913,6 +15944,7 @@ __export(src_exports, {
15913
15944
  normalizeRepoLocator: () => normalizeRepoLocator,
15914
15945
  normalizeSymbolPath: () => normalizeSymbolPath,
15915
15946
  npmPurl: () => npmPurl,
15947
+ operatingSystemCanonicalId: () => operatingSystemCanonicalId,
15916
15948
  packageCanonicalId: () => packageCanonicalId,
15917
15949
  pagerankEdgeTypes: () => pagerankEdgeTypes,
15918
15950
  parsePackageLockJson: () => parsePackageLockJson,
@@ -15931,6 +15963,7 @@ __export(src_exports, {
15931
15963
  summarizeIngestResult: () => summarizeIngestResult,
15932
15964
  summaryRejectionReason: () => summaryRejectionReason,
15933
15965
  toCloudAttrs: () => toCloudAttrs,
15966
+ toolCanonicalId: () => toolCanonicalId,
15934
15967
  validateCastaliaPayload: () => validateCastaliaPayload,
15935
15968
  vetSidecarSummaries: () => vetSidecarSummaries
15936
15969
  });
@@ -17773,6 +17806,59 @@ function linkProblemToPackages(store, problemId, text, ts, index) {
17773
17806
  }
17774
17807
  return { linked };
17775
17808
  }
17809
+ function buildLanguageIndex(store) {
17810
+ const idx = /* @__PURE__ */ new Map();
17811
+ for (const n of store.findNodesByLabel("Language")) {
17812
+ const name2 = String(n.attrs["name"] ?? n.description ?? "").trim().toLowerCase();
17813
+ if (name2) idx.set(name2, n);
17814
+ }
17815
+ return idx;
17816
+ }
17817
+ function matchLanguagesInText(text, index) {
17818
+ const hay = text.toLowerCase();
17819
+ const out2 = [];
17820
+ for (const [key, node2] of index) {
17821
+ if (key.length < LANGUAGE_MIN_LEN) continue;
17822
+ if (!hay.includes(key)) continue;
17823
+ const bounded = new RegExp(`(?<![\\w@/.\\-])${escapeRe3(key)}(?![\\w])`);
17824
+ if (bounded.test(hay)) out2.push(node2);
17825
+ }
17826
+ return out2;
17827
+ }
17828
+ function linkProblemToLanguages(store, problemId, text, ts, index) {
17829
+ const p = store.getNode(problemId);
17830
+ if (!p || p.label !== "Problem") return [];
17831
+ const idx = index ?? buildLanguageIndex(store);
17832
+ if (idx.size === 0) return [];
17833
+ const already = new Set(store.outEdges(problemId, ["OCCURS_IN"]).map((e) => e.to));
17834
+ const linked = [];
17835
+ for (const node2 of matchLanguagesInText(text, idx)) {
17836
+ if (already.has(node2.id)) continue;
17837
+ store.mergeEdge({
17838
+ id: `edge_${digest({ from: problemId, type: "OCCURS_IN", to: node2.id })}`.slice(0, 24),
17839
+ from: problemId,
17840
+ to: node2.id,
17841
+ type: "OCCURS_IN",
17842
+ confidence: 0.3,
17843
+ // below the structural walk's 0.4 — a mention, not an anchor walk
17844
+ extractionSource: "daemon-extracted",
17845
+ createdAt: ts,
17846
+ lastSeenAt: ts,
17847
+ navSuccesses: 0,
17848
+ navFailures: 0,
17849
+ attrs: { provisional: true, mention: true }
17850
+ });
17851
+ already.add(node2.id);
17852
+ linked.push(node2.id);
17853
+ }
17854
+ return linked;
17855
+ }
17856
+ function deriveContextFromStatement(store, problemId, text, ts, pkgIndex) {
17857
+ return {
17858
+ packages: linkProblemToPackages(store, problemId, text, ts, pkgIndex).linked,
17859
+ languages: linkProblemToLanguages(store, problemId, text, ts)
17860
+ };
17861
+ }
17776
17862
  function problemText(store, p) {
17777
17863
  const parts2 = [p.description];
17778
17864
  for (const e of store.outEdges(p.id, ["CAUSED_BY"])) {
@@ -17894,7 +17980,7 @@ function backfillProblemContext(store, ts) {
17894
17980
  }
17895
17981
  return report;
17896
17982
  }
17897
- var NAME_SEPARATOR, MIN_NAME_LEN;
17983
+ var NAME_SEPARATOR, MIN_NAME_LEN, LANGUAGE_MIN_LEN;
17898
17984
  var init_problem_package_link = __esm({
17899
17985
  "../../packages/local-graph/src/problem-package-link.ts"() {
17900
17986
  "use strict";
@@ -17902,6 +17988,7 @@ var init_problem_package_link = __esm({
17902
17988
  init_justification();
17903
17989
  NAME_SEPARATOR = /[@/\-._]/;
17904
17990
  MIN_NAME_LEN = 3;
17991
+ LANGUAGE_MIN_LEN = 4;
17905
17992
  }
17906
17993
  });
17907
17994
 
@@ -18191,7 +18278,7 @@ function solutionForProblem(store, problemId) {
18191
18278
  const e = store.outEdges(problemId, ["SOLVED_BY"])[0];
18192
18279
  return e ? e.to : null;
18193
18280
  }
18194
- function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts) {
18281
+ function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts, provenance = "edited") {
18195
18282
  const p = store.getNode(problemId);
18196
18283
  if (!p || p.label !== "Problem") return false;
18197
18284
  if (!ANCHORABLE_CODE.test(relPath)) return false;
@@ -18204,14 +18291,23 @@ function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts)
18204
18291
  from: problemId,
18205
18292
  to: file2.id,
18206
18293
  type: "ANCHORED_AT",
18207
- confidence: 0.3,
18208
- // soft: in-focus file is a heuristic, below F2's resolve-diff anchor (0.4)
18294
+ // witnessed = the agent's own declaration, F2-grade; edited = soft heuristic.
18295
+ confidence: provenance === "witnessed" ? 0.4 : 0.3,
18209
18296
  extractionSource: "agent-observed",
18210
18297
  createdAt: ts,
18211
18298
  lastSeenAt: ts,
18212
18299
  navSuccesses: 0,
18213
18300
  navFailures: 0,
18214
- attrs: { provisional: true, captureTime: true }
18301
+ attrs: { provisional: true, captureTime: true, anchorProvenance: provenance }
18302
+ });
18303
+ return true;
18304
+ }
18305
+ function recordAnchorHint(store, problemId, relPath, provenance, ts) {
18306
+ const p = store.getNode(problemId);
18307
+ if (!p || p.label !== "Problem") return false;
18308
+ if (p.attrs["anchorHint"]) return false;
18309
+ store.updateNode(problemId, {
18310
+ attrs: { ...p.attrs, anchorHint: { path: relPath, provenance, ts } }
18215
18311
  });
18216
18312
  return true;
18217
18313
  }
@@ -20300,6 +20396,7 @@ __export(src_exports2, {
20300
20396
  applyPulledPrinciples: () => applyPulledPrinciples,
20301
20397
  backfillProblemContext: () => backfillProblemContext,
20302
20398
  backfillProblemPackageLinks: () => backfillProblemPackageLinks,
20399
+ buildLanguageIndex: () => buildLanguageIndex,
20303
20400
  buildPackageIndex: () => buildPackageIndex,
20304
20401
  buildPrincipleSync: () => buildPrincipleSync,
20305
20402
  causalChain: () => causalChain,
@@ -20312,6 +20409,7 @@ __export(src_exports2, {
20312
20409
  creditAssignment: () => creditAssignment,
20313
20410
  crystallize: () => crystallize,
20314
20411
  deriveContextFromAnchors: () => deriveContextFromAnchors,
20412
+ deriveContextFromStatement: () => deriveContextFromStatement,
20315
20413
  designProblemId: () => designProblemId,
20316
20414
  detectLocalCommunities: () => detectLocalCommunities,
20317
20415
  edgeConductance: () => edgeConductance,
@@ -20327,8 +20425,10 @@ __export(src_exports2, {
20327
20425
  induceTriage: () => induceTriage,
20328
20426
  ingestDesignProblem: () => ingestDesignProblem,
20329
20427
  isPlaceholderStatement: () => isPlaceholderStatement,
20428
+ linkProblemToLanguages: () => linkProblemToLanguages,
20330
20429
  linkProblemToPackages: () => linkProblemToPackages,
20331
20430
  listNeedsRevisit: () => listNeedsRevisit,
20431
+ matchLanguagesInText: () => matchLanguagesInText,
20332
20432
  matchPackagesInText: () => matchPackagesInText,
20333
20433
  mergeCloudCounts: () => mergeCloudCounts,
20334
20434
  mergeDuplicateProblems: () => mergeDuplicateProblems,
@@ -20342,6 +20442,7 @@ __export(src_exports2, {
20342
20442
  priorsForFile: () => priorsForFile,
20343
20443
  propagateFactChange: () => propagateFactChange,
20344
20444
  propagateVersionChange: () => propagateVersionChange,
20445
+ recordAnchorHint: () => recordAnchorHint,
20345
20446
  recordClaim: () => recordClaim,
20346
20447
  recordMisreadPrior: () => recordMisreadPrior,
20347
20448
  recordTriageObservation: () => recordTriageObservation,
@@ -24568,7 +24669,7 @@ var init_agent_signals = __esm({
24568
24669
  };
24569
24670
  GLOSS = {
24570
24671
  prior: "a prior we showed you helped \u2014 or this work sits in a primed language/package",
24571
- problem: "you hit a real problem \u2014 incidental, or the one you're fixing",
24672
+ problem: "you hit a real problem \u2014 incidental, or the one you're fixing. About specific code? NAME the file in the tag: [!what's wrong @ src/file.ts:42] \u2014 we anchor to what you name, or to a file you edited this turn, never to one you merely read; a design/topic problem is fine with no @",
24572
24673
  fix: "your edit fixes a problem \u2014 cite the one we showed you, or just say how you fixed one you found yourself: (fix: what you changed)",
24573
24674
  constraint: "you made a design decision because requirements conflict or something's constrained \u2014 tag the tension, ESPECIALLY when your fix makes it 'only look' contradictory (a clean solution still hides a real trap the next agent needs)",
24574
24675
  triage: "you diagnosed a bug \u2014 SPLIT the symptom (what breaks \u2192 [!\u2026]) from the cause (the mechanism you'd change \u2192 (cause:\u2026)); if the line says WHY it breaks, it's a cause, not a problem",
@@ -41402,12 +41503,27 @@ function parseDesignResolutions(text) {
41402
41503
  return out2;
41403
41504
  }
41404
41505
  var FILE_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "MultiEdit", "NotebookEdit"]);
41506
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
41507
+ function isUserTurnBoundary(obj) {
41508
+ if (obj.type !== "user") return false;
41509
+ const content = obj.message?.content;
41510
+ if (typeof content === "string") return content.trim().length > 0;
41511
+ if (!Array.isArray(content)) return false;
41512
+ const blocks = content;
41513
+ if (blocks.some((b) => b["type"] === "tool_result")) return false;
41514
+ return blocks.some((b) => b["type"] === "text");
41515
+ }
41405
41516
  function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2e6) {
41406
41517
  const raw2 = readTail(transcriptPath, maxBytes);
41407
41518
  if (!raw2) return [];
41408
41519
  const turns = [];
41409
41520
  const lines = raw2.split(/\r?\n/);
41410
41521
  let lastFile;
41522
+ let lastFileIsWrite = false;
41523
+ let lastFileTurnSeq = 0;
41524
+ let editedFile;
41525
+ let editedFileTurnSeq = 0;
41526
+ let turnSeq = 0;
41411
41527
  for (let i2 = 0; i2 < lines.length; i2++) {
41412
41528
  const line = lines[i2];
41413
41529
  if (!line) continue;
@@ -41417,6 +41533,10 @@ function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2
41417
41533
  } catch {
41418
41534
  continue;
41419
41535
  }
41536
+ if (isUserTurnBoundary(obj)) {
41537
+ turnSeq++;
41538
+ continue;
41539
+ }
41420
41540
  if (obj.type !== "assistant" || !Array.isArray(obj.message?.content)) continue;
41421
41541
  const parts2 = [];
41422
41542
  for (const b of obj.message.content) {
@@ -41426,13 +41546,24 @@ function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2
41426
41546
  else if (b["type"] === "tool_use" && typeof b["name"] === "string" && FILE_TOOLS.has(b["name"])) {
41427
41547
  const input = b["input"];
41428
41548
  const fp = input && typeof input === "object" ? input["file_path"] : void 0;
41429
- if (typeof fp === "string" && fp) lastFile = fp;
41549
+ if (typeof fp === "string" && fp) {
41550
+ lastFile = fp;
41551
+ lastFileIsWrite = WRITE_TOOLS.has(b["name"]);
41552
+ lastFileTurnSeq = turnSeq;
41553
+ if (lastFileIsWrite) {
41554
+ editedFile = fp;
41555
+ editedFileTurnSeq = turnSeq;
41556
+ }
41557
+ }
41430
41558
  }
41431
41559
  }
41560
+ const provenance = lastFile ? lastFileTurnSeq === turnSeq ? lastFileIsWrite ? "edited" : "read" : "carried" : void 0;
41432
41561
  turns.push({
41433
41562
  uuid: String(obj.uuid ?? i2),
41434
41563
  text: parts2.join("\n\n"),
41435
- ...lastFile ? { workingFile: lastFile } : {}
41564
+ ...lastFile ? { workingFile: lastFile } : {},
41565
+ ...provenance ? { workingFileProvenance: provenance } : {},
41566
+ ...editedFile && editedFileTurnSeq === turnSeq ? { editedFile } : {}
41436
41567
  });
41437
41568
  }
41438
41569
  return turns;
@@ -43533,7 +43664,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
43533
43664
  }
43534
43665
 
43535
43666
  // src/engine.ts
43536
- var DAEMON_VERSION = true ? "2.0.0-dev.84" : "2.0.0-alpha.0";
43667
+ var DAEMON_VERSION = true ? "2.0.0-dev.86" : "2.0.0-alpha.0";
43537
43668
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
43538
43669
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
43539
43670
  var GIT_OP_MUTE_MS = 4e3;
@@ -44078,6 +44209,7 @@ function createWorkspaceEngine(opts) {
44078
44209
  for (const turn of items) {
44079
44210
  if (processedTurns++ > 0) await yieldToLoop();
44080
44211
  const turnFile = turn.workingFile ? toRel(turn.workingFile) : void 0;
44212
+ const editedTurnFile = turn.editedFile ? toRel(turn.editedFile) : void 0;
44081
44213
  if (opts.sharedStore) {
44082
44214
  try {
44083
44215
  abstracted += harvestAbstractionFences(opts.sharedStore, turn.text, t).applied;
@@ -44103,12 +44235,19 @@ function createWorkspaceEngine(opts) {
44103
44235
  });
44104
44236
  if (r.created || r.corroborated) minted++;
44105
44237
  sessionLastProblem.set(sessionId, designProblemId(flag.problem));
44106
- const fencedAnchor = turnFile ?? workingFiles.get(sessionId);
44107
- if ((r.created || r.corroborated) && fencedAnchor) {
44238
+ if (r.created || r.corroborated) {
44108
44239
  try {
44109
- if (anchorProblemToWorkingFile(store, r.problemId, fencedAnchor, profile.id, t)) {
44110
- deriveContextFromAnchors(store, r.problemId, t);
44240
+ if (editedTurnFile) {
44241
+ if (anchorProblemToWorkingFile(store, r.problemId, editedTurnFile, profile.id, t, "edited")) {
44242
+ deriveContextFromAnchors(store, r.problemId, t);
44243
+ }
44244
+ } else if (!flag.anchor) {
44245
+ const hintPath = turnFile ?? workingFiles.get(sessionId);
44246
+ if (hintPath) {
44247
+ recordAnchorHint(store, r.problemId, hintPath, turn.workingFileProvenance === "read" ? "read" : "carried", t);
44248
+ }
44111
44249
  }
44250
+ deriveContextFromStatement(store, r.problemId, flag.problem, t);
44112
44251
  } catch {
44113
44252
  }
44114
44253
  }
@@ -44167,10 +44306,13 @@ function createWorkspaceEngine(opts) {
44167
44306
  const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
44168
44307
  sessionThreads.set(sessionId, threads);
44169
44308
  for (const p of plan.problems) {
44170
- const anchorPath = p.location?.path ?? turnFile ?? wf;
44171
- if (anchorPath) {
44309
+ const anchorPath = p.location?.path ?? editedTurnFile;
44310
+ const anchorProvenance = p.location?.path ? "witnessed" : "edited";
44311
+ const hintPath = anchorPath ? void 0 : turnFile ?? wf;
44312
+ const dedupPath = anchorPath ?? hintPath;
44313
+ if (dedupPath) {
44172
44314
  try {
44173
- const dupId = reinforceSameAnchorProblem(store, anchorPath, profile.id, p.statement, sessionId, t);
44315
+ const dupId = reinforceSameAnchorProblem(store, dedupPath, profile.id, p.statement, sessionId, t);
44174
44316
  if (dupId) {
44175
44317
  minted++;
44176
44318
  sessionLastProblem.set(sessionId, dupId);
@@ -44191,13 +44333,16 @@ function createWorkspaceEngine(opts) {
44191
44333
  sessionLastProblem.set(sessionId, designProblemId(p.statement));
44192
44334
  inScopeProblemId = designProblemId(p.statement);
44193
44335
  if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
44194
- if (anchorPath) {
44195
- try {
44196
- if (anchorProblemToWorkingFile(store, r.problemId, anchorPath, profile.id, t)) {
44336
+ try {
44337
+ if (anchorPath) {
44338
+ if (anchorProblemToWorkingFile(store, r.problemId, anchorPath, profile.id, t, anchorProvenance)) {
44197
44339
  deriveContextFromAnchors(store, r.problemId, t);
44198
44340
  }
44199
- } catch {
44341
+ } else if (hintPath) {
44342
+ recordAnchorHint(store, r.problemId, hintPath, turn.workingFileProvenance === "read" ? "read" : "carried", t);
44200
44343
  }
44344
+ deriveContextFromStatement(store, r.problemId, p.statement, t);
44345
+ } catch {
44201
44346
  }
44202
44347
  }
44203
44348
  }
@@ -44218,7 +44363,7 @@ function createWorkspaceEngine(opts) {
44218
44363
  }
44219
44364
  if (resolveDesignProblemById(store, pid, fx.fixNote, t)) {
44220
44365
  resolved++;
44221
- const fxAnchor = turnFile ?? wf;
44366
+ const fxAnchor = editedTurnFile ?? turnFile ?? wf;
44222
44367
  if (fxAnchor) {
44223
44368
  try {
44224
44369
  anchorProblemToDiff(store, pid, [fxAnchor], profile.id, t);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.84",
3
+ "version": "2.0.0-dev.86",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -65,6 +65,13 @@ var init_castalia = __esm({
65
65
  "Paradigm",
66
66
  "DataStructure",
67
67
  "AgentModel",
68
+ // An OS executable / CLI program (`powercfg`, `git`, `docker` — SP-tooltopic).
69
+ // NAME-keyed identity (`toolCanonicalId`), deliberately unversioned: `powercfg`
70
+ // is one node across Windows builds; OS scoping rides `RUNS_ON`, packaging rides
71
+ // `PROVIDED_BY → Package`. Registry-verified before public minting (tldr-pages
72
+ // is the deps.dev analog) — an unverifiable name (typo, org-internal CLI) is
73
+ // skipped, never minted, so the verifier doubles as the privacy membrane.
74
+ "Tool",
68
75
  // An OPAQUE anchor identity in a project's stratum (PJ-anchors · plan §6c).
69
76
  // Deliberately NOT one of the CODE_NODE_LABELS below — those are the local code
70
77
  // graph and never cross. A cloud `Symbol` carries the salted `sym_` id and a
@@ -109,6 +116,9 @@ var init_castalia = __esm({
109
116
  "PROVIDED_BY",
110
117
  "SUPPORTS",
111
118
  "RUNS_ON",
119
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor ("before calling X
120
+ // consider Y"). Hub-suppressed like OCCURS_IN — see EDGE_RULES for endpoints.
121
+ "CONCERNS",
112
122
  "IMPLEMENTATION_OF",
113
123
  "SUPERSEDED_BY",
114
124
  // canonical direction (older → newer) — Finding 1 fixed
@@ -299,6 +309,9 @@ var init_castalia = __esm({
299
309
  PROVIDED_BY: 0.4,
300
310
  SUPPORTS: 0.4,
301
311
  RUNS_ON: 0.4,
312
+ // Tool spine (SP-tooltopic): knowledge → Tool anchor, hub-suppressed like
313
+ // OCCURS_IN — a `git` node collects hundreds of anchors like `typescript` does.
314
+ CONCERNS: 0.3,
302
315
  IMPLEMENTATION_OF: 0.5,
303
316
  SUPERSEDED_BY: 0,
304
317
  WRITTEN_IN: 0.3,
@@ -15028,6 +15041,15 @@ var init_edge_rules = __esm({
15028
15041
  OCCURS_IN: { to: ["Language"] },
15029
15042
  OCCURS_ON: { to: ["OperatingSystem"] },
15030
15043
  RUNS_ON: { to: ["OperatingSystem"] },
15044
+ // ── Tool spine (SP-tooltopic) ──
15045
+ // CONCERNS is the knowledge→executable anchor ("before calling X consider Y"):
15046
+ // a Claim/Problem/Solution about a Tool hangs off its Tool node so it can be
15047
+ // recalled at the moment of invocation. New type, so constraining it invents
15048
+ // nothing. NOTE: `PROVIDED_BY` (Tool → Package packaging join) is deliberately
15049
+ // NOT ruled here — the type pre-exists with broader extractor senses, and a
15050
+ // new rule on an old type would reject legitimate live flows (reject-never-flip
15051
+ // cuts both ways: only rule types you introduce or senses that are documented).
15052
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15031
15053
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15032
15054
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15033
15055
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -15102,6 +15124,9 @@ var init_wire = __esm({
15102
15124
  "Triage",
15103
15125
  "Claim",
15104
15126
  "AntiPattern",
15127
+ // Tool spine (SP-tooltopic): OS executables, minted by the stream-T importer
15128
+ // (tldr-verified) and referenced by agent CONCERNS anchors.
15129
+ "Tool",
15105
15130
  // PJ-anchors (§6c): the OPAQUE project anchor identity — salted `sym_` id +
15106
15131
  // coarse kind, no path, no qualified name. Writable ONLY on a batch with a
15107
15132
  // verified projectId; the ingest door rejects it outright otherwise, so its