@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/neatd.cjs CHANGED
@@ -6309,11 +6309,25 @@ function upsertInferredEdge(graph, type, source, target, ts) {
6309
6309
  };
6310
6310
  graph.addEdgeWithKey(id, source, target, edge);
6311
6311
  }
6312
+ function emitIncidentEvent(project, ev) {
6313
+ emitNeatEvent({
6314
+ type: "incident",
6315
+ project,
6316
+ payload: {
6317
+ incidentId: ev.id,
6318
+ affectedNode: ev.affectedNode,
6319
+ service: ev.service,
6320
+ incidentKind: (0, import_types8.incidentKindOf)(ev),
6321
+ at: ev.timestamp
6322
+ }
6323
+ });
6324
+ }
6312
6325
  async function appendErrorEvent(ctx, ev) {
6313
6326
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
6314
6327
  await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
6328
+ emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
6315
6329
  }
6316
- async function appendConnectorIncident(errorsPath, input) {
6330
+ async function appendConnectorIncident(errorsPath, input, project) {
6317
6331
  const ev = {
6318
6332
  id: input.id,
6319
6333
  timestamp: input.timestamp,
@@ -6327,6 +6341,7 @@ async function appendConnectorIncident(errorsPath, input) {
6327
6341
  };
6328
6342
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6329
6343
  await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6344
+ if (project) emitIncidentEvent(project, ev);
6330
6345
  }
6331
6346
  function landIncidentCallSite(span, callSite, trusted, graph) {
6332
6347
  const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
@@ -6420,12 +6435,13 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
6420
6435
  affectedNode: locus.affectedNode
6421
6436
  };
6422
6437
  }
6423
- function makeErrorSpanWriter(errorsPath, graph, scanPath) {
6438
+ function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJECT) {
6424
6439
  return async (span) => {
6425
6440
  const ev = buildErrorEventForReceiver(span, graph, scanPath);
6426
6441
  if (!ev) return;
6427
6442
  await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
6428
6443
  await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
6444
+ emitIncidentEvent(project, ev);
6429
6445
  };
6430
6446
  }
6431
6447
  async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
@@ -16132,7 +16148,7 @@ var Projects = class {
16132
16148
  init_cjs_shims();
16133
16149
  var import_fastify2 = __toESM(require("fastify"), 1);
16134
16150
  var import_cors = __toESM(require("@fastify/cors"), 1);
16135
- var import_types97 = require("@neat.is/types");
16151
+ var import_types98 = require("@neat.is/types");
16136
16152
 
16137
16153
  // src/extend/index.ts
16138
16154
  init_cjs_shims();
@@ -17174,9 +17190,128 @@ function queryLogEntries(opts) {
17174
17190
  return merged;
17175
17191
  }
17176
17192
 
17177
- // src/ask.ts
17193
+ // src/goodybag.ts
17178
17194
  init_cjs_shims();
17179
17195
  var import_types59 = require("@neat.is/types");
17196
+ var CODE_FILEPATH_ATTR2 = "code.filepath";
17197
+ var CODE_LINENO_ATTR2 = "code.lineno";
17198
+ var BLAST_NEAREST_LIMIT = 5;
17199
+ function grainOf2(graph, nodeId) {
17200
+ if (graph.hasNode(nodeId)) {
17201
+ const t = graph.getNodeAttributes(nodeId).type;
17202
+ if (typeof t === "string" && t.length > 0) {
17203
+ return (t.endsWith("Node") ? t.slice(0, -4) : t).toLowerCase();
17204
+ }
17205
+ }
17206
+ const colon = nodeId.indexOf(":");
17207
+ return colon > 0 ? nodeId.slice(0, colon) : "unknown";
17208
+ }
17209
+ function locusOf(graph, ev) {
17210
+ const file = ev.attributes?.[CODE_FILEPATH_ATTR2];
17211
+ if (typeof file !== "string" || file.length === 0) return null;
17212
+ const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
17213
+ const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
17214
+ const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
17215
+ return {
17216
+ file,
17217
+ ...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
17218
+ ...node?.name ? { symbol: node.name } : {},
17219
+ service: node?.service ?? ev.service,
17220
+ provenance: import_types59.Provenance.OBSERVED
17221
+ };
17222
+ }
17223
+ function divergenceSummary(d) {
17224
+ const column = "column" in d && d.column ? `.${d.column}` : "";
17225
+ const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
17226
+ return label;
17227
+ }
17228
+ function baseName(p) {
17229
+ const parts = p.split(/[\\/]/);
17230
+ return parts[parts.length - 1] || p;
17231
+ }
17232
+ function shortLabel(graph, nodeId) {
17233
+ if (graph.hasNode(nodeId)) {
17234
+ const name = graph.getNodeAttributes(nodeId).name;
17235
+ if (typeof name === "string" && name.length > 0) return name;
17236
+ }
17237
+ const afterHash = nodeId.includes("#") ? nodeId.slice(nodeId.lastIndexOf("#") + 1) : nodeId;
17238
+ return afterHash.includes(":") ? afterHash.slice(afterHash.lastIndexOf(":") + 1) : afterHash;
17239
+ }
17240
+ function renderHeadline(graph, ev, locus, causeNode) {
17241
+ const what = ev.exceptionType ? `raised ${ev.exceptionType}` : ev.errorMessage || "failed";
17242
+ const cause = causeNode && causeNode !== ev.affectedNode ? ` \u2192 root cause ${shortLabel(graph, causeNode)}` : "";
17243
+ if (locus) {
17244
+ const base = baseName(locus.file);
17245
+ const lines = locus.lineStart != null ? locus.lineEnd && locus.lineEnd !== locus.lineStart ? `LINES ${locus.lineStart}-${locus.lineEnd}` : `LINE ${locus.lineStart}` : "";
17246
+ const symbol = locus.symbol ?? (grainOf2(graph, ev.affectedNode) === "symbol" ? shortLabel(graph, ev.affectedNode) : void 0);
17247
+ const subject = symbol ? `SYMBOL ${symbol}` : `FILE ${base}`;
17248
+ const at = symbol ? `${lines ? `${lines} in ` : ""}${base}` : lines;
17249
+ const where = at ? ` at ${at}` : "";
17250
+ return `${subject}${where} (SERVICE ${ev.service}) ${what} at ${ev.timestamp}${cause}`;
17251
+ }
17252
+ return `SERVICE ${ev.service} ${what} at ${ev.timestamp}${cause}`;
17253
+ }
17254
+ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17255
+ const affected = errorEvent.affectedNode;
17256
+ const locus = locusOf(graph, errorEvent);
17257
+ const inGraph = graph.hasNode(affected);
17258
+ const rc = inGraph ? getRootCause(graph, affected, errorEvent, incidents) : null;
17259
+ let rootCause = null;
17260
+ if (rc) {
17261
+ const provs = rc.edgeProvenances ?? [];
17262
+ const chain = (rc.traversalPath ?? []).map((node, i) => ({
17263
+ node,
17264
+ grain: grainOf2(graph, node),
17265
+ provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
17266
+ }));
17267
+ rootCause = {
17268
+ node: rc.rootCauseNode,
17269
+ ...rc.candidates?.[0]?.classification ? { classification: rc.candidates[0].classification } : {},
17270
+ reason: rc.rootCauseReason,
17271
+ confidence: rc.confidence,
17272
+ fix: rc.fixRecommendation ?? null,
17273
+ chain
17274
+ };
17275
+ }
17276
+ const blast = inGraph ? getBlastRadius(graph, affected) : { origin: affected, affectedNodes: [], totalAffected: 0 };
17277
+ 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 }));
17278
+ const applicable = inGraph ? selectApplicablePolicies(graph, policies, affected) : [];
17279
+ const policyCards = applicable.map((p) => ({
17280
+ policyName: p.policyName,
17281
+ severity: p.severity,
17282
+ message: p.reason
17283
+ }));
17284
+ const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
17285
+ type: d.type,
17286
+ summary: divergenceSummary(d)
17287
+ })) : [];
17288
+ const card = {
17289
+ kind: "incident",
17290
+ id: errorEvent.id,
17291
+ at: errorEvent.timestamp,
17292
+ incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
17293
+ service: errorEvent.service,
17294
+ affectedNode: affected,
17295
+ message: errorEvent.errorMessage,
17296
+ ...errorEvent.exceptionType ? { exceptionType: errorEvent.exceptionType } : {},
17297
+ ...errorEvent.httpStatusCode !== void 0 ? { httpStatusCode: errorEvent.httpStatusCode } : {},
17298
+ ...errorEvent.incidentCount !== void 0 ? { count: errorEvent.incidentCount } : {},
17299
+ ...errorEvent.firstTimestamp && errorEvent.lastTimestamp ? { window: { first: errorEvent.firstTimestamp, last: errorEvent.lastTimestamp } } : {},
17300
+ ...errorEvent.traceId ? { traceId: errorEvent.traceId } : {},
17301
+ ...errorEvent.spanId ? { spanId: errorEvent.spanId } : {},
17302
+ locus,
17303
+ rootCause,
17304
+ ...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
17305
+ ...policyCards.length > 0 ? { policies: policyCards } : {},
17306
+ ...divergences.length > 0 ? { divergence: divergences } : {},
17307
+ headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
17308
+ };
17309
+ return import_types59.IncidentCardSchema.parse(card);
17310
+ }
17311
+
17312
+ // src/ask.ts
17313
+ init_cjs_shims();
17314
+ var import_types60 = require("@neat.is/types");
17180
17315
  var DEFAULT_MAX_NODES = 3;
17181
17316
  var MAX_FACTS_PER_SECTION = 6;
17182
17317
  var INTENT_RULES = [
@@ -17404,7 +17539,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17404
17539
  };
17405
17540
  graph.forEachNode((id, attrs) => {
17406
17541
  const node = attrs;
17407
- if (node.type === import_types59.NodeType.FrontierNode) return;
17542
+ if (node.type === import_types60.NodeType.FrontierNode) return;
17408
17543
  const name = nodeName(node);
17409
17544
  const body = idBody(id);
17410
17545
  const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
@@ -17423,7 +17558,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17423
17558
  const res = await searchIndex.search(question, 10);
17424
17559
  if (res.provider !== "substring") {
17425
17560
  for (const m of res.matches) {
17426
- if (m.node.type === import_types59.NodeType.FrontierNode) continue;
17561
+ if (m.node.type === import_types60.NodeType.FrontierNode) continue;
17427
17562
  const already = best.get(m.node.id);
17428
17563
  if (!already && m.score < EMBED_MIN_SCORE) continue;
17429
17564
  consider({
@@ -17461,7 +17596,7 @@ function edgeSignalNote(e) {
17461
17596
  function buildRootCauseSection(graph, node, incidents, now) {
17462
17597
  const result = getRootCause(graph, node, void 0, incidents, { now });
17463
17598
  if (!result) return null;
17464
- const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types59.Provenance.OBSERVED;
17599
+ const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
17465
17600
  const facts = [
17466
17601
  {
17467
17602
  text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
@@ -17511,7 +17646,7 @@ function buildObservedSection(graph, node) {
17511
17646
  if (result.observed && result.inboundObservedCount > 0) {
17512
17647
  facts.push({
17513
17648
  text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
17514
- provenance: import_types59.Provenance.OBSERVED
17649
+ provenance: import_types60.Provenance.OBSERVED
17515
17650
  });
17516
17651
  } else {
17517
17652
  return null;
@@ -17539,7 +17674,7 @@ function buildIncidentsSection(node, incidents) {
17539
17674
  const facts = ordered.map((ev) => ({
17540
17675
  text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
17541
17676
  // ErrorEvents are observation records — OBSERVED by definition.
17542
- provenance: import_types59.Provenance.OBSERVED
17677
+ provenance: import_types60.Provenance.OBSERVED
17543
17678
  }));
17544
17679
  return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
17545
17680
  }
@@ -17599,7 +17734,7 @@ function buildGlobalIncidentsSection(incidents) {
17599
17734
  }
17600
17735
  const byKey = /* @__PURE__ */ new Map();
17601
17736
  for (const ev of incidents) {
17602
- const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
17737
+ const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17603
17738
  const cur = byKey.get(key);
17604
17739
  if (!cur) {
17605
17740
  byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
@@ -17614,7 +17749,7 @@ function buildGlobalIncidentsSection(incidents) {
17614
17749
  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);
17615
17750
  const facts = rows.map((r) => ({
17616
17751
  text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
17617
- provenance: import_types59.Provenance.OBSERVED
17752
+ provenance: import_types60.Provenance.OBSERVED
17618
17753
  }));
17619
17754
  return {
17620
17755
  heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
@@ -17627,7 +17762,7 @@ function buildOverviewSections(graph, incidents) {
17627
17762
  graph.forEachNode((_id, attrs) => {
17628
17763
  const node = attrs;
17629
17764
  nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
17630
- if (node.type === import_types59.NodeType.ServiceNode) services.push(node.id);
17765
+ if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
17631
17766
  });
17632
17767
  const edgeByProv = /* @__PURE__ */ new Map();
17633
17768
  graph.forEachEdge((_id, attrs) => {
@@ -17639,10 +17774,10 @@ function buildOverviewSections(graph, incidents) {
17639
17774
  const shapeFacts = [
17640
17775
  { text: `${graph.order} nodes, ${graph.size} edges` },
17641
17776
  {
17642
- 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`
17777
+ 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`
17643
17778
  }
17644
17779
  ];
17645
- for (const p of [import_types59.Provenance.EXTRACTED, import_types59.Provenance.OBSERVED, import_types59.Provenance.INFERRED, import_types59.Provenance.STALE]) {
17780
+ for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
17646
17781
  const n = edgeByProv.get(p) ?? 0;
17647
17782
  if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
17648
17783
  }
@@ -17659,7 +17794,7 @@ function buildOverviewSections(graph, incidents) {
17659
17794
  if (incidents && incidents.length > 0) {
17660
17795
  const incCount = /* @__PURE__ */ new Map();
17661
17796
  for (const ev of incidents) {
17662
- const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
17797
+ const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17663
17798
  incCount.set(key, (incCount.get(key) ?? 0) + 1);
17664
17799
  }
17665
17800
  const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
@@ -17667,7 +17802,7 @@ function buildOverviewSections(graph, incidents) {
17667
17802
  heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
17668
17803
  facts: top.map(([k, n]) => ({
17669
17804
  text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
17670
- provenance: import_types59.Provenance.OBSERVED
17805
+ provenance: import_types60.Provenance.OBSERVED
17671
17806
  }))
17672
17807
  });
17673
17808
  }
@@ -17803,7 +17938,7 @@ async function askGraph(graph, question, opts = {}) {
17803
17938
  const provSet = /* @__PURE__ */ new Set();
17804
17939
  for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
17805
17940
  const confidence = sections[0]?.facts[0]?.confidence;
17806
- return import_types59.AskResultSchema.parse({
17941
+ return import_types60.AskResultSchema.parse({
17807
17942
  question,
17808
17943
  intent,
17809
17944
  matched,
@@ -17898,7 +18033,7 @@ init_cjs_shims();
17898
18033
  var import_node_fs37 = require("fs");
17899
18034
  var import_node_os3 = __toESM(require("os"), 1);
17900
18035
  var import_node_path72 = __toESM(require("path"), 1);
17901
- var import_types60 = require("@neat.is/types");
18036
+ var import_types61 = require("@neat.is/types");
17902
18037
  var LOCK_TIMEOUT_MS = 5e3;
17903
18038
  var LOCK_RETRY_MS = 50;
17904
18039
  function neatHome() {
@@ -18100,10 +18235,10 @@ async function readRegistry() {
18100
18235
  throw err;
18101
18236
  }
18102
18237
  const parsed = JSON.parse(raw);
18103
- return import_types60.RegistryFileSchema.parse(parsed);
18238
+ return import_types61.RegistryFileSchema.parse(parsed);
18104
18239
  }
18105
18240
  async function writeRegistry(reg) {
18106
- const validated = import_types60.RegistryFileSchema.parse(reg);
18241
+ const validated = import_types61.RegistryFileSchema.parse(reg);
18107
18242
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18108
18243
  }
18109
18244
  async function getProject(name) {
@@ -18454,15 +18589,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
18454
18589
 
18455
18590
  // src/connectors/index.ts
18456
18591
  init_cjs_shims();
18457
- var import_types61 = require("@neat.is/types");
18592
+ var import_types62 = require("@neat.is/types");
18458
18593
  var NO_ENV = "unknown";
18459
18594
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
18460
18595
  if (!graph.hasNode(targetNodeId)) return void 0;
18461
18596
  const sites = [];
18462
18597
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
18463
18598
  const edge = graph.getEdgeAttributes(edgeId);
18464
- if (edge.provenance !== import_types61.Provenance.EXTRACTED) continue;
18465
- const parsed = (0, import_types61.parseFileId)(edge.source);
18599
+ if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
18600
+ const parsed = (0, import_types62.parseFileId)(edge.source);
18466
18601
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
18467
18602
  const site = { relPath: edge.evidence.file };
18468
18603
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -18473,7 +18608,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
18473
18608
  function routeCallSiteFor(graph, targetNodeId) {
18474
18609
  if (!graph.hasNode(targetNodeId)) return void 0;
18475
18610
  const attrs = graph.getNodeAttributes(targetNodeId);
18476
- if (attrs.type !== import_types61.NodeType.RouteNode || !attrs.path) return void 0;
18611
+ if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
18477
18612
  const site = { relPath: attrs.path };
18478
18613
  if (attrs.line !== void 0) site.line = attrs.line;
18479
18614
  return site;
@@ -18503,7 +18638,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
18503
18638
  errorMessage: signal.incident.errorMessage,
18504
18639
  ...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
18505
18640
  affectedNode: resolved.targetNodeId
18506
- });
18641
+ }, ctx.project);
18507
18642
  continue;
18508
18643
  }
18509
18644
  if (resolved.ensureInfraNode) {
@@ -19085,23 +19220,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
19085
19220
 
19086
19221
  // src/connectors/supabase/resolve.ts
19087
19222
  init_cjs_shims();
19088
- var import_types63 = require("@neat.is/types");
19223
+ var import_types64 = require("@neat.is/types");
19089
19224
  function createSupabaseResolveTarget(graph, config) {
19090
19225
  return (signal, _ctx) => {
19091
19226
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
19092
19227
  return null;
19093
19228
  }
19094
- const subResourceId = (0, import_types63.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19229
+ const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19095
19230
  if (graph.hasNode(subResourceId)) {
19096
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19231
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19097
19232
  }
19098
- const bareResourceId = (0, import_types63.infraId)(signal.targetKind, signal.targetName);
19233
+ const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
19099
19234
  if (graph.hasNode(bareResourceId)) {
19100
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19235
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19101
19236
  }
19102
- const projectLevelId = (0, import_types63.infraId)("supabase", config.nodeRef);
19237
+ const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
19103
19238
  if (graph.hasNode(projectLevelId)) {
19104
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
19239
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19105
19240
  }
19106
19241
  return null;
19107
19242
  };
@@ -19194,7 +19329,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
19194
19329
 
19195
19330
  // src/connectors/railway/index.ts
19196
19331
  init_cjs_shims();
19197
- var import_types67 = require("@neat.is/types");
19332
+ var import_types68 = require("@neat.is/types");
19198
19333
 
19199
19334
  // src/connectors/railway/client.ts
19200
19335
  init_cjs_shims();
@@ -19345,7 +19480,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
19345
19480
  const out = [];
19346
19481
  graph.forEachNode((_id, attrs) => {
19347
19482
  const node = attrs;
19348
- if (node.type !== import_types67.NodeType.RouteNode) return;
19483
+ if (node.type !== import_types68.NodeType.RouteNode) return;
19349
19484
  const route = attrs;
19350
19485
  if (route.service !== serviceName) return;
19351
19486
  out.push({
@@ -19449,12 +19584,12 @@ function createRailwayResolveTarget(config) {
19449
19584
  const serviceName = config.serviceNameById[config.serviceId];
19450
19585
  if (!serviceName) return null;
19451
19586
  if (signal.targetKind === ROUTE_TARGET_KIND) {
19452
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types67.EdgeType.CALLS };
19587
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
19453
19588
  }
19454
19589
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
19455
19590
  const peerName = config.serviceNameById[signal.targetName];
19456
19591
  if (!peerName) return null;
19457
- return { targetNodeId: (0, import_types67.serviceId)(peerName), serviceName, edgeType: import_types67.EdgeType.CONNECTS_TO };
19592
+ return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
19458
19593
  }
19459
19594
  return null;
19460
19595
  };
@@ -19642,7 +19777,7 @@ function mapLogEntriesToSignals(entries) {
19642
19777
 
19643
19778
  // src/connectors/firebase/resolve.ts
19644
19779
  init_cjs_shims();
19645
- var import_types68 = require("@neat.is/types");
19780
+ var import_types69 = require("@neat.is/types");
19646
19781
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
19647
19782
  switch (resourceType) {
19648
19783
  case "cloud_function":
@@ -19657,7 +19792,7 @@ function routeEntriesFor(graph, serviceName) {
19657
19792
  const entries = [];
19658
19793
  graph.forEachNode((_id, attrs) => {
19659
19794
  const node = attrs;
19660
- if (node.type !== import_types68.NodeType.RouteNode) return;
19795
+ if (node.type !== import_types69.NodeType.RouteNode) return;
19661
19796
  const route = attrs;
19662
19797
  if (route.service !== serviceName) return;
19663
19798
  entries.push({
@@ -19689,7 +19824,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
19689
19824
  return {
19690
19825
  targetNodeId: match.routeNodeId,
19691
19826
  serviceName,
19692
- edgeType: import_types68.EdgeType.CALLS
19827
+ edgeType: import_types69.EdgeType.CALLS
19693
19828
  };
19694
19829
  };
19695
19830
  }
@@ -19716,7 +19851,7 @@ init_cjs_shims();
19716
19851
 
19717
19852
  // src/connectors/cloudflare/connector.ts
19718
19853
  init_cjs_shims();
19719
- var import_types70 = require("@neat.is/types");
19854
+ var import_types71 = require("@neat.is/types");
19720
19855
 
19721
19856
  // src/connectors/cloudflare/client.ts
19722
19857
  init_cjs_shims();
@@ -19880,7 +20015,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
19880
20015
  graph.forEachNode((id, attrs) => {
19881
20016
  if (found) return;
19882
20017
  const a = attrs;
19883
- if (a.type === import_types70.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20018
+ if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
19884
20019
  found = id;
19885
20020
  }
19886
20021
  });
@@ -19892,7 +20027,7 @@ function findMatchingRouteNode(graph, serviceName, method, path78) {
19892
20027
  graph.forEachNode((id, attrs) => {
19893
20028
  if (found) return;
19894
20029
  const a = attrs;
19895
- if (a.type !== import_types70.NodeType.RouteNode || a.service !== serviceName) return;
20030
+ if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
19896
20031
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
19897
20032
  const routeMethod = (a.method ?? "").toUpperCase();
19898
20033
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -19911,11 +20046,11 @@ function createCloudflareResolveTarget(config, graph) {
19911
20046
  };
19912
20047
  const mapping = config.workers?.[scriptName];
19913
20048
  if (mapping) {
19914
- const wholeFileId = (0, import_types70.fileId)(mapping.service, mapping.entryFile);
20049
+ const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
19915
20050
  return {
19916
20051
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
19917
20052
  serviceName: mapping.service,
19918
- edgeType: import_types70.EdgeType.CALLS
20053
+ edgeType: import_types71.EdgeType.CALLS
19919
20054
  };
19920
20055
  }
19921
20056
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -19924,13 +20059,13 @@ function createCloudflareResolveTarget(config, graph) {
19924
20059
  return {
19925
20060
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
19926
20061
  serviceName: fileNode.service,
19927
- edgeType: import_types70.EdgeType.CALLS
20062
+ edgeType: import_types71.EdgeType.CALLS
19928
20063
  };
19929
20064
  }
19930
20065
  return {
19931
- targetNodeId: (0, import_types70.infraId)("cloudflare-worker", scriptName),
20066
+ targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
19932
20067
  serviceName: scriptName,
19933
- edgeType: import_types70.EdgeType.CALLS,
20068
+ edgeType: import_types71.EdgeType.CALLS,
19934
20069
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
19935
20070
  };
19936
20071
  };
@@ -20126,14 +20261,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
20126
20261
 
20127
20262
  // src/connectors/neon/resolve.ts
20128
20263
  init_cjs_shims();
20129
- var import_types74 = require("@neat.is/types");
20264
+ var import_types75 = require("@neat.is/types");
20130
20265
  function createNeonResolveTarget(config) {
20131
20266
  return (signal) => {
20132
20267
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20133
20268
  return {
20134
- targetNodeId: (0, import_types74.infraId)("sql-table", signal.targetName),
20269
+ targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
20135
20270
  serviceName: config.serviceName,
20136
- edgeType: import_types74.EdgeType.CALLS,
20271
+ edgeType: import_types75.EdgeType.CALLS,
20137
20272
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
20138
20273
  };
20139
20274
  };
@@ -20314,14 +20449,14 @@ function mapLogEntriesToSignals2(entries) {
20314
20449
 
20315
20450
  // src/connectors/cloud-run/resolve.ts
20316
20451
  init_cjs_shims();
20317
- var import_types78 = require("@neat.is/types");
20452
+ var import_types79 = require("@neat.is/types");
20318
20453
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
20319
20454
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
20320
20455
  let found = null;
20321
20456
  graph.forEachNode((_id, attrs) => {
20322
20457
  if (found) return;
20323
20458
  const node = attrs;
20324
- if (node.type !== import_types78.NodeType.RouteNode) return;
20459
+ if (node.type !== import_types79.NodeType.RouteNode) return;
20325
20460
  const route = attrs;
20326
20461
  if (route.service !== serviceName || !route.pathTemplate) return;
20327
20462
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20346,13 +20481,13 @@ function createCloudRunResolveTarget(graph, config) {
20346
20481
  normalizePathTemplate(path78)
20347
20482
  );
20348
20483
  if (routeNodeId) {
20349
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types78.EdgeType.CALLS };
20484
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
20350
20485
  }
20351
20486
  }
20352
20487
  return {
20353
- targetNodeId: (0, import_types78.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20488
+ targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20354
20489
  serviceName: mappedService ?? gcpServiceName,
20355
- edgeType: import_types78.EdgeType.CALLS,
20490
+ edgeType: import_types79.EdgeType.CALLS,
20356
20491
  ensureInfraNode: {
20357
20492
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
20358
20493
  name: gcpServiceName,
@@ -20534,14 +20669,14 @@ function mapLogEntriesToSignals3(entries) {
20534
20669
 
20535
20670
  // src/connectors/gcp-lb/resolve.ts
20536
20671
  init_cjs_shims();
20537
- var import_types82 = require("@neat.is/types");
20672
+ var import_types83 = require("@neat.is/types");
20538
20673
  var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
20539
20674
  function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
20540
20675
  let found = null;
20541
20676
  graph.forEachNode((_id, attrs) => {
20542
20677
  if (found) return;
20543
20678
  const node = attrs;
20544
- if (node.type !== import_types82.NodeType.RouteNode) return;
20679
+ if (node.type !== import_types83.NodeType.RouteNode) return;
20545
20680
  const route = attrs;
20546
20681
  if (route.service !== serviceName || !route.pathTemplate) return;
20547
20682
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20566,13 +20701,13 @@ function createGcpLbResolveTarget(graph, config) {
20566
20701
  normalizePathTemplate(path78)
20567
20702
  );
20568
20703
  if (routeNodeId) {
20569
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types82.EdgeType.CALLS };
20704
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
20570
20705
  }
20571
20706
  }
20572
20707
  return {
20573
- targetNodeId: (0, import_types82.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20708
+ targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20574
20709
  serviceName: mappedService ?? backendServiceName,
20575
- edgeType: import_types82.EdgeType.CALLS,
20710
+ edgeType: import_types83.EdgeType.CALLS,
20576
20711
  ensureInfraNode: {
20577
20712
  kind: GCP_LB_BACKEND_INFRA_KIND,
20578
20713
  name: backendServiceName,
@@ -20613,7 +20748,7 @@ function createGcpLbConnector(graph, config = {}) {
20613
20748
 
20614
20749
  // src/connectors/render/index.ts
20615
20750
  init_cjs_shims();
20616
- var import_types85 = require("@neat.is/types");
20751
+ var import_types86 = require("@neat.is/types");
20617
20752
 
20618
20753
  // src/connectors/render/types.ts
20619
20754
  init_cjs_shims();
@@ -20691,7 +20826,7 @@ function buildRenderRouteIndex(graph, serviceName) {
20691
20826
  const out = [];
20692
20827
  graph.forEachNode((_id, attrs) => {
20693
20828
  const node = attrs;
20694
- if (node.type !== import_types85.NodeType.RouteNode) return;
20829
+ if (node.type !== import_types86.NodeType.RouteNode) return;
20695
20830
  const route = attrs;
20696
20831
  if (route.service !== serviceName) return;
20697
20832
  out.push({
@@ -20776,7 +20911,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
20776
20911
  function createRenderResolveTarget(config) {
20777
20912
  return (signal) => {
20778
20913
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
20779
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
20914
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
20780
20915
  }
20781
20916
  return null;
20782
20917
  };
@@ -20914,21 +21049,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
20914
21049
 
20915
21050
  // src/connectors/planetscale/resolve.ts
20916
21051
  init_cjs_shims();
20917
- var import_types89 = require("@neat.is/types");
21052
+ var import_types90 = require("@neat.is/types");
20918
21053
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
20919
21054
  function createPlanetscaleResolveTarget(graph, config) {
20920
21055
  const databaseName = `${config.organization}/${config.database}`;
20921
21056
  return (signal, _ctx) => {
20922
21057
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20923
- const tableId = (0, import_types89.infraId)("sql-table", signal.targetName);
21058
+ const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
20924
21059
  if (graph.hasNode(tableId)) {
20925
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types89.EdgeType.CALLS };
21060
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
20926
21061
  }
20927
- const providerId = (0, import_types89.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21062
+ const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
20928
21063
  return {
20929
21064
  targetNodeId: providerId,
20930
21065
  serviceName: config.serviceName,
20931
- edgeType: import_types89.EdgeType.CALLS,
21066
+ edgeType: import_types90.EdgeType.CALLS,
20932
21067
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
20933
21068
  };
20934
21069
  };
@@ -21193,7 +21328,7 @@ function mapBuildsToSignals(builds, serviceName) {
21193
21328
 
21194
21329
  // src/connectors/eas/resolve.ts
21195
21330
  init_cjs_shims();
21196
- var import_types94 = require("@neat.is/types");
21331
+ var import_types95 = require("@neat.is/types");
21197
21332
  var NO_ENV2 = "unknown";
21198
21333
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
21199
21334
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -21209,8 +21344,8 @@ function configBasenamesForPhase(phase) {
21209
21344
  function configNodeService(graph, configNodeId) {
21210
21345
  for (const edgeId of graph.inboundEdges(configNodeId)) {
21211
21346
  const edge = graph.getEdgeAttributes(edgeId);
21212
- if (edge.type !== import_types94.EdgeType.CONFIGURED_BY) continue;
21213
- const parsed = (0, import_types94.parseFileId)(edge.source);
21347
+ if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
21348
+ const parsed = (0, import_types95.parseFileId)(edge.source);
21214
21349
  if (parsed) return parsed.service;
21215
21350
  }
21216
21351
  return null;
@@ -21221,7 +21356,7 @@ function findConfigNode(graph, basenames, serviceName) {
21221
21356
  graph.forEachNode((id, attrs) => {
21222
21357
  if (scoped) return;
21223
21358
  const node = attrs;
21224
- if (node.type !== import_types94.NodeType.ConfigNode) return;
21359
+ if (node.type !== import_types95.NodeType.ConfigNode) return;
21225
21360
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
21226
21361
  if (anyMatch === null) anyMatch = id;
21227
21362
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -21238,13 +21373,13 @@ function createEasResolveTarget(graph) {
21238
21373
  if (basenames.length > 0) {
21239
21374
  const configNodeId = findConfigNode(graph, basenames, serviceName);
21240
21375
  if (configNodeId) {
21241
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types94.EdgeType.CALLS };
21376
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
21242
21377
  }
21243
21378
  }
21244
21379
  return {
21245
21380
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
21246
21381
  serviceName,
21247
- edgeType: import_types94.EdgeType.CALLS
21382
+ edgeType: import_types95.EdgeType.CALLS
21248
21383
  };
21249
21384
  };
21250
21385
  }
@@ -21831,6 +21966,7 @@ async function startConnectorPolling(input) {
21831
21966
  registration.connector,
21832
21967
  {
21833
21968
  projectDir: input.projectDir,
21969
+ project: input.project,
21834
21970
  credentials: registration.credentials,
21835
21971
  ...input.errorsPath ? { errorsPath: input.errorsPath } : {}
21836
21972
  },
@@ -22005,11 +22141,11 @@ function registerRoutes(scope, ctx) {
22005
22141
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
22006
22142
  const parsed = [];
22007
22143
  for (const c of candidates) {
22008
- const r = import_types97.DivergenceTypeSchema.safeParse(c);
22144
+ const r = import_types98.DivergenceTypeSchema.safeParse(c);
22009
22145
  if (!r.success) {
22010
22146
  return reply.code(400).send({
22011
22147
  error: `unknown divergence type "${c}"`,
22012
- allowed: import_types97.DivergenceTypeSchema.options
22148
+ allowed: import_types98.DivergenceTypeSchema.options
22013
22149
  });
22014
22150
  }
22015
22151
  parsed.push(r.data);
@@ -22126,6 +22262,7 @@ function registerRoutes(scope, ctx) {
22126
22262
  reg.connector,
22127
22263
  {
22128
22264
  projectDir: proj.scanPath ?? "",
22265
+ project: proj.name,
22129
22266
  credentials: reg.credentials,
22130
22267
  ...incidentsPath ? { errorsPath: incidentsPath } : {}
22131
22268
  },
@@ -22189,6 +22326,39 @@ function registerRoutes(scope, ctx) {
22189
22326
  if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
22190
22327
  return result;
22191
22328
  });
22329
+ scope.get("/graph/incident-card/:nodeId", async (req2, reply) => {
22330
+ const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
22331
+ if (!proj) return;
22332
+ const { nodeId } = req2.params;
22333
+ if (!proj.graph.hasNode(nodeId)) {
22334
+ return reply.code(404).send({ error: "node not found", id: nodeId });
22335
+ }
22336
+ const epath = errorsPathFor(proj);
22337
+ const incidents = epath ? await readErrorEvents(epath) : [];
22338
+ let errorEvent;
22339
+ if (req2.query.errorId) {
22340
+ errorEvent = incidents.find((e) => e.id === req2.query.errorId);
22341
+ if (!errorEvent) {
22342
+ return reply.code(404).send({ error: "error event not found", id: req2.query.errorId });
22343
+ }
22344
+ } else {
22345
+ const svc = nodeId.replace(/^service:/, "");
22346
+ errorEvent = [...incidents].filter((e) => e.affectedNode === nodeId || e.service === svc).sort((a, b) => (b.timestamp ?? "").localeCompare(a.timestamp ?? ""))[0];
22347
+ if (!errorEvent) {
22348
+ return reply.code(404).send({ error: "no incident found for node", id: nodeId });
22349
+ }
22350
+ }
22351
+ const policyPath = ctx.policyFilePathFor(proj);
22352
+ let policies = [];
22353
+ if (policyPath) {
22354
+ try {
22355
+ policies = await loadPolicyFile(policyPath);
22356
+ } catch {
22357
+ policies = [];
22358
+ }
22359
+ }
22360
+ return buildIncidentCard(proj.graph, errorEvent, incidents, policies);
22361
+ });
22192
22362
  scope.get("/graph/blast-radius/:nodeId", async (req2, reply) => {
22193
22363
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
22194
22364
  if (!proj) return;
@@ -22371,7 +22541,7 @@ function registerRoutes(scope, ctx) {
22371
22541
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
22372
22542
  let violations = await log.readAll();
22373
22543
  if (req2.query.severity) {
22374
- const sev = import_types97.PolicySeveritySchema.safeParse(req2.query.severity);
22544
+ const sev = import_types98.PolicySeveritySchema.safeParse(req2.query.severity);
22375
22545
  if (!sev.success) {
22376
22546
  return reply.code(400).send({
22377
22547
  error: "invalid severity",
@@ -22410,7 +22580,7 @@ function registerRoutes(scope, ctx) {
22410
22580
  scope.post("/policies/check", async (req2, reply) => {
22411
22581
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
22412
22582
  if (!proj) return;
22413
- const parsed = import_types97.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
22583
+ const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
22414
22584
  if (!parsed.success) {
22415
22585
  return reply.code(400).send({
22416
22586
  error: "invalid /policies/check body",
@@ -22751,7 +22921,7 @@ function unroutedErrorsPath(neatHome4) {
22751
22921
  }
22752
22922
 
22753
22923
  // src/daemon.ts
22754
- var import_types98 = require("@neat.is/types");
22924
+ var import_types99 = require("@neat.is/types");
22755
22925
  function daemonJsonPath(scanPath) {
22756
22926
  return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
22757
22927
  }
@@ -22890,7 +23060,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
22890
23060
  if (!serviceName) return true;
22891
23061
  if (serviceNameMatchesProject(serviceName, project)) return true;
22892
23062
  return graph.someNode(
22893
- (_id, attrs) => attrs.type === import_types98.NodeType.ServiceNode && attrs.name === serviceName
23063
+ (_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
22894
23064
  );
22895
23065
  }
22896
23066
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
@@ -23345,7 +23515,12 @@ async function startDaemon(opts = {}) {
23345
23515
  onErrorSpanSync: async (span) => {
23346
23516
  const slot = await resolveTargetSlot(span.service, span.traceId);
23347
23517
  if (!slot) return;
23348
- await makeErrorSpanWriter(slot.paths.errorsPath, slot.graph, slot.entry.path)(span);
23518
+ await makeErrorSpanWriter(
23519
+ slot.paths.errorsPath,
23520
+ slot.graph,
23521
+ slot.entry.path,
23522
+ slot.entry.name
23523
+ )(span);
23349
23524
  },
23350
23525
  // Project-scoped route (issue #367) — the URL already named the
23351
23526
  // project. Resolution is a direct slot lookup; service.name resolves
@@ -23368,7 +23543,12 @@ async function startDaemon(opts = {}) {
23368
23543
  onProjectErrorSpanSync: async (project, span) => {
23369
23544
  const slot = await resolveSlotByName(project, span.service, span.traceId);
23370
23545
  if (!slot) return;
23371
- await makeErrorSpanWriter(slot.paths.errorsPath, slot.graph, slot.entry.path)(span);
23546
+ await makeErrorSpanWriter(
23547
+ slot.paths.errorsPath,
23548
+ slot.graph,
23549
+ slot.entry.path,
23550
+ slot.entry.name
23551
+ )(span);
23372
23552
  },
23373
23553
  // #881 — 404 a project-scoped POST for a project this daemon doesn't
23374
23554
  // host, rather than accepting it and dropping the batch. `slots` covers