@neat.is/core 0.8.1-dev.20260816 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-OJ6BW63H.js";
4
+ } from "./chunk-RQTW7IV3.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,7 +37,7 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-3PXRQH53.js";
40
+ } from "./chunk-ZGZYZ33B.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
43
  } from "./chunk-FCO5Z3RW.js";
package/dist/neatd.cjs CHANGED
@@ -1427,7 +1427,11 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1427
1427
  }
1428
1428
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1429
1429
  function incidentMatchesNode(ev, nodeId) {
1430
- return ev.affectedNode === nodeId || ev.service === nodeId.replace(/^service:/, "");
1430
+ if (ev.affectedNode === nodeId) return true;
1431
+ if (ev.service === nodeId.replace(/^service:/, "")) return true;
1432
+ const sym = (0, import_types.parseSymbolId)(ev.affectedNode);
1433
+ if (sym && (0, import_types.fileId)(sym.service, sym.relPath) === nodeId) return true;
1434
+ return false;
1431
1435
  }
1432
1436
  function localizeFromIncidents(nodeId, incidents, errorEvent) {
1433
1437
  const pool = incidents && incidents.length > 0 ? incidents : errorEvent ? [errorEvent] : [];
@@ -1445,8 +1449,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
1445
1449
  const reasonParts = [`${latest.service}: ${latest.errorMessage}`];
1446
1450
  if (location) reasonParts.push(`surfaced at ${location}`);
1447
1451
  const rootCauseReason = `${reasonParts.join(" \u2014 ")}${tail}`;
1448
- const localizesToFile = latest.affectedNode !== nodeId && latest.affectedNode.startsWith("file:");
1449
- const fileNode = localizesToFile ? latest.affectedNode : void 0;
1452
+ const localizesFiner = latest.affectedNode !== nodeId && (latest.affectedNode.startsWith("file:") || latest.affectedNode.startsWith("symbol:"));
1453
+ const fileNode = localizesFiner ? latest.affectedNode : void 0;
1450
1454
  const fixRecommendation = location ? `Inspect ${location}${route ? ` handling ${route}` : ""}` : route ? `Inspect ${latest.service}'s handler for ${route}` : void 0;
1451
1455
  return {
1452
1456
  rootCauseNode: fileNode ?? nodeId,
@@ -1931,6 +1935,22 @@ function findLoadOrigin(graph, alertNodeId, incidents, now) {
1931
1935
  }
1932
1936
  return best ? { node: best.node, ctx: best.ctx } : null;
1933
1937
  }
1938
+ var LOAD_ORIGIN_CONFIDENCE_CEILING = 0.9;
1939
+ function loadOriginConfidence(originCtx, seedCtx) {
1940
+ const cleanSplit = seedCtx.errorsFromCallers > 0 && seedCtx.errorsEmittedHere === 0;
1941
+ const partialSplit = seedCtx.errorsFromCallers > seedCtx.errorsEmittedHere;
1942
+ let evidence = 0;
1943
+ evidence += cleanSplit ? 0.45 : partialSplit ? 0.2 : 0;
1944
+ evidence += seedCtx.stale ? 0.2 : 0;
1945
+ evidence += isSaturated(seedCtx) ? 0.15 : 0;
1946
+ evidence += (volumeWeight(originCtx.outboundVolume) - 0.5) * 0.4;
1947
+ const anchor = 0.45;
1948
+ const confidence = anchor + Math.min(1, evidence) * (LOAD_ORIGIN_CONFIDENCE_CEILING - anchor);
1949
+ return Math.max(0, Math.min(LOAD_ORIGIN_CONFIDENCE_CEILING, confidence));
1950
+ }
1951
+ function displayNameOf(nodeId) {
1952
+ return nodeId.replace(/^[a-z]+:/, "");
1953
+ }
1934
1954
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1935
1955
  const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1936
1956
  if (!legacy) return null;
@@ -1945,20 +1965,20 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
1945
1965
  const candidates = [];
1946
1966
  if (seedCtx && isVictimSeed(seedCtx)) {
1947
1967
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
1968
+ const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
1969
+ const satNote = isSaturated(seedCtx) ? `; its inbound p95 ${Math.round(seedCtx.latencyP95Ms)}ms is saturated` : "";
1948
1970
  if (origin) {
1949
- const path70 = findPath(graph, errorNodeId, origin.node, "up", ROOT_CAUSE_MAX_DEPTH);
1950
- const originConfidence = confidenceFromMix(path70?.edges ?? [], now);
1971
+ const originName = displayNameOf(origin.node);
1972
+ const seedName = displayNameOf(seedNode);
1951
1973
  candidates.push({
1952
1974
  node: origin.node,
1953
1975
  classification: "primary-failure",
1954
- reason: `Highest-volume upstream source (${origin.ctx.outboundVolume} observed outbound calls) driving a saturated/stale subgraph; the alerting path decays downstream into a starved victim rather than a fault at the callee.`,
1976
+ reason: `${originName} is the root cause: it overloads the failing subgraph, and the alerting node ${seedName} is a downstream symptom, not the fault \u2014 errors reach ${seedName} from its callers (${seedCtx.errorsFromCallers}), none originate there${staleNote}${satNote}. ${originName} is the upstream source driving that load (${origin.ctx.outboundVolume} observed outbound calls).`,
1955
1977
  context: origin.ctx,
1956
- confidence: Math.max(0.3, Math.min(0.8, originConfidence || 0.5)),
1978
+ confidence: loadOriginConfidence(origin.ctx, seedCtx),
1957
1979
  provenance: import_types.Provenance.OBSERVED
1958
1980
  });
1959
1981
  }
1960
- const staleNote = seedCtx.stale ? "; the node has gone STALE" : "";
1961
- const satNote = isSaturated(seedCtx) ? `; inbound p95 ${Math.round(seedCtx.latencyP95Ms)}ms is saturated` : "";
1962
1982
  candidates.push({
1963
1983
  node: seedNode,
1964
1984
  classification: "symptom-only",
@@ -1990,16 +2010,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
1990
2010
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
1991
2011
  }
1992
2012
  }
2013
+ const fixRecommendation = fixRecommendationForTop(top, seedNode, legacy);
1993
2014
  return import_types.RootCauseResultSchema.parse({
1994
2015
  rootCauseNode: top.node,
1995
2016
  rootCauseReason: top.reason,
1996
2017
  traversalPath,
1997
2018
  edgeProvenances,
1998
2019
  confidence: top.confidence,
1999
- ...legacy.fixRecommendation ? { fixRecommendation: legacy.fixRecommendation } : {},
2020
+ ...fixRecommendation ? { fixRecommendation } : {},
2000
2021
  candidates
2001
2022
  });
2002
2023
  }
2024
+ function fixRecommendationForTop(top, seedNode, legacy) {
2025
+ if (top.classification === "primary-failure" && top.node === seedNode) {
2026
+ return legacy.fixRecommendation;
2027
+ }
2028
+ const name = top.node.replace(/^service:/, "");
2029
+ if (top.classification === "primary-failure") {
2030
+ return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
2031
+ }
2032
+ return `${name} is a saturated/starved downstream victim; investigate its inbound load and upstream callers, not its own handler.`;
2033
+ }
2003
2034
 
2004
2035
  // src/policy.ts
2005
2036
  var DEFAULT_ACTION_BY_SEVERITY = {
@@ -5334,7 +5365,7 @@ function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line)
5334
5365
  }
5335
5366
  return sid;
5336
5367
  }
5337
- function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
5368
+ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite, mint = true) {
5338
5369
  const line = callSite.line;
5339
5370
  if (line === void 0) return fileNodeId;
5340
5371
  let sawSymbol = false;
@@ -5349,7 +5380,7 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
5349
5380
  }
5350
5381
  });
5351
5382
  if (candidates.length > 0) return pickContainingSymbol(candidates, callSite.fn);
5352
- if (sawSymbol && callSite.fn) {
5383
+ if (mint && sawSymbol && callSite.fn) {
5353
5384
  return ensureObservedSymbolNode(graph, fileNodeId, service, relPath, callSite.fn, line);
5354
5385
  }
5355
5386
  return fileNodeId;
@@ -5755,6 +5786,20 @@ function incidentAffectedNode(span, graph, scanPath) {
5755
5786
  const callSite = callSiteFromSpan(span, serviceNode, scanPath);
5756
5787
  if (callSite) {
5757
5788
  const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
5789
+ const canonicalService = serviceNode && typeof serviceNode.name === "string" ? serviceNode.name : span.service;
5790
+ if (graph) {
5791
+ const fusedFileId = (0, import_types8.fileId)(canonicalService, relPath);
5792
+ if (graph.hasNode(fusedFileId)) {
5793
+ return landObservedSymbol(
5794
+ graph,
5795
+ fusedFileId,
5796
+ canonicalService,
5797
+ relPath,
5798
+ { ...callSite, relPath },
5799
+ false
5800
+ );
5801
+ }
5802
+ }
5758
5803
  return (0, import_types8.fileId)(span.service, relPath);
5759
5804
  }
5760
5805
  return sid;
@@ -6118,7 +6163,11 @@ async function handleSpan(ctx, span) {
6118
6163
  ...span.exception?.type ? { exceptionType: span.exception.type } : {},
6119
6164
  ...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
6120
6165
  ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
6121
- affectedNode
6166
+ // Attribute to where the failure originated — the symbol / file / service
6167
+ // the throwing span named (incidentAffectedNode, ADR-191) — the same
6168
+ // source-based attribution the durable receiver write uses, not the
6169
+ // outbound edge target this span happened to mint.
6170
+ affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
6122
6171
  };
6123
6172
  await appendErrorEvent(ctx, ev);
6124
6173
  }