@neat.is/core 0.9.13 → 0.9.15-dev.20260902
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/{chunk-BKSM2YLV.js → chunk-NV4WWJSU.js} +45 -5
- package/dist/chunk-NV4WWJSU.js.map +1 -0
- package/dist/{chunk-RL6H3VVD.js → chunk-SSLPBQWY.js} +107 -37
- package/dist/chunk-SSLPBQWY.js.map +1 -0
- package/dist/cli.cjs +156 -58
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +138 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +138 -41
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +73 -36
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-BKSM2YLV.js.map +0 -1
- package/dist/chunk-RL6H3VVD.js.map +0 -1
|
@@ -935,6 +935,7 @@ import {
|
|
|
935
935
|
inferredEdgeId,
|
|
936
936
|
infraId,
|
|
937
937
|
observedEdgeId,
|
|
938
|
+
frontierEdgeId,
|
|
938
939
|
serviceId,
|
|
939
940
|
symbolId,
|
|
940
941
|
websocketChannelId
|
|
@@ -5117,6 +5118,32 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
5117
5118
|
}
|
|
5118
5119
|
return promoted;
|
|
5119
5120
|
}
|
|
5121
|
+
function stageFrontierEdge(graph, source, target, type) {
|
|
5122
|
+
if (!graph.hasNode(source) || !graph.hasNode(target)) return false;
|
|
5123
|
+
if (graph.hasEdge(observedEdgeId(source, target, type))) return false;
|
|
5124
|
+
const key = frontierEdgeId(source, target, type);
|
|
5125
|
+
if (graph.hasEdge(key)) return false;
|
|
5126
|
+
graph.addEdgeWithKey(key, source, target, {
|
|
5127
|
+
id: key,
|
|
5128
|
+
source,
|
|
5129
|
+
target,
|
|
5130
|
+
type,
|
|
5131
|
+
provenance: Provenance5.FRONTIER
|
|
5132
|
+
});
|
|
5133
|
+
return true;
|
|
5134
|
+
}
|
|
5135
|
+
function promoteFrontierEdges(graph) {
|
|
5136
|
+
const graduated = [];
|
|
5137
|
+
graph.forEachEdge((edgeId, attrs) => {
|
|
5138
|
+
const e = attrs;
|
|
5139
|
+
if (e.provenance !== Provenance5.FRONTIER) return;
|
|
5140
|
+
if (graph.hasEdge(observedEdgeId(e.source, e.target, e.type))) {
|
|
5141
|
+
graduated.push(edgeId);
|
|
5142
|
+
}
|
|
5143
|
+
});
|
|
5144
|
+
for (const edgeId of graduated) graph.dropEdge(edgeId);
|
|
5145
|
+
return graduated.length;
|
|
5146
|
+
}
|
|
5120
5147
|
function rewireFrontierEdges(graph, frontierId2, serviceId17) {
|
|
5121
5148
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
5122
5149
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
@@ -5225,6 +5252,7 @@ function startStalenessLoop(graph, options = {}) {
|
|
|
5225
5252
|
project: options.project
|
|
5226
5253
|
});
|
|
5227
5254
|
if (options.onPolicyTrigger) await options.onPolicyTrigger(graph);
|
|
5255
|
+
if (options.onReconcile) await options.onReconcile(graph);
|
|
5228
5256
|
} catch (err) {
|
|
5229
5257
|
console.error("staleness tick failed", err);
|
|
5230
5258
|
}
|
|
@@ -5368,6 +5396,7 @@ import {
|
|
|
5368
5396
|
RootCauseResultSchema,
|
|
5369
5397
|
TransitiveDependenciesResultSchema,
|
|
5370
5398
|
fileId as fileId4,
|
|
5399
|
+
frontierEdgeId as frontierEdgeId2,
|
|
5371
5400
|
parseSymbolId
|
|
5372
5401
|
} from "@neat.is/types";
|
|
5373
5402
|
var ROOT_CAUSE_MAX_DEPTH = 5;
|
|
@@ -5398,13 +5427,19 @@ function resolveOwningService(graph, nodeId) {
|
|
|
5398
5427
|
}
|
|
5399
5428
|
return null;
|
|
5400
5429
|
}
|
|
5430
|
+
function isFrontierEdge(e) {
|
|
5431
|
+
return e.provenance === Provenance6.FRONTIER;
|
|
5432
|
+
}
|
|
5433
|
+
function rankOf(p) {
|
|
5434
|
+
return p === Provenance6.FRONTIER ? -1 : PROV_RANK[p];
|
|
5435
|
+
}
|
|
5401
5436
|
function bestEdgeBySource(graph, edgeIds) {
|
|
5402
5437
|
const best = /* @__PURE__ */ new Map();
|
|
5403
5438
|
for (const id of edgeIds) {
|
|
5404
5439
|
const e = graph.getEdgeAttributes(id);
|
|
5405
|
-
if (isFrontierNode(graph, e.source)) continue;
|
|
5440
|
+
if (isFrontierNode(graph, e.source) || isFrontierEdge(e)) continue;
|
|
5406
5441
|
const cur = best.get(e.source);
|
|
5407
|
-
if (!cur ||
|
|
5442
|
+
if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
|
|
5408
5443
|
best.set(e.source, e);
|
|
5409
5444
|
}
|
|
5410
5445
|
}
|
|
@@ -5414,9 +5449,9 @@ function bestEdgeByTarget(graph, edgeIds) {
|
|
|
5414
5449
|
const best = /* @__PURE__ */ new Map();
|
|
5415
5450
|
for (const id of edgeIds) {
|
|
5416
5451
|
const e = graph.getEdgeAttributes(id);
|
|
5417
|
-
if (isFrontierNode(graph, e.target)) continue;
|
|
5452
|
+
if (isFrontierNode(graph, e.target) || isFrontierEdge(e)) continue;
|
|
5418
5453
|
const cur = best.get(e.target);
|
|
5419
|
-
if (!cur ||
|
|
5454
|
+
if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
|
|
5420
5455
|
best.set(e.target, e);
|
|
5421
5456
|
}
|
|
5422
5457
|
}
|
|
@@ -5426,7 +5461,10 @@ var PROVENANCE_CEILING = {
|
|
|
5426
5461
|
OBSERVED: 1,
|
|
5427
5462
|
INFERRED: 0.7,
|
|
5428
5463
|
EXTRACTED: 0.5,
|
|
5429
|
-
STALE: 0.3
|
|
5464
|
+
STALE: 0.3,
|
|
5465
|
+
// A FRONTIER surface (ADR-226) is a proposal about a cause NEAT could not observe
|
|
5466
|
+
// — the least-trusted claim it makes, below STALE (which was at least once seen).
|
|
5467
|
+
FRONTIER: 0.2
|
|
5430
5468
|
};
|
|
5431
5469
|
function volumeWeight(spanCount) {
|
|
5432
5470
|
if (!spanCount || spanCount <= 0) return 0.5;
|
|
@@ -5661,7 +5699,7 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
5661
5699
|
});
|
|
5662
5700
|
}
|
|
5663
5701
|
function isFailingCallEdge(e) {
|
|
5664
|
-
return e.type === EdgeType6.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
5702
|
+
return e.type === EdgeType6.CALLS && !isFrontierEdge(e) && (e.signal?.errorCount ?? 0) > 0;
|
|
5665
5703
|
}
|
|
5666
5704
|
function callSourcesForService(graph, serviceId17) {
|
|
5667
5705
|
const ids = [serviceId17];
|
|
@@ -5684,8 +5722,8 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
5684
5722
|
const ec = e.signal?.errorCount ?? 0;
|
|
5685
5723
|
const cc = curEdge.signal?.errorCount ?? 0;
|
|
5686
5724
|
if (ec !== cc) return ec > cc;
|
|
5687
|
-
if (
|
|
5688
|
-
return
|
|
5725
|
+
if (rankOf(e.provenance) !== rankOf(curEdge.provenance)) {
|
|
5726
|
+
return rankOf(e.provenance) > rankOf(curEdge.provenance);
|
|
5689
5727
|
}
|
|
5690
5728
|
return id < curId;
|
|
5691
5729
|
}
|
|
@@ -5736,11 +5774,11 @@ function dominantStaleCall(graph, serviceId17, visited) {
|
|
|
5736
5774
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
5737
5775
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5738
5776
|
if (e.type !== EdgeType6.CALLS) continue;
|
|
5739
|
-
if (isFrontierNode(graph, e.target)) continue;
|
|
5777
|
+
if (isFrontierNode(graph, e.target) || isFrontierEdge(e)) continue;
|
|
5740
5778
|
const owner = resolveOwningService(graph, e.target);
|
|
5741
5779
|
if (!owner || visited.has(owner.id)) continue;
|
|
5742
5780
|
const cur = bestByCallee.get(owner.id);
|
|
5743
|
-
if (!cur ||
|
|
5781
|
+
if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
|
|
5744
5782
|
bestByCallee.set(owner.id, e);
|
|
5745
5783
|
}
|
|
5746
5784
|
}
|
|
@@ -6013,6 +6051,7 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6013
6051
|
for (const edgeId of graph.inboundEdges(n)) {
|
|
6014
6052
|
const e = graph.getEdgeAttributes(edgeId);
|
|
6015
6053
|
if (e.type === EdgeType6.CONTAINS) continue;
|
|
6054
|
+
if (isFrontierEdge(e)) continue;
|
|
6016
6055
|
errorsFromCallers += e.signal?.errorCount ?? 0;
|
|
6017
6056
|
inboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
|
|
6018
6057
|
if (e.provenance === Provenance6.STALE) stale = true;
|
|
@@ -6026,6 +6065,7 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6026
6065
|
for (const edgeId of graph.outboundEdges(n)) {
|
|
6027
6066
|
const e = graph.getEdgeAttributes(edgeId);
|
|
6028
6067
|
if (e.type === EdgeType6.CONTAINS) continue;
|
|
6068
|
+
if (isFrontierEdge(e)) continue;
|
|
6029
6069
|
outboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
|
|
6030
6070
|
if (e.type === EdgeType6.CALLS) outboundErrors += e.signal?.errorCount ?? 0;
|
|
6031
6071
|
if (e.provenance === Provenance6.STALE) stale = true;
|
|
@@ -6309,22 +6349,44 @@ function routeMatches(declared, incident) {
|
|
|
6309
6349
|
const b = normalizeRoute(incident);
|
|
6310
6350
|
return a === b || a.startsWith(b) || b.startsWith(a);
|
|
6311
6351
|
}
|
|
6312
|
-
function
|
|
6313
|
-
const
|
|
6314
|
-
const callees = declaredOutboundCallees(graph, boundaryNode);
|
|
6352
|
+
function firstDeclaredCallee(graph, node, route) {
|
|
6353
|
+
const callees = declaredOutboundCallees(graph, node);
|
|
6315
6354
|
if (callees.length === 0) return null;
|
|
6316
6355
|
const narrowed = route !== void 0 ? callees.filter((c) => c.route !== void 0 && routeMatches(c.route, route)) : callees;
|
|
6317
6356
|
const chosen = narrowed.length === 1 ? narrowed[0] : callees.length === 1 ? callees[0] : null;
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
if (
|
|
6324
|
-
|
|
6325
|
-
|
|
6357
|
+
return chosen ? chosen.target : null;
|
|
6358
|
+
}
|
|
6359
|
+
function observedOutboundNextServices(graph, nodeId) {
|
|
6360
|
+
const targets = /* @__PURE__ */ new Set();
|
|
6361
|
+
for (const n of nodeScope(graph, nodeId)) {
|
|
6362
|
+
if (!graph.hasNode(n)) continue;
|
|
6363
|
+
for (const edgeId of graph.outboundEdges(n)) {
|
|
6364
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
6365
|
+
if (!DEP_EDGE_TYPES.has(e.type)) continue;
|
|
6366
|
+
if (e.provenance !== Provenance6.OBSERVED) continue;
|
|
6367
|
+
if (e.target !== nodeId) targets.add(e.target);
|
|
6368
|
+
}
|
|
6326
6369
|
}
|
|
6327
|
-
return
|
|
6370
|
+
return [...targets];
|
|
6371
|
+
}
|
|
6372
|
+
function resolveHangHop(graph, boundaryNode, incidents) {
|
|
6373
|
+
const route = boundaryIncidentRoute(boundaryNode, incidents);
|
|
6374
|
+
const own = firstDeclaredCallee(graph, boundaryNode, route);
|
|
6375
|
+
if (own) {
|
|
6376
|
+
return route !== void 0 ? { source: boundaryNode, target: own, route } : { source: boundaryNode, target: own };
|
|
6377
|
+
}
|
|
6378
|
+
const resolved = observedOutboundNextServices(graph, boundaryNode).map((n) => ({ source: n, target: firstDeclaredCallee(graph, n, route) })).filter((r) => r.target !== null);
|
|
6379
|
+
if (resolved.length === 1) {
|
|
6380
|
+
const hop = resolved[0];
|
|
6381
|
+
return route !== void 0 ? { ...hop, route } : hop;
|
|
6382
|
+
}
|
|
6383
|
+
return null;
|
|
6384
|
+
}
|
|
6385
|
+
function stagedHangProposal(graph, boundaryNode, incidents) {
|
|
6386
|
+
const hop = resolveHangHop(graph, boundaryNode, incidents);
|
|
6387
|
+
if (!hop) return null;
|
|
6388
|
+
if (!graph.hasEdge(frontierEdgeId2(hop.source, hop.target, EdgeType6.CALLS))) return null;
|
|
6389
|
+
return hop.route !== void 0 ? { node: hop.target, route: hop.route } : { node: hop.target };
|
|
6328
6390
|
}
|
|
6329
6391
|
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
6330
6392
|
const legacy = tagged.result;
|
|
@@ -6370,28 +6432,28 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
6370
6432
|
provenance: Provenance6.OBSERVED
|
|
6371
6433
|
});
|
|
6372
6434
|
} else if (seedCtx && isBoundaryTimeoutSymptom(seedCtx)) {
|
|
6373
|
-
const pointer = structuralUpstreamPointer(graph, seedNode, incidents);
|
|
6374
6435
|
const seedName = displayNameOf(seedNode);
|
|
6375
|
-
const
|
|
6376
|
-
|
|
6377
|
-
const causeName = displayNameOf(pointer.node);
|
|
6378
|
-
candidates.push({
|
|
6379
|
-
node: pointer.node,
|
|
6380
|
-
classification: "primary-failure",
|
|
6381
|
-
reason: `${causeName} is the likely root cause (structural, INFERRED \u2014 not observed): the boundary ${seedName} only timed out waiting, and the actual culprit hung without exporting a span, so this is walked from the declared call graph${routeNote}, not from runtime signal. Restore instrumentation / inspect ${causeName} to confirm.`,
|
|
6382
|
-
context: nodeContext(graph, pointer.node, incidents, now),
|
|
6383
|
-
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.EXTRACTED),
|
|
6384
|
-
provenance: Provenance6.INFERRED
|
|
6385
|
-
});
|
|
6386
|
-
}
|
|
6436
|
+
const proposal = stagedHangProposal(graph, seedNode, incidents);
|
|
6437
|
+
const routeNote = proposal?.route ? ` serving ${proposal.route}` : "";
|
|
6387
6438
|
candidates.push({
|
|
6388
6439
|
node: seedNode,
|
|
6389
6440
|
classification: "symptom-only",
|
|
6390
|
-
reason:
|
|
6441
|
+
reason: proposal ? `${seedName} timed out waiting on a downstream that hung and exported no span \u2014 a boundary reporting an upstream fault, not the fault itself. The cause is unobservable; NEAT proposes the staged FRONTIER surface below, a hypothesis to confirm, not an observed cause.` : `${seedName} timed out but nothing it can see downstream is erroring \u2014 the cause is downstream and unobserved (the culprit hung and exported no span). No FRONTIER surface${routeNote} is staged, so no cause is proposed; inspect ${seedName}'s declared downstream and restore instrumentation.`,
|
|
6391
6442
|
context: seedCtx,
|
|
6392
6443
|
confidence: Math.min(legacy.confidence, 0.3),
|
|
6393
6444
|
...lastProv ? { provenance: lastProv } : {}
|
|
6394
6445
|
});
|
|
6446
|
+
if (proposal) {
|
|
6447
|
+
const causeName = displayNameOf(proposal.node);
|
|
6448
|
+
candidates.push({
|
|
6449
|
+
node: proposal.node,
|
|
6450
|
+
classification: "primary-failure",
|
|
6451
|
+
reason: `${causeName} is the proposed hang cause (FRONTIER \u2014 a hypothesis, not observed): the boundary ${seedName} only timed out waiting and the culprit hung without exporting a span, so NEAT staged a FRONTIER surface on the unobservable hop${routeNote}. It graduates to OBSERVED when ${causeName} comes back; until then, inspect it / restore instrumentation to confirm, or it is culled.`,
|
|
6452
|
+
context: nodeContext(graph, proposal.node, incidents, now),
|
|
6453
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.FRONTIER),
|
|
6454
|
+
provenance: Provenance6.FRONTIER
|
|
6455
|
+
});
|
|
6456
|
+
}
|
|
6395
6457
|
} else if (staleChain) {
|
|
6396
6458
|
const culprit = staleChain.culprit;
|
|
6397
6459
|
const culpritName = displayNameOf(culprit);
|
|
@@ -6455,6 +6517,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
|
|
|
6455
6517
|
return legacy.fixRecommendation;
|
|
6456
6518
|
}
|
|
6457
6519
|
const name = top.node.replace(/^service:/, "");
|
|
6520
|
+
if (top.classification === "symptom-only" && top.context.boundaryTimeout === true) {
|
|
6521
|
+
return `${name} timed out waiting on a downstream that hung and exported no span \u2014 the cause is unobservable from traces. Inspect the FRONTIER surface NEAT staged on the unobservable hop (see candidates); it graduates to OBSERVED when that service recovers. Restore instrumentation on that path to confirm, rather than treating ${name} as the fault.`;
|
|
6522
|
+
}
|
|
6458
6523
|
if (top.provenance === Provenance6.STALE) {
|
|
6459
6524
|
return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
|
|
6460
6525
|
}
|
|
@@ -22708,7 +22773,10 @@ export {
|
|
|
22708
22773
|
attachGraphToEventBus,
|
|
22709
22774
|
confidenceForEdge,
|
|
22710
22775
|
getBlastRadius,
|
|
22776
|
+
nodeContext,
|
|
22777
|
+
isBoundaryTimeoutSymptom,
|
|
22711
22778
|
getRootCause,
|
|
22779
|
+
resolveHangHop,
|
|
22712
22780
|
evaluateAllPolicies,
|
|
22713
22781
|
loadPolicyFile,
|
|
22714
22782
|
PolicyViolationsLog,
|
|
@@ -22723,6 +22791,8 @@ export {
|
|
|
22723
22791
|
makeErrorSpanWriter,
|
|
22724
22792
|
handleSpan,
|
|
22725
22793
|
promoteFrontierNodes,
|
|
22794
|
+
stageFrontierEdge,
|
|
22795
|
+
promoteFrontierEdges,
|
|
22726
22796
|
makeSpanHandler,
|
|
22727
22797
|
markStaleEdges,
|
|
22728
22798
|
readStaleEvents,
|
|
@@ -22787,4 +22857,4 @@ export {
|
|
|
22787
22857
|
deprovisionConnector,
|
|
22788
22858
|
buildApi
|
|
22789
22859
|
};
|
|
22790
|
-
//# sourceMappingURL=chunk-
|
|
22860
|
+
//# sourceMappingURL=chunk-SSLPBQWY.js.map
|