@neat.is/core 0.9.13-dev.20260901 → 0.9.13
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-WE3AFQYL.js → chunk-BKSM2YLV.js} +35 -4
- package/dist/{chunk-WE3AFQYL.js.map → chunk-BKSM2YLV.js.map} +1 -1
- package/dist/{chunk-RBZNXA5L.js → chunk-RL6H3VVD.js} +56 -2
- package/dist/chunk-RL6H3VVD.js.map +1 -0
- package/dist/cli.cjs +538 -29
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +18 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +87 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +87 -3
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +55 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RBZNXA5L.js.map +0 -1
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BKSM2YLV.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-
|
|
40
|
+
} from "./chunk-RL6H3VVD.js";
|
|
41
41
|
import {
|
|
42
42
|
startOtelGrpcReceiver
|
|
43
43
|
} from "./chunk-ERE47MCR.js";
|
package/dist/neatd.cjs
CHANGED
|
@@ -1908,6 +1908,11 @@ function isSaturated(ctx) {
|
|
|
1908
1908
|
function isBoundaryTimeoutSymptom(ctx) {
|
|
1909
1909
|
return ctx.errorsEmittedHere > 0 && ctx.boundaryTimeout === true && ctx.errorsFromCallers === 0 && ctx.hasOutboundDeps === true && ctx.observedErroringDownstream !== true;
|
|
1910
1910
|
}
|
|
1911
|
+
var UNREACHABLE_INBOUND_ERROR_RATE = 0.5;
|
|
1912
|
+
var UNREACHABLE_MIN_INBOUND = 3;
|
|
1913
|
+
function isUnreachableSeed(ctx) {
|
|
1914
|
+
return ctx.callCount >= UNREACHABLE_MIN_INBOUND && ctx.errorsFromCallers > 0 && ctx.errorsFromCallers >= UNREACHABLE_INBOUND_ERROR_RATE * ctx.callCount && ctx.errorsEmittedHere === 0 && ctx.outboundVolume === 0;
|
|
1915
|
+
}
|
|
1911
1916
|
function classifyNode(ctx) {
|
|
1912
1917
|
if (ctx.errorsEmittedHere > 0) {
|
|
1913
1918
|
if ((ctx.stale || isSaturated(ctx)) && ctx.errorsFromCallers >= ctx.errorsEmittedHere) {
|
|
@@ -1916,6 +1921,7 @@ function classifyNode(ctx) {
|
|
|
1916
1921
|
if (isBoundaryTimeoutSymptom(ctx)) return "symptom-only";
|
|
1917
1922
|
return "primary-failure";
|
|
1918
1923
|
}
|
|
1924
|
+
if (isUnreachableSeed(ctx)) return "unreachable";
|
|
1919
1925
|
if (ctx.errorsFromCallers > 0) return "symptom-only";
|
|
1920
1926
|
return "unrelated";
|
|
1921
1927
|
}
|
|
@@ -2202,6 +2208,16 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
2202
2208
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
2203
2209
|
...lastProv ? { provenance: lastProv } : {}
|
|
2204
2210
|
});
|
|
2211
|
+
} else if (seedCtx && isUnreachableSeed(seedCtx)) {
|
|
2212
|
+
const name = displayNameOf(seedNode);
|
|
2213
|
+
candidates.push({
|
|
2214
|
+
node: seedNode,
|
|
2215
|
+
classification: "unreachable",
|
|
2216
|
+
reason: `${name} is unreachable: its callers' requests fail (${seedCtx.errorsFromCallers} erroring inbound calls) and it produced no telemetry of its own \u2014 no server spans, no outbound calls \u2014 so it never served. The failure is observed, but its cause is not in the trace: a startup failure, a crash before the first span, or an unschedulable / unhealthy pod. Inspect ${name}'s deploy state and logs \u2014 there is no code fault to find in the graph here.`,
|
|
2217
|
+
context: seedCtx,
|
|
2218
|
+
confidence: legacy.confidence,
|
|
2219
|
+
provenance: import_types.Provenance.OBSERVED
|
|
2220
|
+
});
|
|
2205
2221
|
} else if (seedCtx && isBoundaryTimeoutSymptom(seedCtx)) {
|
|
2206
2222
|
const pointer = structuralUpstreamPointer(graph, seedNode, incidents);
|
|
2207
2223
|
const seedName = displayNameOf(seedNode);
|
|
@@ -6249,6 +6265,14 @@ function mergeColumnsAt(graph, tableNodeId, columns, provenance, confidence) {
|
|
|
6249
6265
|
function mergeObservedColumns(graph, tableNodeId, columns) {
|
|
6250
6266
|
mergeColumnsAt(graph, tableNodeId, columns, import_types8.Provenance.OBSERVED, OBSERVED_COLUMN_CONFIDENCE);
|
|
6251
6267
|
}
|
|
6268
|
+
function mergeObservedDeployState(graph, serviceNodeId, state) {
|
|
6269
|
+
if (!graph.hasNode(serviceNodeId)) return;
|
|
6270
|
+
const attrs = {};
|
|
6271
|
+
if (typeof state.image === "string" && state.image.length > 0) attrs["observedImage"] = state.image;
|
|
6272
|
+
if (typeof state.readyReplicas === "number") attrs["observedReadyReplicas"] = state.readyReplicas;
|
|
6273
|
+
if (Object.keys(attrs).length === 0) return;
|
|
6274
|
+
graph.mergeNodeAttributes(serviceNodeId, attrs);
|
|
6275
|
+
}
|
|
6252
6276
|
function ensureDatabaseNode(graph, host, engine) {
|
|
6253
6277
|
const id = (0, import_types8.databaseId)(host);
|
|
6254
6278
|
if (graph.hasNode(id)) return id;
|
|
@@ -16968,6 +16992,25 @@ function detectColumnDrift(node) {
|
|
|
16968
16992
|
}
|
|
16969
16993
|
return out;
|
|
16970
16994
|
}
|
|
16995
|
+
var RECOMMENDATION_DEPLOY_IMAGE_MISMATCH = "The running image differs from the manifest-declared image \u2014 the rollout has not taken (the old ReplicaSet is still serving). Re-apply the deployment or investigate why the new image failed to roll out.";
|
|
16996
|
+
var DEPLOY_DIVERGENCE_CONFIDENCE = 0.9;
|
|
16997
|
+
function detectDeployDivergence(svc) {
|
|
16998
|
+
const out = [];
|
|
16999
|
+
if (typeof svc.declaredImage === "string" && typeof svc.observedImage === "string" && svc.declaredImage !== svc.observedImage) {
|
|
17000
|
+
out.push({
|
|
17001
|
+
type: "deploy-mismatch",
|
|
17002
|
+
kind: "image",
|
|
17003
|
+
source: svc.id,
|
|
17004
|
+
target: svc.id,
|
|
17005
|
+
declaredImage: svc.declaredImage,
|
|
17006
|
+
observedImage: svc.observedImage,
|
|
17007
|
+
confidence: DEPLOY_DIVERGENCE_CONFIDENCE,
|
|
17008
|
+
reason: `Service ${svc.name} declares image ${svc.declaredImage} but its running pods report ${svc.observedImage} \u2014 the deploy has not taken (the old version is still serving), so no incident fires.`,
|
|
17009
|
+
recommendation: RECOMMENDATION_DEPLOY_IMAGE_MISMATCH
|
|
17010
|
+
});
|
|
17011
|
+
}
|
|
17012
|
+
return out;
|
|
17013
|
+
}
|
|
16971
17014
|
var SYMBOL_MISMATCH_PATTERNS = [
|
|
16972
17015
|
{
|
|
16973
17016
|
// "'ListProductsResponse' object has no attribute 'products_list'" and kin.
|
|
@@ -17295,6 +17338,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
17295
17338
|
const svc = n;
|
|
17296
17339
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
17297
17340
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
17341
|
+
for (const d of detectDeployDivergence(svc)) all.push(d);
|
|
17298
17342
|
return;
|
|
17299
17343
|
}
|
|
17300
17344
|
if (n.type === import_types60.NodeType.InfraNode && n.kind === "sql-table") {
|
|
@@ -17335,7 +17379,12 @@ function computeDivergences(graph, opts = {}) {
|
|
|
17335
17379
|
// orders last so at equal confidence the structural and symbol divergences
|
|
17336
17380
|
// lead, per the contract ("rank below the definitive structural and symbol
|
|
17337
17381
|
// divergences").
|
|
17338
|
-
"observed-failing": 6
|
|
17382
|
+
"observed-failing": 6,
|
|
17383
|
+
// Deploy mismatch (ADR-225) is a definitive structural declared-vs-observed
|
|
17384
|
+
// divergence carrying high confidence (0.9), so the confidence sort already
|
|
17385
|
+
// places it among the structural leaders; this slot only breaks an exact
|
|
17386
|
+
// confidence tie and sits last as a stable tiebreaker.
|
|
17387
|
+
"deploy-mismatch": 7
|
|
17339
17388
|
};
|
|
17340
17389
|
filtered.sort((a, b) => {
|
|
17341
17390
|
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
@@ -18875,6 +18924,11 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
18875
18924
|
unresolved++;
|
|
18876
18925
|
continue;
|
|
18877
18926
|
}
|
|
18927
|
+
if (signal.deployState) {
|
|
18928
|
+
ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
18929
|
+
mergeObservedDeployState(graph, resolved.targetNodeId, signal.deployState);
|
|
18930
|
+
continue;
|
|
18931
|
+
}
|
|
18878
18932
|
if (signal.incident) {
|
|
18879
18933
|
ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
18880
18934
|
if (!ctx.errorsPath || !graph.hasNode(resolved.targetNodeId)) {
|
|
@@ -23326,6 +23380,7 @@ var IMAGE_PULL_REASONS = /* @__PURE__ */ new Set(["ImagePullBackOff", "ErrImageP
|
|
|
23326
23380
|
var CRASH_LOOP_REASON = "CrashLoopBackOff";
|
|
23327
23381
|
var FIELD_SEP5 = "\0";
|
|
23328
23382
|
var K8S_TARGET_KIND = "k8s-workload";
|
|
23383
|
+
var K8S_DEPLOY_STATE = "deploy-state";
|
|
23329
23384
|
function packK8sTargetName(identity) {
|
|
23330
23385
|
return [identity.serviceName, identity.fault].join(FIELD_SEP5);
|
|
23331
23386
|
}
|
|
@@ -23457,11 +23512,40 @@ function mapDeploymentToSignal(deployment, pods, config) {
|
|
|
23457
23512
|
}
|
|
23458
23513
|
};
|
|
23459
23514
|
}
|
|
23515
|
+
function observedDeployState(deployment, pods) {
|
|
23516
|
+
const readyReplicas = typeof deployment.status?.readyReplicas === "number" ? deployment.status.readyReplicas : 0;
|
|
23517
|
+
let image;
|
|
23518
|
+
for (const pod of podsForDeployment(deployment, pods)) {
|
|
23519
|
+
for (const cs of pod.status?.containerStatuses ?? []) {
|
|
23520
|
+
if (cs.state?.running && typeof cs.image === "string" && cs.image.length > 0) {
|
|
23521
|
+
image = cs.image;
|
|
23522
|
+
break;
|
|
23523
|
+
}
|
|
23524
|
+
}
|
|
23525
|
+
if (image) break;
|
|
23526
|
+
}
|
|
23527
|
+
return image !== void 0 ? { image, readyReplicas } : { readyReplicas };
|
|
23528
|
+
}
|
|
23529
|
+
function deployStateSignal(deployment, pods, config) {
|
|
23530
|
+
const name = deployment.metadata?.name;
|
|
23531
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
23532
|
+
const serviceName = serviceNameFor(deployment, config);
|
|
23533
|
+
return {
|
|
23534
|
+
targetKind: K8S_TARGET_KIND,
|
|
23535
|
+
targetName: packK8sTargetName({ serviceName, fault: K8S_DEPLOY_STATE }),
|
|
23536
|
+
callCount: 0,
|
|
23537
|
+
errorCount: 0,
|
|
23538
|
+
lastObservedIso: nowIso2(),
|
|
23539
|
+
deployState: observedDeployState(deployment, pods)
|
|
23540
|
+
};
|
|
23541
|
+
}
|
|
23460
23542
|
function mapWorkloadsToSignals(deployments, pods, config) {
|
|
23461
23543
|
const out = [];
|
|
23462
23544
|
for (const deployment of deployments) {
|
|
23463
|
-
const
|
|
23464
|
-
if (
|
|
23545
|
+
const deployState = deployStateSignal(deployment, pods, config);
|
|
23546
|
+
if (deployState) out.push(deployState);
|
|
23547
|
+
const incident = mapDeploymentToSignal(deployment, pods, config);
|
|
23548
|
+
if (incident) out.push(incident);
|
|
23465
23549
|
}
|
|
23466
23550
|
return out;
|
|
23467
23551
|
}
|