@neat.is/core 0.9.8-dev.20260828 → 0.9.8

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
@@ -6346,11 +6346,25 @@ function upsertInferredEdge(graph, type, source, target, ts) {
6346
6346
  };
6347
6347
  graph.addEdgeWithKey(id, source, target, edge);
6348
6348
  }
6349
+ function emitIncidentEvent(project, ev) {
6350
+ emitNeatEvent({
6351
+ type: "incident",
6352
+ project,
6353
+ payload: {
6354
+ incidentId: ev.id,
6355
+ affectedNode: ev.affectedNode,
6356
+ service: ev.service,
6357
+ incidentKind: (0, import_types8.incidentKindOf)(ev),
6358
+ at: ev.timestamp
6359
+ }
6360
+ });
6361
+ }
6349
6362
  async function appendErrorEvent(ctx, ev) {
6350
6363
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
6351
6364
  await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6365
+ emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
6352
6366
  }
6353
- async function appendConnectorIncident(errorsPath, input) {
6367
+ async function appendConnectorIncident(errorsPath, input, project) {
6354
6368
  const ev = {
6355
6369
  id: input.id,
6356
6370
  timestamp: input.timestamp,
@@ -6364,6 +6378,7 @@ async function appendConnectorIncident(errorsPath, input) {
6364
6378
  };
6365
6379
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6366
6380
  await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6381
+ if (project) emitIncidentEvent(project, ev);
6367
6382
  }
6368
6383
  function landIncidentCallSite(span, callSite, trusted, graph) {
6369
6384
  const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
@@ -6457,12 +6472,13 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
6457
6472
  affectedNode: locus.affectedNode
6458
6473
  };
6459
6474
  }
6460
- function makeErrorSpanWriter(errorsPath, graph, scanPath) {
6475
+ function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJECT) {
6461
6476
  return async (span) => {
6462
6477
  const ev = buildErrorEventForReceiver(span, graph, scanPath);
6463
6478
  if (!ev) return;
6464
6479
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6465
6480
  await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6481
+ emitIncidentEvent(project, ev);
6466
6482
  };
6467
6483
  }
6468
6484
  async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
@@ -16120,7 +16136,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
16120
16136
  init_cjs_shims();
16121
16137
  var import_fastify2 = __toESM(require("fastify"), 1);
16122
16138
  var import_cors = __toESM(require("@fastify/cors"), 1);
16123
- var import_types97 = require("@neat.is/types");
16139
+ var import_types98 = require("@neat.is/types");
16124
16140
 
16125
16141
  // src/extend/index.ts
16126
16142
  init_cjs_shims();
@@ -17162,9 +17178,128 @@ function queryLogEntries(opts) {
17162
17178
  return merged;
17163
17179
  }
17164
17180
 
17165
- // src/ask.ts
17181
+ // src/goodybag.ts
17166
17182
  init_cjs_shims();
17167
17183
  var import_types59 = require("@neat.is/types");
17184
+ var CODE_FILEPATH_ATTR2 = "code.filepath";
17185
+ var CODE_LINENO_ATTR2 = "code.lineno";
17186
+ var BLAST_NEAREST_LIMIT = 5;
17187
+ function grainOf2(graph, nodeId) {
17188
+ if (graph.hasNode(nodeId)) {
17189
+ const t = graph.getNodeAttributes(nodeId).type;
17190
+ if (typeof t === "string" && t.length > 0) {
17191
+ return (t.endsWith("Node") ? t.slice(0, -4) : t).toLowerCase();
17192
+ }
17193
+ }
17194
+ const colon = nodeId.indexOf(":");
17195
+ return colon > 0 ? nodeId.slice(0, colon) : "unknown";
17196
+ }
17197
+ function locusOf(graph, ev) {
17198
+ const file = ev.attributes?.[CODE_FILEPATH_ATTR2];
17199
+ if (typeof file !== "string" || file.length === 0) return null;
17200
+ const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
17201
+ const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
17202
+ const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
17203
+ return {
17204
+ file,
17205
+ ...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
17206
+ ...node?.name ? { symbol: node.name } : {},
17207
+ service: node?.service ?? ev.service,
17208
+ provenance: import_types59.Provenance.OBSERVED
17209
+ };
17210
+ }
17211
+ function divergenceSummary(d) {
17212
+ const column = "column" in d && d.column ? `.${d.column}` : "";
17213
+ const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
17214
+ return label;
17215
+ }
17216
+ function baseName(p) {
17217
+ const parts = p.split(/[\\/]/);
17218
+ return parts[parts.length - 1] || p;
17219
+ }
17220
+ function shortLabel(graph, nodeId) {
17221
+ if (graph.hasNode(nodeId)) {
17222
+ const name = graph.getNodeAttributes(nodeId).name;
17223
+ if (typeof name === "string" && name.length > 0) return name;
17224
+ }
17225
+ const afterHash = nodeId.includes("#") ? nodeId.slice(nodeId.lastIndexOf("#") + 1) : nodeId;
17226
+ return afterHash.includes(":") ? afterHash.slice(afterHash.lastIndexOf(":") + 1) : afterHash;
17227
+ }
17228
+ function renderHeadline(graph, ev, locus, causeNode) {
17229
+ const what = ev.exceptionType ? `raised ${ev.exceptionType}` : ev.errorMessage || "failed";
17230
+ const cause = causeNode && causeNode !== ev.affectedNode ? ` \u2192 root cause ${shortLabel(graph, causeNode)}` : "";
17231
+ if (locus) {
17232
+ const base = baseName(locus.file);
17233
+ const lines = locus.lineStart != null ? locus.lineEnd && locus.lineEnd !== locus.lineStart ? `LINES ${locus.lineStart}-${locus.lineEnd}` : `LINE ${locus.lineStart}` : "";
17234
+ const symbol = locus.symbol ?? (grainOf2(graph, ev.affectedNode) === "symbol" ? shortLabel(graph, ev.affectedNode) : void 0);
17235
+ const subject = symbol ? `SYMBOL ${symbol}` : `FILE ${base}`;
17236
+ const at = symbol ? `${lines ? `${lines} in ` : ""}${base}` : lines;
17237
+ const where = at ? ` at ${at}` : "";
17238
+ return `${subject}${where} (SERVICE ${ev.service}) ${what} at ${ev.timestamp}${cause}`;
17239
+ }
17240
+ return `SERVICE ${ev.service} ${what} at ${ev.timestamp}${cause}`;
17241
+ }
17242
+ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17243
+ const affected = errorEvent.affectedNode;
17244
+ const locus = locusOf(graph, errorEvent);
17245
+ const inGraph = graph.hasNode(affected);
17246
+ const rc = inGraph ? getRootCause(graph, affected, errorEvent, incidents) : null;
17247
+ let rootCause = null;
17248
+ if (rc) {
17249
+ const provs = rc.edgeProvenances ?? [];
17250
+ const chain = (rc.traversalPath ?? []).map((node, i) => ({
17251
+ node,
17252
+ grain: grainOf2(graph, node),
17253
+ provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
17254
+ }));
17255
+ rootCause = {
17256
+ node: rc.rootCauseNode,
17257
+ ...rc.candidates?.[0]?.classification ? { classification: rc.candidates[0].classification } : {},
17258
+ reason: rc.rootCauseReason,
17259
+ confidence: rc.confidence,
17260
+ fix: rc.fixRecommendation ?? null,
17261
+ chain
17262
+ };
17263
+ }
17264
+ const blast = inGraph ? getBlastRadius(graph, affected) : { origin: affected, affectedNodes: [], totalAffected: 0 };
17265
+ const nearest = [...blast.affectedNodes].sort((a, b) => a.distance - b.distance).slice(0, BLAST_NEAREST_LIMIT).map((n) => ({ node: n.nodeId, distance: n.distance, provenance: n.edgeProvenance }));
17266
+ const applicable = inGraph ? selectApplicablePolicies(graph, policies, affected) : [];
17267
+ const policyCards = applicable.map((p) => ({
17268
+ policyName: p.policyName,
17269
+ severity: p.severity,
17270
+ message: p.reason
17271
+ }));
17272
+ const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
17273
+ type: d.type,
17274
+ summary: divergenceSummary(d)
17275
+ })) : [];
17276
+ const card = {
17277
+ kind: "incident",
17278
+ id: errorEvent.id,
17279
+ at: errorEvent.timestamp,
17280
+ incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
17281
+ service: errorEvent.service,
17282
+ affectedNode: affected,
17283
+ message: errorEvent.errorMessage,
17284
+ ...errorEvent.exceptionType ? { exceptionType: errorEvent.exceptionType } : {},
17285
+ ...errorEvent.httpStatusCode !== void 0 ? { httpStatusCode: errorEvent.httpStatusCode } : {},
17286
+ ...errorEvent.incidentCount !== void 0 ? { count: errorEvent.incidentCount } : {},
17287
+ ...errorEvent.firstTimestamp && errorEvent.lastTimestamp ? { window: { first: errorEvent.firstTimestamp, last: errorEvent.lastTimestamp } } : {},
17288
+ ...errorEvent.traceId ? { traceId: errorEvent.traceId } : {},
17289
+ ...errorEvent.spanId ? { spanId: errorEvent.spanId } : {},
17290
+ locus,
17291
+ rootCause,
17292
+ ...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
17293
+ ...policyCards.length > 0 ? { policies: policyCards } : {},
17294
+ ...divergences.length > 0 ? { divergence: divergences } : {},
17295
+ headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
17296
+ };
17297
+ return import_types59.IncidentCardSchema.parse(card);
17298
+ }
17299
+
17300
+ // src/ask.ts
17301
+ init_cjs_shims();
17302
+ var import_types60 = require("@neat.is/types");
17168
17303
  var DEFAULT_MAX_NODES = 3;
17169
17304
  var MAX_FACTS_PER_SECTION = 6;
17170
17305
  var INTENT_RULES = [
@@ -17392,7 +17527,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17392
17527
  };
17393
17528
  graph.forEachNode((id, attrs) => {
17394
17529
  const node = attrs;
17395
- if (node.type === import_types59.NodeType.FrontierNode) return;
17530
+ if (node.type === import_types60.NodeType.FrontierNode) return;
17396
17531
  const name = nodeName(node);
17397
17532
  const body = idBody(id);
17398
17533
  const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
@@ -17411,7 +17546,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17411
17546
  const res = await searchIndex.search(question, 10);
17412
17547
  if (res.provider !== "substring") {
17413
17548
  for (const m of res.matches) {
17414
- if (m.node.type === import_types59.NodeType.FrontierNode) continue;
17549
+ if (m.node.type === import_types60.NodeType.FrontierNode) continue;
17415
17550
  const already = best.get(m.node.id);
17416
17551
  if (!already && m.score < EMBED_MIN_SCORE) continue;
17417
17552
  consider({
@@ -17449,7 +17584,7 @@ function edgeSignalNote(e) {
17449
17584
  function buildRootCauseSection(graph, node, incidents, now) {
17450
17585
  const result = getRootCause(graph, node, void 0, incidents, { now });
17451
17586
  if (!result) return null;
17452
- const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types59.Provenance.OBSERVED;
17587
+ const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
17453
17588
  const facts = [
17454
17589
  {
17455
17590
  text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
@@ -17499,7 +17634,7 @@ function buildObservedSection(graph, node) {
17499
17634
  if (result.observed && result.inboundObservedCount > 0) {
17500
17635
  facts.push({
17501
17636
  text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
17502
- provenance: import_types59.Provenance.OBSERVED
17637
+ provenance: import_types60.Provenance.OBSERVED
17503
17638
  });
17504
17639
  } else {
17505
17640
  return null;
@@ -17527,7 +17662,7 @@ function buildIncidentsSection(node, incidents) {
17527
17662
  const facts = ordered.map((ev) => ({
17528
17663
  text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
17529
17664
  // ErrorEvents are observation records — OBSERVED by definition.
17530
- provenance: import_types59.Provenance.OBSERVED
17665
+ provenance: import_types60.Provenance.OBSERVED
17531
17666
  }));
17532
17667
  return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
17533
17668
  }
@@ -17587,7 +17722,7 @@ function buildGlobalIncidentsSection(incidents) {
17587
17722
  }
17588
17723
  const byKey = /* @__PURE__ */ new Map();
17589
17724
  for (const ev of incidents) {
17590
- const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
17725
+ const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17591
17726
  const cur = byKey.get(key);
17592
17727
  if (!cur) {
17593
17728
  byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
@@ -17602,7 +17737,7 @@ function buildGlobalIncidentsSection(incidents) {
17602
17737
  const rows = [...byKey.values()].sort((a, b) => b.count - a.count || b.latest.localeCompare(a.latest) || a.key.localeCompare(b.key)).slice(0, MAX_FACTS_PER_SECTION);
17603
17738
  const facts = rows.map((r) => ({
17604
17739
  text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
17605
- provenance: import_types59.Provenance.OBSERVED
17740
+ provenance: import_types60.Provenance.OBSERVED
17606
17741
  }));
17607
17742
  return {
17608
17743
  heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
@@ -17615,7 +17750,7 @@ function buildOverviewSections(graph, incidents) {
17615
17750
  graph.forEachNode((_id, attrs) => {
17616
17751
  const node = attrs;
17617
17752
  nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
17618
- if (node.type === import_types59.NodeType.ServiceNode) services.push(node.id);
17753
+ if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
17619
17754
  });
17620
17755
  const edgeByProv = /* @__PURE__ */ new Map();
17621
17756
  graph.forEachEdge((_id, attrs) => {
@@ -17627,10 +17762,10 @@ function buildOverviewSections(graph, incidents) {
17627
17762
  const shapeFacts = [
17628
17763
  { text: `${graph.order} nodes, ${graph.size} edges` },
17629
17764
  {
17630
- text: `${count(import_types59.NodeType.ServiceNode)} services, ${count(import_types59.NodeType.FileNode)} files, ${count(import_types59.NodeType.SymbolNode)} symbols, ${count(import_types59.NodeType.DatabaseNode)} databases`
17765
+ text: `${count(import_types60.NodeType.ServiceNode)} services, ${count(import_types60.NodeType.FileNode)} files, ${count(import_types60.NodeType.SymbolNode)} symbols, ${count(import_types60.NodeType.DatabaseNode)} databases`
17631
17766
  }
17632
17767
  ];
17633
- for (const p of [import_types59.Provenance.EXTRACTED, import_types59.Provenance.OBSERVED, import_types59.Provenance.INFERRED, import_types59.Provenance.STALE]) {
17768
+ for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
17634
17769
  const n = edgeByProv.get(p) ?? 0;
17635
17770
  if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
17636
17771
  }
@@ -17647,7 +17782,7 @@ function buildOverviewSections(graph, incidents) {
17647
17782
  if (incidents && incidents.length > 0) {
17648
17783
  const incCount = /* @__PURE__ */ new Map();
17649
17784
  for (const ev of incidents) {
17650
- const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
17785
+ const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17651
17786
  incCount.set(key, (incCount.get(key) ?? 0) + 1);
17652
17787
  }
17653
17788
  const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
@@ -17655,7 +17790,7 @@ function buildOverviewSections(graph, incidents) {
17655
17790
  heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
17656
17791
  facts: top.map(([k, n]) => ({
17657
17792
  text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
17658
- provenance: import_types59.Provenance.OBSERVED
17793
+ provenance: import_types60.Provenance.OBSERVED
17659
17794
  }))
17660
17795
  });
17661
17796
  }
@@ -17791,7 +17926,7 @@ async function askGraph(graph, question, opts = {}) {
17791
17926
  const provSet = /* @__PURE__ */ new Set();
17792
17927
  for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
17793
17928
  const confidence = sections[0]?.facts[0]?.confidence;
17794
- return import_types59.AskResultSchema.parse({
17929
+ return import_types60.AskResultSchema.parse({
17795
17930
  question,
17796
17931
  intent,
17797
17932
  matched,
@@ -17938,7 +18073,7 @@ init_cjs_shims();
17938
18073
  var import_node_fs37 = require("fs");
17939
18074
  var import_node_os3 = __toESM(require("os"), 1);
17940
18075
  var import_node_path72 = __toESM(require("path"), 1);
17941
- var import_types60 = require("@neat.is/types");
18076
+ var import_types61 = require("@neat.is/types");
17942
18077
  var LOCK_TIMEOUT_MS = 5e3;
17943
18078
  var LOCK_RETRY_MS = 50;
17944
18079
  function neatHome() {
@@ -18148,10 +18283,10 @@ async function readRegistry() {
18148
18283
  throw err;
18149
18284
  }
18150
18285
  const parsed = JSON.parse(raw);
18151
- return import_types60.RegistryFileSchema.parse(parsed);
18286
+ return import_types61.RegistryFileSchema.parse(parsed);
18152
18287
  }
18153
18288
  async function writeRegistry(reg) {
18154
- const validated = import_types60.RegistryFileSchema.parse(reg);
18289
+ const validated = import_types61.RegistryFileSchema.parse(reg);
18155
18290
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18156
18291
  }
18157
18292
  var ProjectNameCollisionError = class extends Error {
@@ -18552,15 +18687,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
18552
18687
 
18553
18688
  // src/connectors/index.ts
18554
18689
  init_cjs_shims();
18555
- var import_types61 = require("@neat.is/types");
18690
+ var import_types62 = require("@neat.is/types");
18556
18691
  var NO_ENV = "unknown";
18557
18692
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
18558
18693
  if (!graph.hasNode(targetNodeId)) return void 0;
18559
18694
  const sites = [];
18560
18695
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
18561
18696
  const edge = graph.getEdgeAttributes(edgeId);
18562
- if (edge.provenance !== import_types61.Provenance.EXTRACTED) continue;
18563
- const parsed = (0, import_types61.parseFileId)(edge.source);
18697
+ if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
18698
+ const parsed = (0, import_types62.parseFileId)(edge.source);
18564
18699
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
18565
18700
  const site = { relPath: edge.evidence.file };
18566
18701
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -18571,7 +18706,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
18571
18706
  function routeCallSiteFor(graph, targetNodeId) {
18572
18707
  if (!graph.hasNode(targetNodeId)) return void 0;
18573
18708
  const attrs = graph.getNodeAttributes(targetNodeId);
18574
- if (attrs.type !== import_types61.NodeType.RouteNode || !attrs.path) return void 0;
18709
+ if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
18575
18710
  const site = { relPath: attrs.path };
18576
18711
  if (attrs.line !== void 0) site.line = attrs.line;
18577
18712
  return site;
@@ -18601,7 +18736,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
18601
18736
  errorMessage: signal.incident.errorMessage,
18602
18737
  ...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
18603
18738
  affectedNode: resolved.targetNodeId
18604
- });
18739
+ }, ctx.project);
18605
18740
  continue;
18606
18741
  }
18607
18742
  if (resolved.ensureInfraNode) {
@@ -19183,23 +19318,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
19183
19318
 
19184
19319
  // src/connectors/supabase/resolve.ts
19185
19320
  init_cjs_shims();
19186
- var import_types63 = require("@neat.is/types");
19321
+ var import_types64 = require("@neat.is/types");
19187
19322
  function createSupabaseResolveTarget(graph, config) {
19188
19323
  return (signal, _ctx) => {
19189
19324
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
19190
19325
  return null;
19191
19326
  }
19192
- const subResourceId = (0, import_types63.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19327
+ const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19193
19328
  if (graph.hasNode(subResourceId)) {
19194
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19329
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19195
19330
  }
19196
- const bareResourceId = (0, import_types63.infraId)(signal.targetKind, signal.targetName);
19331
+ const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
19197
19332
  if (graph.hasNode(bareResourceId)) {
19198
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19333
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19199
19334
  }
19200
- const projectLevelId = (0, import_types63.infraId)("supabase", config.nodeRef);
19335
+ const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
19201
19336
  if (graph.hasNode(projectLevelId)) {
19202
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19337
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19203
19338
  }
19204
19339
  return null;
19205
19340
  };
@@ -19292,7 +19427,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
19292
19427
 
19293
19428
  // src/connectors/railway/index.ts
19294
19429
  init_cjs_shims();
19295
- var import_types67 = require("@neat.is/types");
19430
+ var import_types68 = require("@neat.is/types");
19296
19431
 
19297
19432
  // src/connectors/railway/client.ts
19298
19433
  init_cjs_shims();
@@ -19443,7 +19578,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
19443
19578
  const out = [];
19444
19579
  graph.forEachNode((_id, attrs) => {
19445
19580
  const node = attrs;
19446
- if (node.type !== import_types67.NodeType.RouteNode) return;
19581
+ if (node.type !== import_types68.NodeType.RouteNode) return;
19447
19582
  const route = attrs;
19448
19583
  if (route.service !== serviceName) return;
19449
19584
  out.push({
@@ -19547,12 +19682,12 @@ function createRailwayResolveTarget(config) {
19547
19682
  const serviceName = config.serviceNameById[config.serviceId];
19548
19683
  if (!serviceName) return null;
19549
19684
  if (signal.targetKind === ROUTE_TARGET_KIND) {
19550
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types67.EdgeType.CALLS };
19685
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
19551
19686
  }
19552
19687
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
19553
19688
  const peerName = config.serviceNameById[signal.targetName];
19554
19689
  if (!peerName) return null;
19555
- return { targetNodeId: (0, import_types67.serviceId)(peerName), serviceName, edgeType: import_types67.EdgeType.CONNECTS_TO };
19690
+ return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
19556
19691
  }
19557
19692
  return null;
19558
19693
  };
@@ -19740,7 +19875,7 @@ function mapLogEntriesToSignals(entries) {
19740
19875
 
19741
19876
  // src/connectors/firebase/resolve.ts
19742
19877
  init_cjs_shims();
19743
- var import_types68 = require("@neat.is/types");
19878
+ var import_types69 = require("@neat.is/types");
19744
19879
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
19745
19880
  switch (resourceType) {
19746
19881
  case "cloud_function":
@@ -19755,7 +19890,7 @@ function routeEntriesFor(graph, serviceName) {
19755
19890
  const entries = [];
19756
19891
  graph.forEachNode((_id, attrs) => {
19757
19892
  const node = attrs;
19758
- if (node.type !== import_types68.NodeType.RouteNode) return;
19893
+ if (node.type !== import_types69.NodeType.RouteNode) return;
19759
19894
  const route = attrs;
19760
19895
  if (route.service !== serviceName) return;
19761
19896
  entries.push({
@@ -19787,7 +19922,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
19787
19922
  return {
19788
19923
  targetNodeId: match.routeNodeId,
19789
19924
  serviceName,
19790
- edgeType: import_types68.EdgeType.CALLS
19925
+ edgeType: import_types69.EdgeType.CALLS
19791
19926
  };
19792
19927
  };
19793
19928
  }
@@ -19814,7 +19949,7 @@ init_cjs_shims();
19814
19949
 
19815
19950
  // src/connectors/cloudflare/connector.ts
19816
19951
  init_cjs_shims();
19817
- var import_types70 = require("@neat.is/types");
19952
+ var import_types71 = require("@neat.is/types");
19818
19953
 
19819
19954
  // src/connectors/cloudflare/client.ts
19820
19955
  init_cjs_shims();
@@ -19978,7 +20113,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
19978
20113
  graph.forEachNode((id, attrs) => {
19979
20114
  if (found) return;
19980
20115
  const a = attrs;
19981
- if (a.type === import_types70.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20116
+ if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
19982
20117
  found = id;
19983
20118
  }
19984
20119
  });
@@ -19990,7 +20125,7 @@ function findMatchingRouteNode(graph, serviceName, method, path76) {
19990
20125
  graph.forEachNode((id, attrs) => {
19991
20126
  if (found) return;
19992
20127
  const a = attrs;
19993
- if (a.type !== import_types70.NodeType.RouteNode || a.service !== serviceName) return;
20128
+ if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
19994
20129
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
19995
20130
  const routeMethod = (a.method ?? "").toUpperCase();
19996
20131
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -20009,11 +20144,11 @@ function createCloudflareResolveTarget(config, graph) {
20009
20144
  };
20010
20145
  const mapping = config.workers?.[scriptName];
20011
20146
  if (mapping) {
20012
- const wholeFileId = (0, import_types70.fileId)(mapping.service, mapping.entryFile);
20147
+ const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
20013
20148
  return {
20014
20149
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
20015
20150
  serviceName: mapping.service,
20016
- edgeType: import_types70.EdgeType.CALLS
20151
+ edgeType: import_types71.EdgeType.CALLS
20017
20152
  };
20018
20153
  }
20019
20154
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -20022,13 +20157,13 @@ function createCloudflareResolveTarget(config, graph) {
20022
20157
  return {
20023
20158
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
20024
20159
  serviceName: fileNode.service,
20025
- edgeType: import_types70.EdgeType.CALLS
20160
+ edgeType: import_types71.EdgeType.CALLS
20026
20161
  };
20027
20162
  }
20028
20163
  return {
20029
- targetNodeId: (0, import_types70.infraId)("cloudflare-worker", scriptName),
20164
+ targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
20030
20165
  serviceName: scriptName,
20031
- edgeType: import_types70.EdgeType.CALLS,
20166
+ edgeType: import_types71.EdgeType.CALLS,
20032
20167
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
20033
20168
  };
20034
20169
  };
@@ -20224,14 +20359,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
20224
20359
 
20225
20360
  // src/connectors/neon/resolve.ts
20226
20361
  init_cjs_shims();
20227
- var import_types74 = require("@neat.is/types");
20362
+ var import_types75 = require("@neat.is/types");
20228
20363
  function createNeonResolveTarget(config) {
20229
20364
  return (signal) => {
20230
20365
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20231
20366
  return {
20232
- targetNodeId: (0, import_types74.infraId)("sql-table", signal.targetName),
20367
+ targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
20233
20368
  serviceName: config.serviceName,
20234
- edgeType: import_types74.EdgeType.CALLS,
20369
+ edgeType: import_types75.EdgeType.CALLS,
20235
20370
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
20236
20371
  };
20237
20372
  };
@@ -20412,14 +20547,14 @@ function mapLogEntriesToSignals2(entries) {
20412
20547
 
20413
20548
  // src/connectors/cloud-run/resolve.ts
20414
20549
  init_cjs_shims();
20415
- var import_types78 = require("@neat.is/types");
20550
+ var import_types79 = require("@neat.is/types");
20416
20551
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
20417
20552
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
20418
20553
  let found = null;
20419
20554
  graph.forEachNode((_id, attrs) => {
20420
20555
  if (found) return;
20421
20556
  const node = attrs;
20422
- if (node.type !== import_types78.NodeType.RouteNode) return;
20557
+ if (node.type !== import_types79.NodeType.RouteNode) return;
20423
20558
  const route = attrs;
20424
20559
  if (route.service !== serviceName || !route.pathTemplate) return;
20425
20560
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20444,13 +20579,13 @@ function createCloudRunResolveTarget(graph, config) {
20444
20579
  normalizePathTemplate(path76)
20445
20580
  );
20446
20581
  if (routeNodeId) {
20447
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types78.EdgeType.CALLS };
20582
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
20448
20583
  }
20449
20584
  }
20450
20585
  return {
20451
- targetNodeId: (0, import_types78.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20586
+ targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20452
20587
  serviceName: mappedService ?? gcpServiceName,
20453
- edgeType: import_types78.EdgeType.CALLS,
20588
+ edgeType: import_types79.EdgeType.CALLS,
20454
20589
  ensureInfraNode: {
20455
20590
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
20456
20591
  name: gcpServiceName,
@@ -20632,14 +20767,14 @@ function mapLogEntriesToSignals3(entries) {
20632
20767
 
20633
20768
  // src/connectors/gcp-lb/resolve.ts
20634
20769
  init_cjs_shims();
20635
- var import_types82 = require("@neat.is/types");
20770
+ var import_types83 = require("@neat.is/types");
20636
20771
  var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
20637
20772
  function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
20638
20773
  let found = null;
20639
20774
  graph.forEachNode((_id, attrs) => {
20640
20775
  if (found) return;
20641
20776
  const node = attrs;
20642
- if (node.type !== import_types82.NodeType.RouteNode) return;
20777
+ if (node.type !== import_types83.NodeType.RouteNode) return;
20643
20778
  const route = attrs;
20644
20779
  if (route.service !== serviceName || !route.pathTemplate) return;
20645
20780
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20664,13 +20799,13 @@ function createGcpLbResolveTarget(graph, config) {
20664
20799
  normalizePathTemplate(path76)
20665
20800
  );
20666
20801
  if (routeNodeId) {
20667
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
20802
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
20668
20803
  }
20669
20804
  }
20670
20805
  return {
20671
- targetNodeId: (0, import_types82.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20806
+ targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20672
20807
  serviceName: mappedService ?? backendServiceName,
20673
- edgeType: import_types82.EdgeType.CALLS,
20808
+ edgeType: import_types83.EdgeType.CALLS,
20674
20809
  ensureInfraNode: {
20675
20810
  kind: GCP_LB_BACKEND_INFRA_KIND,
20676
20811
  name: backendServiceName,
@@ -20711,7 +20846,7 @@ function createGcpLbConnector(graph, config = {}) {
20711
20846
 
20712
20847
  // src/connectors/render/index.ts
20713
20848
  init_cjs_shims();
20714
- var import_types85 = require("@neat.is/types");
20849
+ var import_types86 = require("@neat.is/types");
20715
20850
 
20716
20851
  // src/connectors/render/types.ts
20717
20852
  init_cjs_shims();
@@ -20789,7 +20924,7 @@ function buildRenderRouteIndex(graph, serviceName) {
20789
20924
  const out = [];
20790
20925
  graph.forEachNode((_id, attrs) => {
20791
20926
  const node = attrs;
20792
- if (node.type !== import_types85.NodeType.RouteNode) return;
20927
+ if (node.type !== import_types86.NodeType.RouteNode) return;
20793
20928
  const route = attrs;
20794
20929
  if (route.service !== serviceName) return;
20795
20930
  out.push({
@@ -20874,7 +21009,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
20874
21009
  function createRenderResolveTarget(config) {
20875
21010
  return (signal) => {
20876
21011
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
20877
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
21012
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
20878
21013
  }
20879
21014
  return null;
20880
21015
  };
@@ -21012,21 +21147,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
21012
21147
 
21013
21148
  // src/connectors/planetscale/resolve.ts
21014
21149
  init_cjs_shims();
21015
- var import_types89 = require("@neat.is/types");
21150
+ var import_types90 = require("@neat.is/types");
21016
21151
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
21017
21152
  function createPlanetscaleResolveTarget(graph, config) {
21018
21153
  const databaseName = `${config.organization}/${config.database}`;
21019
21154
  return (signal, _ctx) => {
21020
21155
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
21021
- const tableId = (0, import_types89.infraId)("sql-table", signal.targetName);
21156
+ const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
21022
21157
  if (graph.hasNode(tableId)) {
21023
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types89.EdgeType.CALLS };
21158
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
21024
21159
  }
21025
- const providerId = (0, import_types89.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21160
+ const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21026
21161
  return {
21027
21162
  targetNodeId: providerId,
21028
21163
  serviceName: config.serviceName,
21029
- edgeType: import_types89.EdgeType.CALLS,
21164
+ edgeType: import_types90.EdgeType.CALLS,
21030
21165
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
21031
21166
  };
21032
21167
  };
@@ -21291,7 +21426,7 @@ function mapBuildsToSignals(builds, serviceName) {
21291
21426
 
21292
21427
  // src/connectors/eas/resolve.ts
21293
21428
  init_cjs_shims();
21294
- var import_types94 = require("@neat.is/types");
21429
+ var import_types95 = require("@neat.is/types");
21295
21430
  var NO_ENV2 = "unknown";
21296
21431
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
21297
21432
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -21307,8 +21442,8 @@ function configBasenamesForPhase(phase) {
21307
21442
  function configNodeService(graph, configNodeId) {
21308
21443
  for (const edgeId of graph.inboundEdges(configNodeId)) {
21309
21444
  const edge = graph.getEdgeAttributes(edgeId);
21310
- if (edge.type !== import_types94.EdgeType.CONFIGURED_BY) continue;
21311
- const parsed = (0, import_types94.parseFileId)(edge.source);
21445
+ if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
21446
+ const parsed = (0, import_types95.parseFileId)(edge.source);
21312
21447
  if (parsed) return parsed.service;
21313
21448
  }
21314
21449
  return null;
@@ -21319,7 +21454,7 @@ function findConfigNode(graph, basenames, serviceName) {
21319
21454
  graph.forEachNode((id, attrs) => {
21320
21455
  if (scoped) return;
21321
21456
  const node = attrs;
21322
- if (node.type !== import_types94.NodeType.ConfigNode) return;
21457
+ if (node.type !== import_types95.NodeType.ConfigNode) return;
21323
21458
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
21324
21459
  if (anyMatch === null) anyMatch = id;
21325
21460
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -21336,13 +21471,13 @@ function createEasResolveTarget(graph) {
21336
21471
  if (basenames.length > 0) {
21337
21472
  const configNodeId = findConfigNode(graph, basenames, serviceName);
21338
21473
  if (configNodeId) {
21339
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types94.EdgeType.CALLS };
21474
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
21340
21475
  }
21341
21476
  }
21342
21477
  return {
21343
21478
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
21344
21479
  serviceName,
21345
- edgeType: import_types94.EdgeType.CALLS
21480
+ edgeType: import_types95.EdgeType.CALLS
21346
21481
  };
21347
21482
  };
21348
21483
  }
@@ -21929,6 +22064,7 @@ async function startConnectorPolling(input) {
21929
22064
  registration.connector,
21930
22065
  {
21931
22066
  projectDir: input.projectDir,
22067
+ project: input.project,
21932
22068
  credentials: registration.credentials,
21933
22069
  ...input.errorsPath ? { errorsPath: input.errorsPath } : {}
21934
22070
  },
@@ -22103,11 +22239,11 @@ function registerRoutes(scope, ctx) {
22103
22239
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
22104
22240
  const parsed = [];
22105
22241
  for (const c of candidates) {
22106
- const r = import_types97.DivergenceTypeSchema.safeParse(c);
22242
+ const r = import_types98.DivergenceTypeSchema.safeParse(c);
22107
22243
  if (!r.success) {
22108
22244
  return reply.code(400).send({
22109
22245
  error: `unknown divergence type "${c}"`,
22110
- allowed: import_types97.DivergenceTypeSchema.options
22246
+ allowed: import_types98.DivergenceTypeSchema.options
22111
22247
  });
22112
22248
  }
22113
22249
  parsed.push(r.data);
@@ -22224,6 +22360,7 @@ function registerRoutes(scope, ctx) {
22224
22360
  reg.connector,
22225
22361
  {
22226
22362
  projectDir: proj.scanPath ?? "",
22363
+ project: proj.name,
22227
22364
  credentials: reg.credentials,
22228
22365
  ...incidentsPath ? { errorsPath: incidentsPath } : {}
22229
22366
  },
@@ -22287,6 +22424,39 @@ function registerRoutes(scope, ctx) {
22287
22424
  if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
22288
22425
  return result;
22289
22426
  });
22427
+ scope.get("/graph/incident-card/:nodeId", async (req, reply) => {
22428
+ const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
22429
+ if (!proj) return;
22430
+ const { nodeId } = req.params;
22431
+ if (!proj.graph.hasNode(nodeId)) {
22432
+ return reply.code(404).send({ error: "node not found", id: nodeId });
22433
+ }
22434
+ const epath = errorsPathFor(proj);
22435
+ const incidents = epath ? await readErrorEvents(epath) : [];
22436
+ let errorEvent;
22437
+ if (req.query.errorId) {
22438
+ errorEvent = incidents.find((e) => e.id === req.query.errorId);
22439
+ if (!errorEvent) {
22440
+ return reply.code(404).send({ error: "error event not found", id: req.query.errorId });
22441
+ }
22442
+ } else {
22443
+ const svc = nodeId.replace(/^service:/, "");
22444
+ errorEvent = [...incidents].filter((e) => e.affectedNode === nodeId || e.service === svc).sort((a, b) => (b.timestamp ?? "").localeCompare(a.timestamp ?? ""))[0];
22445
+ if (!errorEvent) {
22446
+ return reply.code(404).send({ error: "no incident found for node", id: nodeId });
22447
+ }
22448
+ }
22449
+ const policyPath = ctx.policyFilePathFor(proj);
22450
+ let policies = [];
22451
+ if (policyPath) {
22452
+ try {
22453
+ policies = await loadPolicyFile(policyPath);
22454
+ } catch {
22455
+ policies = [];
22456
+ }
22457
+ }
22458
+ return buildIncidentCard(proj.graph, errorEvent, incidents, policies);
22459
+ });
22290
22460
  scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
22291
22461
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
22292
22462
  if (!proj) return;
@@ -22469,7 +22639,7 @@ function registerRoutes(scope, ctx) {
22469
22639
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
22470
22640
  let violations = await log.readAll();
22471
22641
  if (req.query.severity) {
22472
- const sev = import_types97.PolicySeveritySchema.safeParse(req.query.severity);
22642
+ const sev = import_types98.PolicySeveritySchema.safeParse(req.query.severity);
22473
22643
  if (!sev.success) {
22474
22644
  return reply.code(400).send({
22475
22645
  error: "invalid severity",
@@ -22508,7 +22678,7 @@ function registerRoutes(scope, ctx) {
22508
22678
  scope.post("/policies/check", async (req, reply) => {
22509
22679
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
22510
22680
  if (!proj) return;
22511
- const parsed = import_types97.PoliciesCheckBodySchema.safeParse(req.body ?? {});
22681
+ const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req.body ?? {});
22512
22682
  if (!parsed.success) {
22513
22683
  return reply.code(400).send({
22514
22684
  error: "invalid /policies/check body",
@@ -22857,7 +23027,7 @@ function unroutedErrorsPath(neatHome3) {
22857
23027
  }
22858
23028
 
22859
23029
  // src/daemon.ts
22860
- var import_types98 = require("@neat.is/types");
23030
+ var import_types99 = require("@neat.is/types");
22861
23031
  function daemonJsonPath(scanPath) {
22862
23032
  return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
22863
23033
  }
@@ -22982,7 +23152,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
22982
23152
  if (!serviceName) return true;
22983
23153
  if (serviceNameMatchesProject(serviceName, project)) return true;
22984
23154
  return graph.someNode(
22985
- (_id, attrs) => attrs.type === import_types98.NodeType.ServiceNode && attrs.name === serviceName
23155
+ (_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
22986
23156
  );
22987
23157
  }
22988
23158
  async function bootstrapProject(entry, connectors = [], neatHome3) {
@@ -23437,7 +23607,12 @@ async function startDaemon(opts = {}) {
23437
23607
  onErrorSpanSync: async (span) => {
23438
23608
  const slot = await resolveTargetSlot(span.service, span.traceId);
23439
23609
  if (!slot) return;
23440
- await makeErrorSpanWriter(slot.paths.errorsPath, slot.graph, slot.entry.path)(span);
23610
+ await makeErrorSpanWriter(
23611
+ slot.paths.errorsPath,
23612
+ slot.graph,
23613
+ slot.entry.path,
23614
+ slot.entry.name
23615
+ )(span);
23441
23616
  },
23442
23617
  // Project-scoped route (issue #367) — the URL already named the
23443
23618
  // project. Resolution is a direct slot lookup; service.name resolves
@@ -23460,7 +23635,12 @@ async function startDaemon(opts = {}) {
23460
23635
  onProjectErrorSpanSync: async (project, span) => {
23461
23636
  const slot = await resolveSlotByName(project, span.service, span.traceId);
23462
23637
  if (!slot) return;
23463
- await makeErrorSpanWriter(slot.paths.errorsPath, slot.graph, slot.entry.path)(span);
23638
+ await makeErrorSpanWriter(
23639
+ slot.paths.errorsPath,
23640
+ slot.graph,
23641
+ slot.entry.path,
23642
+ slot.entry.name
23643
+ )(span);
23464
23644
  },
23465
23645
  // #881 — 404 a project-scoped POST for a project this daemon doesn't
23466
23646
  // host, rather than accepting it and dropping the batch. `slots` covers