@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/neatd.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  reconcileDaemonRecordSync,
4
4
  startDaemon
5
- } from "./chunk-BKSM2YLV.js";
5
+ } from "./chunk-NV4WWJSU.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-RL6H3VVD.js";
9
+ } from "./chunk-SSLPBQWY.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
package/dist/server.cjs CHANGED
@@ -6231,6 +6231,7 @@ function startStalenessLoop(graph, options = {}) {
6231
6231
  project: options.project
6232
6232
  });
6233
6233
  if (options.onPolicyTrigger) await options.onPolicyTrigger(graph);
6234
+ if (options.onReconcile) await options.onReconcile(graph);
6234
6235
  } catch (err) {
6235
6236
  console.error("staleness tick failed", err);
6236
6237
  }
@@ -6391,13 +6392,19 @@ function resolveOwningService(graph, nodeId) {
6391
6392
  }
6392
6393
  return null;
6393
6394
  }
6395
+ function isFrontierEdge(e) {
6396
+ return e.provenance === import_types8.Provenance.FRONTIER;
6397
+ }
6398
+ function rankOf(p) {
6399
+ return p === import_types8.Provenance.FRONTIER ? -1 : import_types8.PROV_RANK[p];
6400
+ }
6394
6401
  function bestEdgeBySource(graph, edgeIds) {
6395
6402
  const best = /* @__PURE__ */ new Map();
6396
6403
  for (const id of edgeIds) {
6397
6404
  const e = graph.getEdgeAttributes(id);
6398
- if (isFrontierNode(graph, e.source)) continue;
6405
+ if (isFrontierNode(graph, e.source) || isFrontierEdge(e)) continue;
6399
6406
  const cur = best.get(e.source);
6400
- if (!cur || import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[cur.provenance]) {
6407
+ if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
6401
6408
  best.set(e.source, e);
6402
6409
  }
6403
6410
  }
@@ -6407,9 +6414,9 @@ function bestEdgeByTarget(graph, edgeIds) {
6407
6414
  const best = /* @__PURE__ */ new Map();
6408
6415
  for (const id of edgeIds) {
6409
6416
  const e = graph.getEdgeAttributes(id);
6410
- if (isFrontierNode(graph, e.target)) continue;
6417
+ if (isFrontierNode(graph, e.target) || isFrontierEdge(e)) continue;
6411
6418
  const cur = best.get(e.target);
6412
- if (!cur || import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[cur.provenance]) {
6419
+ if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
6413
6420
  best.set(e.target, e);
6414
6421
  }
6415
6422
  }
@@ -6419,7 +6426,10 @@ var PROVENANCE_CEILING = {
6419
6426
  OBSERVED: 1,
6420
6427
  INFERRED: 0.7,
6421
6428
  EXTRACTED: 0.5,
6422
- STALE: 0.3
6429
+ STALE: 0.3,
6430
+ // A FRONTIER surface (ADR-226) is a proposal about a cause NEAT could not observe
6431
+ // — the least-trusted claim it makes, below STALE (which was at least once seen).
6432
+ FRONTIER: 0.2
6423
6433
  };
6424
6434
  function volumeWeight(spanCount) {
6425
6435
  if (!spanCount || spanCount <= 0) return 0.5;
@@ -6654,7 +6664,7 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
6654
6664
  });
6655
6665
  }
6656
6666
  function isFailingCallEdge(e) {
6657
- return e.type === import_types8.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
6667
+ return e.type === import_types8.EdgeType.CALLS && !isFrontierEdge(e) && (e.signal?.errorCount ?? 0) > 0;
6658
6668
  }
6659
6669
  function callSourcesForService(graph, serviceId17) {
6660
6670
  const ids = [serviceId17];
@@ -6677,8 +6687,8 @@ function failingCallDominates(e, id, curEdge, curId) {
6677
6687
  const ec = e.signal?.errorCount ?? 0;
6678
6688
  const cc = curEdge.signal?.errorCount ?? 0;
6679
6689
  if (ec !== cc) return ec > cc;
6680
- if (import_types8.PROV_RANK[e.provenance] !== import_types8.PROV_RANK[curEdge.provenance]) {
6681
- return import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[curEdge.provenance];
6690
+ if (rankOf(e.provenance) !== rankOf(curEdge.provenance)) {
6691
+ return rankOf(e.provenance) > rankOf(curEdge.provenance);
6682
6692
  }
6683
6693
  return id < curId;
6684
6694
  }
@@ -6729,11 +6739,11 @@ function dominantStaleCall(graph, serviceId17, visited) {
6729
6739
  for (const edgeId of graph.outboundEdges(src)) {
6730
6740
  const e = graph.getEdgeAttributes(edgeId);
6731
6741
  if (e.type !== import_types8.EdgeType.CALLS) continue;
6732
- if (isFrontierNode(graph, e.target)) continue;
6742
+ if (isFrontierNode(graph, e.target) || isFrontierEdge(e)) continue;
6733
6743
  const owner = resolveOwningService(graph, e.target);
6734
6744
  if (!owner || visited.has(owner.id)) continue;
6735
6745
  const cur = bestByCallee.get(owner.id);
6736
- if (!cur || import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[cur.provenance]) {
6746
+ if (!cur || rankOf(e.provenance) > rankOf(cur.provenance)) {
6737
6747
  bestByCallee.set(owner.id, e);
6738
6748
  }
6739
6749
  }
@@ -7006,6 +7016,7 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
7006
7016
  for (const edgeId of graph.inboundEdges(n)) {
7007
7017
  const e = graph.getEdgeAttributes(edgeId);
7008
7018
  if (e.type === import_types8.EdgeType.CONTAINS) continue;
7019
+ if (isFrontierEdge(e)) continue;
7009
7020
  errorsFromCallers += e.signal?.errorCount ?? 0;
7010
7021
  inboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
7011
7022
  if (e.provenance === import_types8.Provenance.STALE) stale = true;
@@ -7019,6 +7030,7 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
7019
7030
  for (const edgeId of graph.outboundEdges(n)) {
7020
7031
  const e = graph.getEdgeAttributes(edgeId);
7021
7032
  if (e.type === import_types8.EdgeType.CONTAINS) continue;
7033
+ if (isFrontierEdge(e)) continue;
7022
7034
  outboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
7023
7035
  if (e.type === import_types8.EdgeType.CALLS) outboundErrors += e.signal?.errorCount ?? 0;
7024
7036
  if (e.provenance === import_types8.Provenance.STALE) stale = true;
@@ -7302,22 +7314,44 @@ function routeMatches(declared, incident) {
7302
7314
  const b = normalizeRoute(incident);
7303
7315
  return a === b || a.startsWith(b) || b.startsWith(a);
7304
7316
  }
7305
- function structuralUpstreamPointer(graph, boundaryNode, incidents) {
7306
- const route = boundaryIncidentRoute(boundaryNode, incidents);
7307
- const callees = declaredOutboundCallees(graph, boundaryNode);
7317
+ function firstDeclaredCallee(graph, node, route) {
7318
+ const callees = declaredOutboundCallees(graph, node);
7308
7319
  if (callees.length === 0) return null;
7309
7320
  const narrowed = route !== void 0 ? callees.filter((c) => c.route !== void 0 && routeMatches(c.route, route)) : callees;
7310
7321
  const chosen = narrowed.length === 1 ? narrowed[0] : callees.length === 1 ? callees[0] : null;
7311
- if (!chosen) return null;
7312
- let node = chosen.target;
7313
- const seen = /* @__PURE__ */ new Set([boundaryNode, node]);
7314
- for (let depth = 0; depth < ROOT_CAUSE_MAX_DEPTH; depth++) {
7315
- const next = declaredOutboundCallees(graph, node);
7316
- if (next.length !== 1 || seen.has(next[0].target)) break;
7317
- node = next[0].target;
7318
- seen.add(node);
7322
+ return chosen ? chosen.target : null;
7323
+ }
7324
+ function observedOutboundNextServices(graph, nodeId) {
7325
+ const targets = /* @__PURE__ */ new Set();
7326
+ for (const n of nodeScope(graph, nodeId)) {
7327
+ if (!graph.hasNode(n)) continue;
7328
+ for (const edgeId of graph.outboundEdges(n)) {
7329
+ const e = graph.getEdgeAttributes(edgeId);
7330
+ if (!DEP_EDGE_TYPES.has(e.type)) continue;
7331
+ if (e.provenance !== import_types8.Provenance.OBSERVED) continue;
7332
+ if (e.target !== nodeId) targets.add(e.target);
7333
+ }
7334
+ }
7335
+ return [...targets];
7336
+ }
7337
+ function resolveHangHop(graph, boundaryNode, incidents) {
7338
+ const route = boundaryIncidentRoute(boundaryNode, incidents);
7339
+ const own = firstDeclaredCallee(graph, boundaryNode, route);
7340
+ if (own) {
7341
+ return route !== void 0 ? { source: boundaryNode, target: own, route } : { source: boundaryNode, target: own };
7342
+ }
7343
+ const resolved = observedOutboundNextServices(graph, boundaryNode).map((n) => ({ source: n, target: firstDeclaredCallee(graph, n, route) })).filter((r) => r.target !== null);
7344
+ if (resolved.length === 1) {
7345
+ const hop = resolved[0];
7346
+ return route !== void 0 ? { ...hop, route } : hop;
7319
7347
  }
7320
- return route !== void 0 ? { node, route } : { node };
7348
+ return null;
7349
+ }
7350
+ function stagedHangProposal(graph, boundaryNode, incidents) {
7351
+ const hop = resolveHangHop(graph, boundaryNode, incidents);
7352
+ if (!hop) return null;
7353
+ if (!graph.hasEdge((0, import_types8.frontierEdgeId)(hop.source, hop.target, import_types8.EdgeType.CALLS))) return null;
7354
+ return hop.route !== void 0 ? { node: hop.target, route: hop.route } : { node: hop.target };
7321
7355
  }
7322
7356
  function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
7323
7357
  const legacy = tagged.result;
@@ -7363,28 +7397,28 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
7363
7397
  provenance: import_types8.Provenance.OBSERVED
7364
7398
  });
7365
7399
  } else if (seedCtx && isBoundaryTimeoutSymptom(seedCtx)) {
7366
- const pointer = structuralUpstreamPointer(graph, seedNode, incidents);
7367
7400
  const seedName = displayNameOf(seedNode);
7368
- const routeNote = pointer?.route ? ` serving ${pointer.route}` : "";
7369
- if (pointer) {
7370
- const causeName = displayNameOf(pointer.node);
7371
- candidates.push({
7372
- node: pointer.node,
7373
- classification: "primary-failure",
7374
- 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.`,
7375
- context: nodeContext(graph, pointer.node, incidents, now),
7376
- confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.EXTRACTED),
7377
- provenance: import_types8.Provenance.INFERRED
7378
- });
7379
- }
7401
+ const proposal = stagedHangProposal(graph, seedNode, incidents);
7402
+ const routeNote = proposal?.route ? ` serving ${proposal.route}` : "";
7380
7403
  candidates.push({
7381
7404
  node: seedNode,
7382
7405
  classification: "symptom-only",
7383
- reason: pointer ? `${seedName} timed out waiting on a downstream that hung and exported nothing \u2014 a boundary reporting an upstream fault, not the fault itself.` : `${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 single declared callee${routeNote} resolves, so no confident cause is named; inspect ${seedName}'s declared downstream.`,
7406
+ 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.`,
7384
7407
  context: seedCtx,
7385
7408
  confidence: Math.min(legacy.confidence, 0.3),
7386
7409
  ...lastProv ? { provenance: lastProv } : {}
7387
7410
  });
7411
+ if (proposal) {
7412
+ const causeName = displayNameOf(proposal.node);
7413
+ candidates.push({
7414
+ node: proposal.node,
7415
+ classification: "primary-failure",
7416
+ 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.`,
7417
+ context: nodeContext(graph, proposal.node, incidents, now),
7418
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.FRONTIER),
7419
+ provenance: import_types8.Provenance.FRONTIER
7420
+ });
7421
+ }
7388
7422
  } else if (staleChain) {
7389
7423
  const culprit = staleChain.culprit;
7390
7424
  const culpritName = displayNameOf(culprit);
@@ -7448,6 +7482,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
7448
7482
  return legacy.fixRecommendation;
7449
7483
  }
7450
7484
  const name = top.node.replace(/^service:/, "");
7485
+ if (top.classification === "symptom-only" && top.context.boundaryTimeout === true) {
7486
+ 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.`;
7487
+ }
7451
7488
  if (top.provenance === import_types8.Provenance.STALE) {
7452
7489
  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}.`;
7453
7490
  }