@neat.is/core 0.9.6 → 0.9.7

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-XYAZOGEX.js";
5
+ } from "./chunk-JSUPWMH3.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-XOGTJVUM.js";
9
+ } from "./chunk-KZBFP7L6.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
package/dist/server.cjs CHANGED
@@ -7676,6 +7676,139 @@ function detectSymbolMismatches(graph, incidents) {
7676
7676
  }
7677
7677
  return out;
7678
7678
  }
7679
+ var OBSERVED_FAILING_ERROR_RATE = 0.5;
7680
+ var OBSERVED_FAILING_MIN_SPANS = 5;
7681
+ var OBSERVED_FAILING_CONFIDENCE = 0.6;
7682
+ function detectObservedFailingEdge(graph, bucket) {
7683
+ if (!bucket.extracted || !bucket.observed) return [];
7684
+ if (!OBSERVABLE_EDGE_TYPES.has(bucket.type)) return [];
7685
+ if (bucket.observed.provenance !== import_types9.Provenance.OBSERVED) return [];
7686
+ const signal = bucket.observed.signal;
7687
+ if (!signal) return [];
7688
+ const { spanCount, errorCount } = signal;
7689
+ if (spanCount < OBSERVED_FAILING_MIN_SPANS) return [];
7690
+ const errorRate = errorCount / spanCount;
7691
+ if (errorRate < OBSERVED_FAILING_ERROR_RATE) return [];
7692
+ const pct = Math.round(errorRate * 100);
7693
+ return [
7694
+ {
7695
+ type: "observed-failing",
7696
+ source: bucket.source,
7697
+ target: bucket.target,
7698
+ failureKind: "error-rate",
7699
+ provenance: import_types9.Provenance.INFERRED,
7700
+ edgeType: bucket.type,
7701
+ observed: bucket.observed,
7702
+ spanCount,
7703
+ errorCount,
7704
+ errorRate: clampConfidence(errorRate),
7705
+ confidence: OBSERVED_FAILING_CONFIDENCE,
7706
+ reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
7707
+ recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
7708
+ }
7709
+ ];
7710
+ }
7711
+ var OBSERVED_FAILURE_PATTERNS = [
7712
+ {
7713
+ kind: "connection-refused",
7714
+ patterns: [/\bECONNREFUSED\b/i, /\bconnection refused\b/i]
7715
+ },
7716
+ {
7717
+ kind: "deadline-exceeded",
7718
+ patterns: [/\bDEADLINE_EXCEEDED\b/i, /\bdeadline exceeded\b/i]
7719
+ },
7720
+ {
7721
+ kind: "timeout",
7722
+ patterns: [/\bETIMEDOUT\b/i, /\btimed[\s-]?out\b/i, /\btimeout\b/i]
7723
+ },
7724
+ {
7725
+ kind: "unavailable",
7726
+ patterns: [
7727
+ /\bUNAVAILABLE\b/i,
7728
+ /\bservice unavailable\b/i,
7729
+ /\bECONNRESET\b/i,
7730
+ /\bconnection reset\b/i,
7731
+ /\bno healthy upstream\b/i
7732
+ ]
7733
+ }
7734
+ ];
7735
+ function classifyObservedFailure(ev) {
7736
+ const haystack = [ev.errorType, ev.exceptionType, ev.errorMessage].filter((s) => typeof s === "string").join(" \n ");
7737
+ for (const entry of OBSERVED_FAILURE_PATTERNS) {
7738
+ for (const re of entry.patterns) {
7739
+ if (re.test(haystack)) return entry.kind;
7740
+ }
7741
+ }
7742
+ const code = ev.httpStatusCode;
7743
+ if (typeof code === "number" && code >= 500 && code < 600) return "server-error";
7744
+ return null;
7745
+ }
7746
+ function observedFailureLabel(kind) {
7747
+ switch (kind) {
7748
+ case "error-rate":
7749
+ return "a high error rate";
7750
+ case "connection-refused":
7751
+ return "a refused connection";
7752
+ case "deadline-exceeded":
7753
+ return "a deadline exceeded";
7754
+ case "timeout":
7755
+ return "a timeout";
7756
+ case "unavailable":
7757
+ return "an unavailable or reset connection";
7758
+ case "server-error":
7759
+ return "a server error (5xx)";
7760
+ }
7761
+ }
7762
+ function detectObservedFailingIncidents(graph, incidents) {
7763
+ const groups = /* @__PURE__ */ new Map();
7764
+ for (const ev of incidents) {
7765
+ const kind = classifyObservedFailure(ev);
7766
+ if (!kind) continue;
7767
+ const locus = symbolLocus(graph, ev);
7768
+ if (!locus) continue;
7769
+ const key = `${locus.node}|${kind}`;
7770
+ const existing = groups.get(key);
7771
+ if (!existing) {
7772
+ groups.set(key, {
7773
+ node: locus.node,
7774
+ kind,
7775
+ ...locus.location ? { location: locus.location } : {},
7776
+ latest: ev,
7777
+ count: 1,
7778
+ ...typeof ev.httpStatusCode === "number" ? { httpStatusCode: ev.httpStatusCode } : {}
7779
+ });
7780
+ } else {
7781
+ existing.count += 1;
7782
+ if (ev.timestamp.localeCompare(existing.latest.timestamp) > 0) {
7783
+ existing.latest = ev;
7784
+ if (locus.location) existing.location = locus.location;
7785
+ if (typeof ev.httpStatusCode === "number") existing.httpStatusCode = ev.httpStatusCode;
7786
+ }
7787
+ }
7788
+ }
7789
+ const out = [];
7790
+ for (const g of groups.values()) {
7791
+ const where = g.location ? ` at ${g.location}` : "";
7792
+ const label = observedFailureLabel(g.kind);
7793
+ const times = g.count > 1 ? ` (${g.count} recorded incidents)` : " (1 recorded incident)";
7794
+ out.push({
7795
+ type: "observed-failing",
7796
+ source: g.node,
7797
+ target: g.node,
7798
+ failureKind: g.kind,
7799
+ provenance: import_types9.Provenance.INFERRED,
7800
+ ...g.location ? { location: g.location } : {},
7801
+ incidentId: g.latest.id,
7802
+ errorMessage: g.latest.errorMessage,
7803
+ incidentCount: g.count,
7804
+ ...typeof g.httpStatusCode === "number" ? { httpStatusCode: g.httpStatusCode } : {},
7805
+ confidence: OBSERVED_FAILING_CONFIDENCE,
7806
+ reason: `Code${where} declares an external call that production observes failing \u2014 ${g.latest.service} recorded ${label}: "${g.latest.errorMessage}"${times}. The declared call (EXTRACTED) and the observed failure (OBSERVED) diverge \u2014 the dependency is reached and does not work.`,
7807
+ recommendation: `The declared call is running in production and failing at the transport or response level (${label}). Check the target host/endpoint the code reaches, connectivity, and the call\u2019s deadline \u2014 a wrong or unreachable host and an exceeded deadline both land here.`
7808
+ });
7809
+ }
7810
+ return out;
7811
+ }
7679
7812
  function involvesNode(d, nodeId) {
7680
7813
  return d.source === nodeId || d.target === nodeId;
7681
7814
  }
@@ -7748,6 +7881,7 @@ function computeDivergences(graph, opts = {}) {
7748
7881
  const buckets2 = bucketEdges(graph);
7749
7882
  for (const bucket of buckets2.values()) {
7750
7883
  for (const d of detectMissingDivergences(graph, bucket)) all.push(d);
7884
+ for (const d of detectObservedFailingEdge(graph, bucket)) all.push(d);
7751
7885
  }
7752
7886
  graph.forEachNode((nodeId, attrs) => {
7753
7887
  const n = attrs;
@@ -7763,6 +7897,7 @@ function computeDivergences(graph, opts = {}) {
7763
7897
  });
7764
7898
  if (opts.incidents && opts.incidents.length > 0) {
7765
7899
  for (const d of detectSymbolMismatches(graph, opts.incidents)) all.push(d);
7900
+ for (const d of detectObservedFailingIncidents(graph, opts.incidents)) all.push(d);
7766
7901
  }
7767
7902
  const reconciled = suppressHostMismatchHalves(all);
7768
7903
  const dampened = dampenDeadCodeProbes(graph, buckets2, reconciled);
@@ -7789,7 +7924,12 @@ function computeDivergences(graph, opts = {}) {
7789
7924
  // type; this only breaks a confidence tie, and it orders last so a same-
7790
7925
  // confidence edge finding leads. In practice it carries the INFERRED grade
7791
7926
  // (0.6), so it sits below the high-confidence edge divergences already.
7792
- "observed-symbol-mismatch": 5
7927
+ "observed-symbol-mismatch": 5,
7928
+ // Behavioral-failure (ADR-220) also carries the INFERRED grade (0.6); it
7929
+ // orders last so at equal confidence the structural and symbol divergences
7930
+ // lead, per the contract ("rank below the definitive structural and symbol
7931
+ // divergences").
7932
+ "observed-failing": 6
7793
7933
  };
7794
7934
  filtered.sort((a, b) => {
7795
7935
  if (b.confidence !== a.confidence) return b.confidence - a.confidence;
@@ -7803,7 +7943,13 @@ function computeDivergences(graph, opts = {}) {
7803
7943
  if (ac !== bc) return ac.localeCompare(bc);
7804
7944
  const asym = "symbol" in a && a.symbol ? a.symbol : "";
7805
7945
  const bsym = "symbol" in b && b.symbol ? b.symbol : "";
7806
- return asym.localeCompare(bsym);
7946
+ if (asym !== bsym) return asym.localeCompare(bsym);
7947
+ const afk = "failureKind" in a && a.failureKind ? a.failureKind : "";
7948
+ const bfk = "failureKind" in b && b.failureKind ? b.failureKind : "";
7949
+ if (afk !== bfk) return afk.localeCompare(bfk);
7950
+ const aloc = "location" in a && a.location ? a.location : "";
7951
+ const bloc = "location" in b && b.location ? b.location : "";
7952
+ return aloc.localeCompare(bloc);
7807
7953
  });
7808
7954
  return import_types9.DivergenceResultSchema.parse({
7809
7955
  divergences: filtered,
@@ -17099,6 +17245,10 @@ function divergenceLine(d) {
17099
17245
  const member = d.symbol ? ` ${d.symbol}` : "";
17100
17246
  return `[${d.type}] ${d.source}${member}${at} \u2014 ${d.reason}`;
17101
17247
  }
17248
+ if (d.type === "observed-failing" && !d.edgeType) {
17249
+ const at = d.location ? ` at ${d.location}` : "";
17250
+ return `[${d.type}] ${d.source}${at} \u2014 ${d.reason}`;
17251
+ }
17102
17252
  return `[${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.reason}`;
17103
17253
  }
17104
17254
  function buildDivergenceSection(graph, node, incidents) {