@neat.is/core 0.3.3 → 0.3.5

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.cjs CHANGED
@@ -100,8 +100,8 @@ function reshapeGrpcRequest(req) {
100
100
  };
101
101
  }
102
102
  function resolveProtoRoot() {
103
- const here = import_node_path33.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
104
- return import_node_path33.default.resolve(here, "..", "proto");
103
+ const here = import_node_path34.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
104
+ return import_node_path34.default.resolve(here, "..", "proto");
105
105
  }
106
106
  function loadTraceService() {
107
107
  const protoRoot = resolveProtoRoot();
@@ -156,13 +156,13 @@ async function startOtelGrpcReceiver(opts) {
156
156
  })
157
157
  };
158
158
  }
159
- var import_node_url, import_node_path33, grpc, protoLoader;
159
+ var import_node_url, import_node_path34, grpc, protoLoader;
160
160
  var init_otel_grpc = __esm({
161
161
  "src/otel-grpc.ts"() {
162
162
  "use strict";
163
163
  init_cjs_shims();
164
164
  import_node_url = require("url");
165
- import_node_path33 = __toESM(require("path"), 1);
165
+ import_node_path34 = __toESM(require("path"), 1);
166
166
  grpc = __toESM(require("@grpc/grpc-js"), 1);
167
167
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
168
168
  init_otel();
@@ -259,10 +259,10 @@ function parseOtlpRequest(body) {
259
259
  }
260
260
  function loadProtobufDecoder() {
261
261
  if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
262
- const here = import_node_path34.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
263
- const protoRoot = import_node_path34.default.resolve(here, "..", "proto");
262
+ const here = import_node_path35.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
263
+ const protoRoot = import_node_path35.default.resolve(here, "..", "proto");
264
264
  const root = new import_protobufjs.default.Root();
265
- root.resolvePath = (_origin, target) => import_node_path34.default.resolve(protoRoot, target);
265
+ root.resolvePath = (_origin, target) => import_node_path35.default.resolve(protoRoot, target);
266
266
  root.loadSync(
267
267
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
268
268
  { keepCase: true }
@@ -362,12 +362,12 @@ function logSpanHandler(span) {
362
362
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
363
363
  );
364
364
  }
365
- var import_node_path34, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
365
+ var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
366
366
  var init_otel = __esm({
367
367
  "src/otel.ts"() {
368
368
  "use strict";
369
369
  init_cjs_shims();
370
- import_node_path34 = __toESM(require("path"), 1);
370
+ import_node_path35 = __toESM(require("path"), 1);
371
371
  import_node_url2 = require("url");
372
372
  import_fastify2 = __toESM(require("fastify"), 1);
373
373
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -794,11 +794,16 @@ init_cjs_shims();
794
794
  var import_types = require("@neat.is/types");
795
795
  var ROOT_CAUSE_MAX_DEPTH = 5;
796
796
  var BLAST_RADIUS_DEFAULT_DEPTH = 10;
797
+ function isFrontierNode(graph, nodeId) {
798
+ if (!graph.hasNode(nodeId)) return false;
799
+ const attrs = graph.getNodeAttributes(nodeId);
800
+ return attrs.type === import_types.NodeType.FrontierNode;
801
+ }
797
802
  function bestEdgeBySource(graph, edgeIds) {
798
803
  const best = /* @__PURE__ */ new Map();
799
804
  for (const id of edgeIds) {
800
805
  const e = graph.getEdgeAttributes(id);
801
- if (e.provenance === import_types.Provenance.FRONTIER) continue;
806
+ if (isFrontierNode(graph, e.source)) continue;
802
807
  const cur = best.get(e.source);
803
808
  if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
804
809
  best.set(e.source, e);
@@ -810,7 +815,7 @@ function bestEdgeByTarget(graph, edgeIds) {
810
815
  const best = /* @__PURE__ */ new Map();
811
816
  for (const id of edgeIds) {
812
817
  const e = graph.getEdgeAttributes(id);
813
- if (e.provenance === import_types.Provenance.FRONTIER) continue;
818
+ if (isFrontierNode(graph, e.target)) continue;
814
819
  const cur = best.get(e.target);
815
820
  if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
816
821
  best.set(e.target, e);
@@ -822,8 +827,7 @@ var PROVENANCE_CEILING = {
822
827
  OBSERVED: 1,
823
828
  INFERRED: 0.7,
824
829
  EXTRACTED: 0.5,
825
- STALE: 0.3,
826
- FRONTIER: 0.3
830
+ STALE: 0.3
827
831
  };
828
832
  function volumeWeight(spanCount) {
829
833
  if (!spanCount || spanCount <= 0) return 0.5;
@@ -876,19 +880,19 @@ function confidenceFromMix(edges, now = Date.now()) {
876
880
  function longestIncomingWalk(graph, start, maxDepth) {
877
881
  let best = { path: [start], edges: [] };
878
882
  const visited = /* @__PURE__ */ new Set([start]);
879
- function step(node, path36, edges) {
880
- if (path36.length > best.path.length) {
881
- best = { path: [...path36], edges: [...edges] };
883
+ function step(node, path37, edges) {
884
+ if (path37.length > best.path.length) {
885
+ best = { path: [...path37], edges: [...edges] };
882
886
  }
883
- if (path36.length - 1 >= maxDepth) return;
887
+ if (path37.length - 1 >= maxDepth) return;
884
888
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
885
889
  for (const [srcId, edge] of incoming) {
886
890
  if (visited.has(srcId)) continue;
887
891
  visited.add(srcId);
888
- path36.push(srcId);
892
+ path37.push(srcId);
889
893
  edges.push(edge);
890
- step(srcId, path36, edges);
891
- path36.pop();
894
+ step(srcId, path37, edges);
895
+ path37.pop();
892
896
  edges.pop();
893
897
  visited.delete(srcId);
894
898
  }
@@ -1107,8 +1111,8 @@ var evaluateStructural = ({
1107
1111
  for (const edgeId of graph.outboundEdges(id)) {
1108
1112
  const e = graph.getEdgeAttributes(edgeId);
1109
1113
  if (e.type !== rule.edgeType) continue;
1110
- if (e.provenance === import_types2.Provenance.FRONTIER) continue;
1111
1114
  const target = graph.getNodeAttributes(e.target);
1115
+ if (target.type === import_types2.NodeType.FrontierNode) continue;
1112
1116
  if (target.type === rule.toNodeType) {
1113
1117
  satisfied = true;
1114
1118
  break;
@@ -1227,8 +1231,8 @@ var evaluateCompatibility = ({
1227
1231
  for (const edgeId of graph.outboundEdges(svcId)) {
1228
1232
  const e = graph.getEdgeAttributes(edgeId);
1229
1233
  if (e.type !== import_types2.EdgeType.CONNECTS_TO) continue;
1230
- if (e.provenance === import_types2.Provenance.FRONTIER) continue;
1231
1234
  const dbAttrs = graph.getNodeAttributes(e.target);
1235
+ if (dbAttrs.type === import_types2.NodeType.FrontierNode) continue;
1232
1236
  if (dbAttrs.type !== import_types2.NodeType.DatabaseNode) continue;
1233
1237
  const db = dbAttrs;
1234
1238
  for (const pair of compatPairs()) {
@@ -1545,31 +1549,6 @@ function ensureFrontierNode(graph, host, ts) {
1545
1549
  graph.addNode(id, node);
1546
1550
  return id;
1547
1551
  }
1548
- function upsertFrontierEdge(graph, type, source, target, ts) {
1549
- const id = (0, import_types3.frontierEdgeId)(source, target, type);
1550
- if (graph.hasEdge(id)) {
1551
- const existing = graph.getEdgeAttributes(id);
1552
- const updated = {
1553
- ...existing,
1554
- provenance: import_types3.Provenance.FRONTIER,
1555
- lastObserved: ts,
1556
- callCount: (existing.callCount ?? 0) + 1
1557
- };
1558
- graph.replaceEdgeAttributes(id, updated);
1559
- return;
1560
- }
1561
- const edge = {
1562
- id,
1563
- source,
1564
- target,
1565
- type,
1566
- provenance: import_types3.Provenance.FRONTIER,
1567
- confidence: 1,
1568
- lastObserved: ts,
1569
- callCount: 1
1570
- };
1571
- graph.addEdgeWithKey(id, source, target, edge);
1572
- }
1573
1552
  function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
1574
1553
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
1575
1554
  const id = makeObservedEdgeId(type, source, target);
@@ -1577,35 +1556,37 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
1577
1556
  const existing = graph.getEdgeAttributes(id);
1578
1557
  const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1;
1579
1558
  const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
1559
+ const newSignal = {
1560
+ spanCount: newSpanCount,
1561
+ errorCount: newErrorCount,
1562
+ lastObservedAgeMs: 0
1563
+ };
1580
1564
  const updated = {
1581
1565
  ...existing,
1582
1566
  provenance: import_types3.Provenance.OBSERVED,
1583
1567
  lastObserved: ts,
1584
1568
  callCount: newSpanCount,
1585
- signal: {
1586
- spanCount: newSpanCount,
1587
- errorCount: newErrorCount,
1588
- lastObservedAgeMs: 0
1589
- },
1590
- confidence: 1
1569
+ signal: newSignal,
1570
+ confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
1591
1571
  };
1592
1572
  graph.replaceEdgeAttributes(id, updated);
1593
1573
  return { edge: updated, created: false };
1594
1574
  }
1575
+ const signal = {
1576
+ spanCount: 1,
1577
+ errorCount: isError ? 1 : 0,
1578
+ lastObservedAgeMs: 0
1579
+ };
1595
1580
  const edge = {
1596
1581
  id,
1597
1582
  source,
1598
1583
  target,
1599
1584
  type,
1600
1585
  provenance: import_types3.Provenance.OBSERVED,
1601
- confidence: 1,
1586
+ confidence: (0, import_types3.confidenceForObservedSignal)(signal),
1602
1587
  lastObserved: ts,
1603
1588
  callCount: 1,
1604
- signal: {
1605
- spanCount: 1,
1606
- errorCount: isError ? 1 : 0,
1607
- lastObservedAgeMs: 0
1608
- }
1589
+ signal
1609
1590
  };
1610
1591
  graph.addEdgeWithKey(id, source, target, edge);
1611
1592
  return { edge, created: true };
@@ -1653,18 +1634,28 @@ async function appendErrorEvent(ctx, ev) {
1653
1634
  await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(ctx.errorsPath), { recursive: true });
1654
1635
  await import_node_fs3.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
1655
1636
  }
1637
+ function sanitizeAttributes(attrs) {
1638
+ const out = {};
1639
+ for (const [k, v] of Object.entries(attrs)) {
1640
+ if (typeof v === "bigint") out[k] = v.toString();
1641
+ else out[k] = v;
1642
+ }
1643
+ return out;
1644
+ }
1656
1645
  function buildErrorEventForReceiver(span) {
1657
1646
  if (span.statusCode !== 2) return null;
1658
1647
  const ts = span.startTimeIso ?? (/* @__PURE__ */ new Date()).toISOString();
1648
+ const attrs = sanitizeAttributes(span.attributes);
1659
1649
  return {
1660
1650
  id: `${span.traceId}:${span.spanId}`,
1661
1651
  timestamp: ts,
1662
1652
  service: span.service,
1663
1653
  traceId: span.traceId,
1664
1654
  spanId: span.spanId,
1665
- errorMessage: span.exception?.message ?? span.errorMessage ?? span.name ?? "unknown error",
1655
+ errorMessage: span.exception?.message ?? span.name ?? "unknown error",
1666
1656
  ...span.exception?.type ? { exceptionType: span.exception.type } : {},
1667
1657
  ...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
1658
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
1668
1659
  affectedNode: (0, import_types3.serviceId)(span.service)
1669
1660
  };
1670
1661
  }
@@ -1715,11 +1706,16 @@ async function handleSpan(ctx, span) {
1715
1706
  affectedNode = targetId;
1716
1707
  resolvedViaAddress = true;
1717
1708
  } else if (!targetId) {
1718
- const frontierId2 = ensureFrontierNode(ctx.graph, host, ts);
1719
- if (ctx.graph.hasNode(sourceId)) {
1720
- upsertFrontierEdge(ctx.graph, import_types3.EdgeType.CALLS, sourceId, frontierId2, ts);
1721
- }
1722
- affectedNode = frontierId2;
1709
+ const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
1710
+ upsertObservedEdge(
1711
+ ctx.graph,
1712
+ import_types3.EdgeType.CALLS,
1713
+ sourceId,
1714
+ frontierNodeId,
1715
+ ts,
1716
+ isError
1717
+ );
1718
+ affectedNode = frontierNodeId;
1723
1719
  resolvedViaAddress = true;
1724
1720
  }
1725
1721
  }
@@ -1741,15 +1737,17 @@ async function handleSpan(ctx, span) {
1741
1737
  if (span.statusCode === 2) {
1742
1738
  stitchTrace(ctx.graph, sourceId, ts);
1743
1739
  if (ctx.writeErrorEventInline !== false) {
1740
+ const attrs = sanitizeAttributes(span.attributes);
1744
1741
  const ev = {
1745
1742
  id: `${span.traceId}:${span.spanId}`,
1746
1743
  timestamp: ts,
1747
1744
  service: span.service,
1748
1745
  traceId: span.traceId,
1749
1746
  spanId: span.spanId,
1750
- errorMessage: span.exception?.message ?? span.errorMessage ?? span.name ?? "unknown error",
1747
+ errorMessage: span.exception?.message ?? span.name ?? "unknown error",
1751
1748
  ...span.exception?.type ? { exceptionType: span.exception.type } : {},
1752
1749
  ...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
1750
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
1753
1751
  affectedNode
1754
1752
  };
1755
1753
  await appendErrorEvent(ctx, ev);
@@ -1805,8 +1803,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId3) {
1805
1803
  }
1806
1804
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
1807
1805
  graph.dropEdge(oldEdgeId);
1808
- const promotedProvenance = edge.provenance === import_types3.Provenance.FRONTIER ? import_types3.Provenance.OBSERVED : edge.provenance;
1809
- const newId = promotedProvenance === import_types3.Provenance.OBSERVED ? (0, import_types3.observedEdgeId)(newSource, newTarget, edge.type) : promotedProvenance === import_types3.Provenance.INFERRED ? (0, import_types3.inferredEdgeId)(newSource, newTarget, edge.type) : promotedProvenance === import_types3.Provenance.EXTRACTED ? (0, import_types3.extractedEdgeId)(newSource, newTarget, edge.type) : (0, import_types3.frontierEdgeId)(newSource, newTarget, edge.type);
1806
+ const newId = edge.provenance === import_types3.Provenance.OBSERVED ? (0, import_types3.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types3.Provenance.INFERRED ? (0, import_types3.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types3.extractedEdgeId)(newSource, newTarget, edge.type);
1810
1807
  if (graph.hasEdge(newId)) {
1811
1808
  const existing = graph.getEdgeAttributes(newId);
1812
1809
  const merged = {
@@ -1821,8 +1818,7 @@ function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
1821
1818
  ...edge,
1822
1819
  id: newId,
1823
1820
  source: newSource,
1824
- target: newTarget,
1825
- provenance: promotedProvenance
1821
+ target: newTarget
1826
1822
  };
1827
1823
  graph.addEdgeWithKey(newId, newSource, newTarget, rebuilt);
1828
1824
  }
@@ -2232,6 +2228,23 @@ async function writeExtractionErrors(errors, errorsPath) {
2232
2228
  const lines = errors.map((e) => JSON.stringify(e)).join("\n") + "\n";
2233
2229
  await import_node_fs7.promises.appendFile(errorsPath, lines, "utf8");
2234
2230
  }
2231
+ var droppedSink = [];
2232
+ function noteExtractedDropped(edge) {
2233
+ droppedSink.push(edge);
2234
+ }
2235
+ function drainDroppedExtracted() {
2236
+ return droppedSink.splice(0, droppedSink.length);
2237
+ }
2238
+ function isRejectedLogEnabled() {
2239
+ const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
2240
+ return raw === "1" || raw === "true";
2241
+ }
2242
+ async function writeRejectedExtracted(drops, rejectedPath) {
2243
+ if (drops.length === 0) return;
2244
+ await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
2245
+ const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
2246
+ await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
2247
+ }
2235
2248
 
2236
2249
  // src/extract/services.ts
2237
2250
  var DEFAULT_SCAN_DEPTH = 5;
@@ -3170,6 +3183,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
3170
3183
  target: dbNode.id,
3171
3184
  type: import_types7.EdgeType.CONNECTS_TO,
3172
3185
  provenance: import_types7.Provenance.EXTRACTED,
3186
+ confidence: (0, import_types7.confidenceForExtracted)("structural"),
3173
3187
  // ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
3174
3188
  // Ghost-edge cleanup keys retirement on this; the conditional
3175
3189
  // sourceFile spread that used to live here was a v0.1.x leftover.
@@ -3244,6 +3258,7 @@ async function addConfigNodes(graph, services, scanPath) {
3244
3258
  target: node.id,
3245
3259
  type: import_types8.EdgeType.CONFIGURED_BY,
3246
3260
  provenance: import_types8.Provenance.EXTRACTED,
3261
+ confidence: (0, import_types8.confidenceForExtracted)("structural"),
3247
3262
  evidence: { file: relPath.split(import_node_path18.default.sep).join("/") }
3248
3263
  };
3249
3264
  if (!graph.hasEdge(edge.id)) {
@@ -3398,17 +3413,32 @@ async function addHttpCallEdges(graph, services) {
3398
3413
  for (const [targetId, evidenceFile] of seenTargets) {
3399
3414
  const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
3400
3415
  const line = lineOf(fileContent, `//${evidenceFile.host}`);
3416
+ const confidence = (0, import_types9.confidenceForExtracted)("hostname-shape-match");
3417
+ const ev = {
3418
+ file: import_node_path20.default.relative(service.dir, evidenceFile.file),
3419
+ line,
3420
+ snippet: snippet(fileContent, line)
3421
+ };
3422
+ const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS);
3423
+ if (!(0, import_types9.passesExtractedFloor)(confidence)) {
3424
+ noteExtractedDropped({
3425
+ source: service.node.id,
3426
+ target: targetId,
3427
+ type: import_types9.EdgeType.CALLS,
3428
+ confidence,
3429
+ confidenceKind: "hostname-shape-match",
3430
+ evidence: ev
3431
+ });
3432
+ continue;
3433
+ }
3401
3434
  const edge = {
3402
- id: (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS),
3435
+ id: edgeId,
3403
3436
  source: service.node.id,
3404
3437
  target: targetId,
3405
3438
  type: import_types9.EdgeType.CALLS,
3406
3439
  provenance: import_types9.Provenance.EXTRACTED,
3407
- evidence: {
3408
- file: import_node_path20.default.relative(service.dir, evidenceFile.file),
3409
- line,
3410
- snippet: snippet(fileContent, line)
3411
- }
3440
+ confidence,
3441
+ evidence: ev
3412
3442
  };
3413
3443
  if (!graph.hasEdge(edge.id)) {
3414
3444
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -3447,6 +3477,10 @@ function kafkaEndpointsFromFile(file, serviceDir) {
3447
3477
  name: topic,
3448
3478
  kind: "kafka-topic",
3449
3479
  edgeType,
3480
+ // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —
3481
+ // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
3482
+ // tier (ADR-066).
3483
+ confidenceKind: "verified-call-site",
3450
3484
  evidence: {
3451
3485
  file: import_node_path21.default.relative(serviceDir, file.path),
3452
3486
  line,
@@ -3479,6 +3513,10 @@ function redisEndpointsFromFile(file, serviceDir) {
3479
3513
  name: host,
3480
3514
  kind: "redis",
3481
3515
  edgeType: "CALLS",
3516
+ // `redis://host` URL literal — the scheme is structural support, but no
3517
+ // call expression is verified to wire it through. URL-with-structural-
3518
+ // support tier (ADR-066).
3519
+ confidenceKind: "url-with-structural-support",
3482
3520
  evidence: {
3483
3521
  file: import_node_path22.default.relative(serviceDir, file.path),
3484
3522
  line,
@@ -3520,6 +3558,10 @@ function awsEndpointsFromFile(file, serviceDir) {
3520
3558
  name,
3521
3559
  kind,
3522
3560
  edgeType: "CALLS",
3561
+ // SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName
3562
+ // literal — framework-aware recognizer, verified-call-site tier
3563
+ // (ADR-066).
3564
+ confidenceKind: "verified-call-site",
3523
3565
  evidence: {
3524
3566
  file: import_node_path23.default.relative(serviceDir, file.path),
3525
3567
  line,
@@ -3599,6 +3641,10 @@ function grpcEndpointsFromFile(file, serviceDir) {
3599
3641
  name,
3600
3642
  kind,
3601
3643
  edgeType: "CALLS",
3644
+ // `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import
3645
+ // context — import-aware classification per #238. Verified-call-site
3646
+ // tier (ADR-066).
3647
+ confidenceKind: "verified-call-site",
3602
3648
  evidence: {
3603
3649
  file: import_node_path24.default.relative(serviceDir, file.path),
3604
3650
  line,
@@ -3659,6 +3705,18 @@ async function addExternalEndpointEdges(graph, services) {
3659
3705
  const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, ep.infraId, edgeType);
3660
3706
  if (seenEdges.has(edgeId)) continue;
3661
3707
  seenEdges.add(edgeId);
3708
+ const confidence = (0, import_types14.confidenceForExtracted)(ep.confidenceKind);
3709
+ if (!(0, import_types14.passesExtractedFloor)(confidence)) {
3710
+ noteExtractedDropped({
3711
+ source: service.node.id,
3712
+ target: ep.infraId,
3713
+ type: edgeType,
3714
+ confidence,
3715
+ confidenceKind: ep.confidenceKind,
3716
+ evidence: ep.evidence
3717
+ });
3718
+ continue;
3719
+ }
3662
3720
  if (!graph.hasEdge(edgeId)) {
3663
3721
  const edge = {
3664
3722
  id: edgeId,
@@ -3666,6 +3724,7 @@ async function addExternalEndpointEdges(graph, services) {
3666
3724
  target: ep.infraId,
3667
3725
  type: edgeType,
3668
3726
  provenance: import_types14.Provenance.EXTRACTED,
3727
+ confidence,
3669
3728
  evidence: ep.evidence
3670
3729
  };
3671
3730
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -3785,6 +3844,7 @@ async function addComposeInfra(graph, scanPath, services) {
3785
3844
  target: targetId,
3786
3845
  type: import_types16.EdgeType.DEPENDS_ON,
3787
3846
  provenance: import_types16.Provenance.EXTRACTED,
3847
+ confidence: (0, import_types16.confidenceForExtracted)("structural"),
3788
3848
  evidence: { file: evidenceFile }
3789
3849
  };
3790
3850
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -3845,6 +3905,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
3845
3905
  target: node.id,
3846
3906
  type: import_types17.EdgeType.RUNS_ON,
3847
3907
  provenance: import_types17.Provenance.EXTRACTED,
3908
+ confidence: (0, import_types17.confidenceForExtracted)("structural"),
3848
3909
  evidence: {
3849
3910
  file: import_node_path26.default.relative(scanPath, dockerfilePath).split(import_node_path26.default.sep).join("/")
3850
3911
  }
@@ -3961,6 +4022,9 @@ async function addInfra(graph, scanPath, services) {
3961
4022
  };
3962
4023
  }
3963
4024
 
4025
+ // src/extract/index.ts
4026
+ var import_node_path30 = __toESM(require("path"), 1);
4027
+
3964
4028
  // src/extract/retire.ts
3965
4029
  init_cjs_shims();
3966
4030
  var import_node_fs17 = require("fs");
@@ -4013,13 +4077,26 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4013
4077
  );
4014
4078
  }
4015
4079
  }
4080
+ const droppedEntries = drainDroppedExtracted();
4081
+ if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
4082
+ const rejectedPath = import_node_path30.default.join(import_node_path30.default.dirname(opts.errorsPath), "rejected.ndjson");
4083
+ try {
4084
+ await writeRejectedExtracted(droppedEntries, rejectedPath);
4085
+ } catch (err) {
4086
+ console.warn(
4087
+ `[neat] failed to write rejected extracted edges to ${rejectedPath}: ${err.message}`
4088
+ );
4089
+ }
4090
+ }
4016
4091
  const result = {
4017
4092
  nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
4018
4093
  edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
4019
4094
  frontiersPromoted,
4020
4095
  extractionErrors: errorEntries.length,
4021
4096
  errorEntries,
4022
- ghostsRetired
4097
+ ghostsRetired,
4098
+ extractedDropped: droppedEntries.length,
4099
+ droppedEntries
4023
4100
  };
4024
4101
  emitNeatEvent({
4025
4102
  type: "extraction-complete",
@@ -4037,8 +4114,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
4037
4114
  // src/persist.ts
4038
4115
  init_cjs_shims();
4039
4116
  var import_node_fs18 = require("fs");
4040
- var import_node_path30 = __toESM(require("path"), 1);
4041
- var SCHEMA_VERSION = 2;
4117
+ var import_node_path31 = __toESM(require("path"), 1);
4118
+ var import_types19 = require("@neat.is/types");
4119
+ var SCHEMA_VERSION = 3;
4042
4120
  function migrateV1ToV2(payload) {
4043
4121
  const nodes = payload.graph.nodes;
4044
4122
  if (Array.isArray(nodes)) {
@@ -4050,8 +4128,27 @@ function migrateV1ToV2(payload) {
4050
4128
  }
4051
4129
  return { ...payload, schemaVersion: 2 };
4052
4130
  }
4131
+ function migrateV2ToV3(payload) {
4132
+ const edges = payload.graph.edges;
4133
+ if (Array.isArray(edges)) {
4134
+ for (const edge of edges) {
4135
+ const attrs = edge.attributes;
4136
+ if (!attrs || attrs.provenance !== "FRONTIER") continue;
4137
+ attrs.provenance = import_types19.Provenance.OBSERVED;
4138
+ const type = typeof attrs.type === "string" ? attrs.type : void 0;
4139
+ const source = typeof attrs.source === "string" ? attrs.source : void 0;
4140
+ const target = typeof attrs.target === "string" ? attrs.target : void 0;
4141
+ if (type && source && target) {
4142
+ const newId = (0, import_types19.observedEdgeId)(source, target, type);
4143
+ attrs.id = newId;
4144
+ if (edge.key) edge.key = newId;
4145
+ }
4146
+ }
4147
+ }
4148
+ return { ...payload, schemaVersion: 3 };
4149
+ }
4053
4150
  async function ensureDir(filePath) {
4054
- await import_node_fs18.promises.mkdir(import_node_path30.default.dirname(filePath), { recursive: true });
4151
+ await import_node_fs18.promises.mkdir(import_node_path31.default.dirname(filePath), { recursive: true });
4055
4152
  }
4056
4153
  async function saveGraphToDisk(graph, outPath) {
4057
4154
  await ensureDir(outPath);
@@ -4076,6 +4173,9 @@ async function loadGraphFromDisk(graph, outPath) {
4076
4173
  if (payload.schemaVersion === 1) {
4077
4174
  payload = migrateV1ToV2(payload);
4078
4175
  }
4176
+ if (payload.schemaVersion === 2) {
4177
+ payload = migrateV2ToV3(payload);
4178
+ }
4079
4179
  if (payload.schemaVersion !== SCHEMA_VERSION) {
4080
4180
  throw new Error(
4081
4181
  `persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
@@ -4122,11 +4222,11 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
4122
4222
  init_cjs_shims();
4123
4223
  var import_fastify = __toESM(require("fastify"), 1);
4124
4224
  var import_cors = __toESM(require("@fastify/cors"), 1);
4125
- var import_types21 = require("@neat.is/types");
4225
+ var import_types22 = require("@neat.is/types");
4126
4226
 
4127
4227
  // src/divergences.ts
4128
4228
  init_cjs_shims();
4129
- var import_types19 = require("@neat.is/types");
4229
+ var import_types20 = require("@neat.is/types");
4130
4230
  function bucketKey(source, target, type) {
4131
4231
  return `${type}|${source}|${target}`;
4132
4232
  }
@@ -4134,25 +4234,22 @@ function bucketEdges(graph) {
4134
4234
  const buckets = /* @__PURE__ */ new Map();
4135
4235
  graph.forEachEdge((id, attrs) => {
4136
4236
  const e = attrs;
4137
- const parsed = (0, import_types19.parseEdgeId)(id);
4237
+ const parsed = (0, import_types20.parseEdgeId)(id);
4138
4238
  const provenance = parsed?.provenance ?? e.provenance;
4139
4239
  const key = bucketKey(e.source, e.target, e.type);
4140
4240
  const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
4141
4241
  switch (provenance) {
4142
- case import_types19.Provenance.EXTRACTED:
4242
+ case import_types20.Provenance.EXTRACTED:
4143
4243
  cur.extracted = e;
4144
4244
  break;
4145
- case import_types19.Provenance.OBSERVED:
4245
+ case import_types20.Provenance.OBSERVED:
4146
4246
  cur.observed = e;
4147
4247
  break;
4148
- case import_types19.Provenance.INFERRED:
4248
+ case import_types20.Provenance.INFERRED:
4149
4249
  cur.inferred = e;
4150
4250
  break;
4151
- case import_types19.Provenance.FRONTIER:
4152
- cur.frontier = e;
4153
- break;
4154
4251
  default:
4155
- if (e.provenance === import_types19.Provenance.STALE) cur.stale = e;
4252
+ if (e.provenance === import_types20.Provenance.STALE) cur.stale = e;
4156
4253
  }
4157
4254
  buckets.set(key, cur);
4158
4255
  });
@@ -4161,15 +4258,7 @@ function bucketEdges(graph) {
4161
4258
  function nodeIsFrontier(graph, nodeId) {
4162
4259
  if (!graph.hasNode(nodeId)) return false;
4163
4260
  const attrs = graph.getNodeAttributes(nodeId);
4164
- return attrs.type === import_types19.NodeType.FrontierNode;
4165
- }
4166
- function hasAnyObservedFromSource(graph, sourceId) {
4167
- if (!graph.hasNode(sourceId)) return false;
4168
- for (const edgeId of graph.outboundEdges(sourceId)) {
4169
- const e = graph.getEdgeAttributes(edgeId);
4170
- if (e.provenance === import_types19.Provenance.OBSERVED) return true;
4171
- }
4172
- return false;
4261
+ return attrs.type === import_types20.NodeType.FrontierNode;
4173
4262
  }
4174
4263
  function clampConfidence(n) {
4175
4264
  if (!Number.isFinite(n)) return 0;
@@ -4184,32 +4273,34 @@ function reasonForMissingExtracted(source, target, type) {
4184
4273
  var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
4185
4274
  var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
4186
4275
  var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
4276
+ function gradedConfidence(edge) {
4277
+ if (typeof edge.confidence === "number") return clampConfidence(edge.confidence);
4278
+ return clampConfidence(confidenceForEdge(edge));
4279
+ }
4187
4280
  function detectMissingDivergences(graph, bucket) {
4188
4281
  const out = [];
4189
4282
  if (bucket.extracted && !bucket.observed) {
4190
4283
  if (!nodeIsFrontier(graph, bucket.target)) {
4191
- const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
4192
4284
  out.push({
4193
4285
  type: "missing-observed",
4194
4286
  source: bucket.source,
4195
4287
  target: bucket.target,
4196
4288
  edgeType: bucket.type,
4197
4289
  extracted: bucket.extracted,
4198
- confidence: sourceHasTraffic ? 1 : 0.5,
4290
+ confidence: gradedConfidence(bucket.extracted),
4199
4291
  reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
4200
4292
  recommendation: RECOMMENDATION_MISSING_OBSERVED
4201
4293
  });
4202
4294
  }
4203
4295
  }
4204
4296
  if (bucket.observed && !bucket.extracted) {
4205
- const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
4206
4297
  out.push({
4207
4298
  type: "missing-extracted",
4208
4299
  source: bucket.source,
4209
4300
  target: bucket.target,
4210
4301
  edgeType: bucket.type,
4211
4302
  observed: bucket.observed,
4212
- confidence: cascaded,
4303
+ confidence: gradedConfidence(bucket.observed),
4213
4304
  reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
4214
4305
  recommendation: RECOMMENDATION_MISSING_EXTRACTED
4215
4306
  });
@@ -4228,7 +4319,7 @@ function declaredHostFor(svc) {
4228
4319
  function hasExtractedConfiguredBy(graph, svcId) {
4229
4320
  for (const edgeId of graph.outboundEdges(svcId)) {
4230
4321
  const e = graph.getEdgeAttributes(edgeId);
4231
- if (e.type === import_types19.EdgeType.CONFIGURED_BY && e.provenance === import_types19.Provenance.EXTRACTED) {
4322
+ if (e.type === import_types20.EdgeType.CONFIGURED_BY && e.provenance === import_types20.Provenance.EXTRACTED) {
4232
4323
  return true;
4233
4324
  }
4234
4325
  }
@@ -4241,10 +4332,10 @@ function detectHostMismatch(graph, svcId, svc) {
4241
4332
  const out = [];
4242
4333
  for (const edgeId of graph.outboundEdges(svcId)) {
4243
4334
  const edge = graph.getEdgeAttributes(edgeId);
4244
- if (edge.type !== import_types19.EdgeType.CONNECTS_TO) continue;
4245
- if (edge.provenance !== import_types19.Provenance.OBSERVED) continue;
4335
+ if (edge.type !== import_types20.EdgeType.CONNECTS_TO) continue;
4336
+ if (edge.provenance !== import_types20.Provenance.OBSERVED) continue;
4246
4337
  const target = graph.getNodeAttributes(edge.target);
4247
- if (target.type !== import_types19.NodeType.DatabaseNode) continue;
4338
+ if (target.type !== import_types20.NodeType.DatabaseNode) continue;
4248
4339
  const observedHost = target.host?.trim();
4249
4340
  if (!observedHost) continue;
4250
4341
  if (observedHost === declaredHost) continue;
@@ -4266,10 +4357,10 @@ function detectCompatDivergences(graph, svcId, svc) {
4266
4357
  const deps = svc.dependencies ?? {};
4267
4358
  for (const edgeId of graph.outboundEdges(svcId)) {
4268
4359
  const edge = graph.getEdgeAttributes(edgeId);
4269
- if (edge.type !== import_types19.EdgeType.CONNECTS_TO) continue;
4270
- if (edge.provenance !== import_types19.Provenance.OBSERVED) continue;
4360
+ if (edge.type !== import_types20.EdgeType.CONNECTS_TO) continue;
4361
+ if (edge.provenance !== import_types20.Provenance.OBSERVED) continue;
4271
4362
  const target = graph.getNodeAttributes(edge.target);
4272
- if (target.type !== import_types19.NodeType.DatabaseNode) continue;
4363
+ if (target.type !== import_types20.NodeType.DatabaseNode) continue;
4273
4364
  for (const pair of compatPairs()) {
4274
4365
  if (pair.engine !== target.engine) continue;
4275
4366
  const declared = deps[pair.driver];
@@ -4330,7 +4421,7 @@ function computeDivergences(graph, opts = {}) {
4330
4421
  }
4331
4422
  graph.forEachNode((nodeId, attrs) => {
4332
4423
  const n = attrs;
4333
- if (n.type !== import_types19.NodeType.ServiceNode) return;
4424
+ if (n.type !== import_types20.NodeType.ServiceNode) return;
4334
4425
  const svc = n;
4335
4426
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
4336
4427
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -4348,13 +4439,22 @@ function computeDivergences(graph, opts = {}) {
4348
4439
  const target = opts.node;
4349
4440
  filtered = filtered.filter((d) => involvesNode(d, target));
4350
4441
  }
4442
+ const TYPE_LEADERSHIP = {
4443
+ "missing-extracted": 0,
4444
+ "missing-observed": 1,
4445
+ "version-mismatch": 2,
4446
+ "host-mismatch": 3,
4447
+ "compat-violation": 4
4448
+ };
4351
4449
  filtered.sort((a, b) => {
4352
4450
  if (b.confidence !== a.confidence) return b.confidence - a.confidence;
4451
+ const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type];
4452
+ if (lead !== 0) return lead;
4353
4453
  if (a.type !== b.type) return a.type.localeCompare(b.type);
4354
4454
  if (a.source !== b.source) return a.source.localeCompare(b.source);
4355
4455
  return a.target.localeCompare(b.target);
4356
4456
  });
4357
- return import_types19.DivergenceResultSchema.parse({
4457
+ return import_types20.DivergenceResultSchema.parse({
4358
4458
  divergences: filtered,
4359
4459
  totalAffected: filtered.length,
4360
4460
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4440,23 +4540,23 @@ function canonicalJson(value) {
4440
4540
 
4441
4541
  // src/projects.ts
4442
4542
  init_cjs_shims();
4443
- var import_node_path31 = __toESM(require("path"), 1);
4543
+ var import_node_path32 = __toESM(require("path"), 1);
4444
4544
  function pathsForProject(project, baseDir) {
4445
4545
  if (project === DEFAULT_PROJECT) {
4446
4546
  return {
4447
- snapshotPath: import_node_path31.default.join(baseDir, "graph.json"),
4448
- errorsPath: import_node_path31.default.join(baseDir, "errors.ndjson"),
4449
- staleEventsPath: import_node_path31.default.join(baseDir, "stale-events.ndjson"),
4450
- embeddingsCachePath: import_node_path31.default.join(baseDir, "embeddings.json"),
4451
- policyViolationsPath: import_node_path31.default.join(baseDir, "policy-violations.ndjson")
4547
+ snapshotPath: import_node_path32.default.join(baseDir, "graph.json"),
4548
+ errorsPath: import_node_path32.default.join(baseDir, "errors.ndjson"),
4549
+ staleEventsPath: import_node_path32.default.join(baseDir, "stale-events.ndjson"),
4550
+ embeddingsCachePath: import_node_path32.default.join(baseDir, "embeddings.json"),
4551
+ policyViolationsPath: import_node_path32.default.join(baseDir, "policy-violations.ndjson")
4452
4552
  };
4453
4553
  }
4454
4554
  return {
4455
- snapshotPath: import_node_path31.default.join(baseDir, `${project}.json`),
4456
- errorsPath: import_node_path31.default.join(baseDir, `errors.${project}.ndjson`),
4457
- staleEventsPath: import_node_path31.default.join(baseDir, `stale-events.${project}.ndjson`),
4458
- embeddingsCachePath: import_node_path31.default.join(baseDir, `embeddings.${project}.json`),
4459
- policyViolationsPath: import_node_path31.default.join(baseDir, `policy-violations.${project}.ndjson`)
4555
+ snapshotPath: import_node_path32.default.join(baseDir, `${project}.json`),
4556
+ errorsPath: import_node_path32.default.join(baseDir, `errors.${project}.ndjson`),
4557
+ staleEventsPath: import_node_path32.default.join(baseDir, `stale-events.${project}.ndjson`),
4558
+ embeddingsCachePath: import_node_path32.default.join(baseDir, `embeddings.${project}.json`),
4559
+ policyViolationsPath: import_node_path32.default.join(baseDir, `policy-violations.${project}.ndjson`)
4460
4560
  };
4461
4561
  }
4462
4562
  var Projects = class {
@@ -4494,23 +4594,23 @@ var Projects = class {
4494
4594
  init_cjs_shims();
4495
4595
  var import_node_fs20 = require("fs");
4496
4596
  var import_node_os2 = __toESM(require("os"), 1);
4497
- var import_node_path32 = __toESM(require("path"), 1);
4498
- var import_types20 = require("@neat.is/types");
4597
+ var import_node_path33 = __toESM(require("path"), 1);
4598
+ var import_types21 = require("@neat.is/types");
4499
4599
  var LOCK_TIMEOUT_MS = 5e3;
4500
4600
  var LOCK_RETRY_MS = 50;
4501
4601
  function neatHome() {
4502
4602
  const override = process.env.NEAT_HOME;
4503
- if (override && override.length > 0) return import_node_path32.default.resolve(override);
4504
- return import_node_path32.default.join(import_node_os2.default.homedir(), ".neat");
4603
+ if (override && override.length > 0) return import_node_path33.default.resolve(override);
4604
+ return import_node_path33.default.join(import_node_os2.default.homedir(), ".neat");
4505
4605
  }
4506
4606
  function registryPath() {
4507
- return import_node_path32.default.join(neatHome(), "projects.json");
4607
+ return import_node_path33.default.join(neatHome(), "projects.json");
4508
4608
  }
4509
4609
  function registryLockPath() {
4510
- return import_node_path32.default.join(neatHome(), "projects.json.lock");
4610
+ return import_node_path33.default.join(neatHome(), "projects.json.lock");
4511
4611
  }
4512
4612
  async function normalizeProjectPath(input) {
4513
- const resolved = import_node_path32.default.resolve(input);
4613
+ const resolved = import_node_path33.default.resolve(input);
4514
4614
  try {
4515
4615
  return await import_node_fs20.promises.realpath(resolved);
4516
4616
  } catch {
@@ -4518,7 +4618,7 @@ async function normalizeProjectPath(input) {
4518
4618
  }
4519
4619
  }
4520
4620
  async function writeAtomically(target, contents) {
4521
- await import_node_fs20.promises.mkdir(import_node_path32.default.dirname(target), { recursive: true });
4621
+ await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(target), { recursive: true });
4522
4622
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
4523
4623
  const fd = await import_node_fs20.promises.open(tmp, "w");
4524
4624
  try {
@@ -4531,7 +4631,7 @@ async function writeAtomically(target, contents) {
4531
4631
  }
4532
4632
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS) {
4533
4633
  const deadline = Date.now() + timeoutMs;
4534
- await import_node_fs20.promises.mkdir(import_node_path32.default.dirname(lockPath), { recursive: true });
4634
+ await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(lockPath), { recursive: true });
4535
4635
  while (true) {
4536
4636
  try {
4537
4637
  const fd = await import_node_fs20.promises.open(lockPath, "wx");
@@ -4574,10 +4674,10 @@ async function readRegistry() {
4574
4674
  throw err;
4575
4675
  }
4576
4676
  const parsed = JSON.parse(raw);
4577
- return import_types20.RegistryFileSchema.parse(parsed);
4677
+ return import_types21.RegistryFileSchema.parse(parsed);
4578
4678
  }
4579
4679
  async function writeRegistry(reg) {
4580
- const validated = import_types20.RegistryFileSchema.parse(reg);
4680
+ const validated = import_types21.RegistryFileSchema.parse(reg);
4581
4681
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
4582
4682
  }
4583
4683
  var ProjectNameCollisionError = class extends Error {
@@ -4837,11 +4937,11 @@ function registerRoutes(scope, ctx) {
4837
4937
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
4838
4938
  const parsed = [];
4839
4939
  for (const c of candidates) {
4840
- const r = import_types21.DivergenceTypeSchema.safeParse(c);
4940
+ const r = import_types22.DivergenceTypeSchema.safeParse(c);
4841
4941
  if (!r.success) {
4842
4942
  return reply.code(400).send({
4843
4943
  error: `unknown divergence type "${c}"`,
4844
- allowed: import_types21.DivergenceTypeSchema.options
4944
+ allowed: import_types22.DivergenceTypeSchema.options
4845
4945
  });
4846
4946
  }
4847
4947
  parsed.push(r.data);
@@ -5025,7 +5125,7 @@ function registerRoutes(scope, ctx) {
5025
5125
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
5026
5126
  let violations = await log.readAll();
5027
5127
  if (req.query.severity) {
5028
- const sev = import_types21.PolicySeveritySchema.safeParse(req.query.severity);
5128
+ const sev = import_types22.PolicySeveritySchema.safeParse(req.query.severity);
5029
5129
  if (!sev.success) {
5030
5130
  return reply.code(400).send({
5031
5131
  error: "invalid severity",
@@ -5042,7 +5142,7 @@ function registerRoutes(scope, ctx) {
5042
5142
  scope.post("/policies/check", async (req, reply) => {
5043
5143
  const proj = resolveProject(registry, req, reply);
5044
5144
  if (!proj) return;
5045
- const parsed = import_types21.PoliciesCheckBodySchema.safeParse(req.body ?? {});
5145
+ const parsed = import_types22.PoliciesCheckBodySchema.safeParse(req.body ?? {});
5046
5146
  if (!parsed.success) {
5047
5147
  return reply.code(400).send({
5048
5148
  error: "invalid /policies/check body",
@@ -5147,14 +5247,14 @@ init_otel_grpc();
5147
5247
  // src/daemon.ts
5148
5248
  init_cjs_shims();
5149
5249
  var import_node_fs21 = require("fs");
5150
- var import_node_path35 = __toESM(require("path"), 1);
5250
+ var import_node_path36 = __toESM(require("path"), 1);
5151
5251
  init_otel();
5152
5252
  function neatHomeFor(opts) {
5153
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path35.default.resolve(opts.neatHome);
5253
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path36.default.resolve(opts.neatHome);
5154
5254
  const env = process.env.NEAT_HOME;
5155
- if (env && env.length > 0) return import_node_path35.default.resolve(env);
5255
+ if (env && env.length > 0) return import_node_path36.default.resolve(env);
5156
5256
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
5157
- return import_node_path35.default.join(home, ".neat");
5257
+ return import_node_path36.default.join(home, ".neat");
5158
5258
  }
5159
5259
  function routeSpanToProject(serviceName, projects) {
5160
5260
  if (!serviceName) return DEFAULT_PROJECT;
@@ -5167,7 +5267,7 @@ function routeSpanToProject(serviceName, projects) {
5167
5267
  return DEFAULT_PROJECT;
5168
5268
  }
5169
5269
  async function bootstrapProject(entry) {
5170
- const paths = pathsForProject(entry.name, import_node_path35.default.join(entry.path, "neat-out"));
5270
+ const paths = pathsForProject(entry.name, import_node_path36.default.join(entry.path, "neat-out"));
5171
5271
  try {
5172
5272
  const stat = await import_node_fs21.promises.stat(entry.path);
5173
5273
  if (!stat.isDirectory()) {
@@ -5240,7 +5340,7 @@ async function startDaemon(opts = {}) {
5240
5340
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
5241
5341
  );
5242
5342
  }
5243
- const pidPath = import_node_path35.default.join(home, "neatd.pid");
5343
+ const pidPath = import_node_path36.default.join(home, "neatd.pid");
5244
5344
  await writeAtomically(pidPath, `${process.pid}
5245
5345
  `);
5246
5346
  const slots = /* @__PURE__ */ new Map();