{"version":3,"sources":["../../../node_modules/tsup/assets/cjs_shims.js","../src/auth.ts","../src/otel-grpc.ts","../src/otel.ts","../src/server.ts","../src/graph.ts","../src/api.ts","../src/extend/index.ts","../src/installers/package-manager.ts","../src/divergences.ts","../src/compat.ts","../compat.json","../src/traverse.ts","../src/policy.ts","../src/events.ts","../src/extract.ts","../src/extract/index.ts","../src/ingest.ts","../src/extract/services.ts","../src/extract/shared.ts","../src/extract/python.ts","../src/extract/owners.ts","../src/extract/errors.ts","../src/extract/aliases.ts","../src/extract/files.ts","../src/extract/calls/shared.ts","../src/extract/imports.ts","../src/extract/databases/index.ts","../src/extract/databases/db-config-yaml.ts","../src/extract/databases/dotenv.ts","../src/extract/databases/shared.ts","../src/extract/databases/prisma.ts","../src/extract/databases/drizzle.ts","../src/extract/databases/knex.ts","../src/extract/databases/ormconfig.ts","../src/extract/databases/typeorm.ts","../src/extract/databases/sequelize.ts","../src/extract/databases/docker-compose.ts","../src/extract/configs.ts","../src/extract/routes.ts","../src/extract/proto.ts","../src/extract/calls/index.ts","../src/extract/calls/http.ts","../src/extract/calls/route-match.ts","../src/extract/calls/kafka.ts","../src/extract/calls/redis.ts","../src/extract/calls/aws.ts","../src/extract/calls/grpc.ts","../src/extract/calls/supabase.ts","../src/extract/infra/index.ts","../src/extract/infra/docker-compose.ts","../src/extract/infra/shared.ts","../src/extract/infra/dockerfile.ts","../src/extract/infra/terraform.ts","../src/extract/infra/k8s.ts","../src/extract/retire.ts","../src/diff.ts","../src/persist.ts","../src/projects.ts","../src/registry.ts","../src/streaming.ts","../src/search.ts"],"sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n  typeof document === \"undefined\" \n    ? new URL(`file:${__filename}`).href \n    : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n      ? document.currentScript.src \n      : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * Delegated auth at the daemon boundary (ADR-073 §3 + §4).\n *\n * NEAT does not issue, rotate, or distribute the token — that is the deploy\n * platform's job. This module provides the two surfaces the daemon needs:\n *\n *   - `assertBindAuthority(host, token)` — fail-loud pre-bind check. When no\n *     token is set, the daemon refuses to bind on any non-loopback address.\n *     Loopback-only without a token stays unauthenticated (laptop dev path).\n *\n *   - `mountBearerAuth(app, opts)` — Fastify `preHandler` that requires\n *     `Authorization: Bearer <token>` on every request other than the\n *     unauthenticated health/readiness probes. Constant-time comparison.\n *\n * The same shape covers both the REST host and the OTLP receivers; the OTLP\n * side passes a different token (`NEAT_OTEL_TOKEN ?? NEAT_AUTH_TOKEN`) so the\n * two surfaces rotate independently.\n */\n\nimport { timingSafeEqual } from 'node:crypto'\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'\n\n// Hosts that count as loopback for the bind-authority gate. `0.0.0.0` is\n// explicitly not loopback — it binds on every interface, including any\n// public one the operator's box happens to have.\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n  '127.0.0.1',\n  'localhost',\n  '::1',\n  '::ffff:127.0.0.1',\n])\n\nexport function isLoopbackHost(host: string | undefined | null): boolean {\n  if (!host) return false\n  return LOOPBACK_HOSTS.has(host)\n}\n\nexport class BindAuthorityError extends Error {\n  constructor(host: string) {\n    super(\n      `NEAT refuses to bind on a public interface without \\`NEAT_AUTH_TOKEN\\` set (host=\"${host}\"). Set the token or bind to loopback only.`,\n    )\n    this.name = 'BindAuthorityError'\n  }\n}\n\nexport function assertBindAuthority(host: string, token: string | undefined): void {\n  if (token && token.length > 0) return\n  if (isLoopbackHost(host)) return\n  throw new BindAuthorityError(host)\n}\n\nexport interface AuthOptions {\n  // Bearer token required on every protected route. Undefined / empty → the\n  // middleware is not mounted and the route stays unauthenticated. The\n  // loopback-only gate above is what keeps that case from being public.\n  token?: string\n  // When `true`, trust an upstream reverse proxy and skip the request-side\n  // check. The fail-loud bind-authority gate still applies upstream. Wired\n  // to `NEAT_AUTH_PROXY=true` in production.\n  trustProxy?: boolean\n  // ADR-073 §3 amendment — public-read mode for reference deployments.\n  // When `true`, GET / HEAD / OPTIONS pass through without a bearer; every\n  // other verb still requires the token. OTLP ingest is excluded — that\n  // surface stays gated unconditionally (the receiver mounts its own\n  // middleware without this flag).\n  publicRead?: boolean\n  // Extra paths (or path suffixes) to leave unauthenticated. Used by tests\n  // and by ad-hoc callers that mount their own probes.\n  extraUnauthenticatedSuffixes?: ReadonlyArray<string>\n  // Diagnostic hook fired whenever a request is rejected with 401 for a\n  // missing or invalid bearer. The REST host leaves this unset — a human\n  // running `curl` doesn't need a server-side line for their own 401. The\n  // OTLP receiver passes it so an app whose telemetry is being dropped for a\n  // bad token leaves a signal on the daemon side instead of failing silently.\n  // The hook does not change the response body or status; it only observes.\n  onReject?: () => void\n}\n\n// Verbs the public-read split treats as reads. Everything else is a write\n// and keeps the bearer requirement.\nconst PUBLIC_READ_METHODS: ReadonlySet<string> = new Set(['GET', 'HEAD', 'OPTIONS'])\n\n// Probes that always stay open. Dual-mounted under `/projects/:project/` too,\n// so the check is a suffix match — `/projects/foo/health` is skipped along\n// with the top-level `/health`. ADR-073 §3 names `/healthz` and `/readyz`\n// explicitly; `/health` is the existing endpoint the web shell and CI smoke\n// already lean on, so it keeps the unauthenticated treatment. `/api/config`\n// is the public-read negotiation endpoint — the web shell hits it before any\n// authed call to learn which mode the daemon is running in.\nconst DEFAULT_UNAUTH_SUFFIXES: ReadonlyArray<string> = [\n  '/health',\n  '/healthz',\n  '/readyz',\n  '/api/config',\n]\n\nexport function mountBearerAuth(app: FastifyInstance, opts: AuthOptions): void {\n  if (!opts.token || opts.token.length === 0) return\n  if (opts.trustProxy) return\n\n  const expected = Buffer.from(opts.token, 'utf8')\n  const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...(opts.extraUnauthenticatedSuffixes ?? [])]\n  const publicRead = opts.publicRead === true\n\n  app.addHook('preHandler', (req: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void) => {\n    const path = (req.url.split('?')[0] ?? '').replace(/\\/+$/, '')\n    for (const suffix of suffixes) {\n      if (path === suffix || path.endsWith(suffix)) {\n        done()\n        return\n      }\n    }\n\n    // Public-read split: GET / HEAD / OPTIONS pass through anonymously, every\n    // other verb keeps the bearer check. The token still authorizes writes,\n    // and the bind-authority gate above still demands a token for non-loopback\n    // binds — public-read enables anonymous reads on top of that, it doesn't\n    // replace either invariant.\n    if (publicRead && PUBLIC_READ_METHODS.has(req.method)) {\n      done()\n      return\n    }\n\n    const header = req.headers.authorization\n    if (typeof header !== 'string' || !header.startsWith('Bearer ')) {\n      opts.onReject?.()\n      void reply.code(401).send({ error: 'unauthorized' })\n      return\n    }\n    const provided = Buffer.from(header.slice('Bearer '.length).trim(), 'utf8')\n    if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {\n      opts.onReject?.()\n      void reply.code(401).send({ error: 'unauthorized' })\n      return\n    }\n    done()\n  })\n}\n\n// Read both tokens from the environment in one place so server.ts, daemon.ts,\n// and the OTel receivers all agree on precedence (ADR-073 §4). `publicRead`\n// rides the same shape so callers don't need a second env read.\nexport interface AuthEnv {\n  authToken: string | undefined\n  otelToken: string | undefined\n  trustProxy: boolean\n  publicRead: boolean\n}\n\nfunction parseBoolEnv(v: string | undefined): boolean {\n  if (!v) return false\n  return v === 'true' || v === '1'\n}\n\nexport function readAuthEnv(env: NodeJS.ProcessEnv = process.env): AuthEnv {\n  const t = env.NEAT_AUTH_TOKEN\n  const ot = env.NEAT_OTEL_TOKEN\n  return {\n    authToken: t && t.length > 0 ? t : undefined,\n    otelToken: ot && ot.length > 0 ? ot : t && t.length > 0 ? t : undefined,\n    trustProxy: env.NEAT_AUTH_PROXY === 'true',\n    publicRead: parseBoolEnv(env.NEAT_PUBLIC_READ),\n  }\n}\n","import { fileURLToPath } from 'node:url'\nimport path from 'node:path'\nimport { timingSafeEqual } from 'node:crypto'\nimport * as grpc from '@grpc/grpc-js'\nimport * as protoLoader from '@grpc/proto-loader'\nimport {\n  parseOtlpRequest,\n  type OtlpTracesRequest,\n  type ParsedSpan,\n  type SpanHandler,\n} from './otel.js'\n\n// OTLP/gRPC receiver. Sits next to buildOtelReceiver (HTTP/JSON) in otel.ts;\n// shares the same parseOtlpRequest decoder so a span looks identical to the\n// downstream onSpan handler whether it came in over JSON or protobuf.\n//\n// Default OFF — opts.enabled (typically NEAT_OTLP_GRPC=true) decides whether\n// server.ts wires this up. We keep gRPC behind a flag so existing HTTP-only\n// deployments don't get a surprise port binding on upgrade.\n\nexport interface BuildOtelGrpcReceiverOptions {\n  onSpan: SpanHandler\n  // ADR-073 §4 — bearer required in the gRPC call's `authorization` metadata\n  // when set. Same precedence as the HTTP receiver: NEAT_OTEL_TOKEN ?? NEAT_AUTH_TOKEN.\n  authToken?: string\n  // Skip the request-side check when an upstream proxy already authenticated.\n  trustProxy?: boolean\n}\n\n// proto-loader output for the trace service has fields like resource_spans,\n// scope_spans, span_id, etc. (snake_case keys, since we leave keepCase: true\n// when loading). The HTTP path uses camelCase JSON, so we shape-shift the\n// gRPC payload onto the HTTP shape and let parseOtlpRequest do the rest.\n//\n// All `bytes` fields arrive as Buffers; the HTTP wire format encodes them as\n// hex strings, so we hex-encode for consistency.\n\ninterface GrpcAnyValue {\n  string_value?: string\n  bool_value?: boolean\n  int_value?: string | number\n  double_value?: number\n  array_value?: { values?: GrpcAnyValue[] }\n  // bytes/kvlist fields exist in the proto but the demo doesn't use them.\n}\n\ninterface GrpcKeyValue {\n  key?: string\n  value?: GrpcAnyValue\n}\n\ninterface GrpcStatus {\n  code?: number\n  message?: string\n}\n\ninterface GrpcEvent {\n  name?: string\n  time_unix_nano?: string | number\n  attributes?: GrpcKeyValue[]\n}\n\ninterface GrpcSpan {\n  trace_id?: Buffer | Uint8Array\n  span_id?: Buffer | Uint8Array\n  parent_span_id?: Buffer | Uint8Array\n  name?: string\n  kind?: number\n  start_time_unix_nano?: string | number\n  end_time_unix_nano?: string | number\n  attributes?: GrpcKeyValue[]\n  events?: GrpcEvent[]\n  status?: GrpcStatus\n}\n\ninterface GrpcScopeSpans {\n  spans?: GrpcSpan[]\n}\n\ninterface GrpcResourceSpans {\n  resource?: { attributes?: GrpcKeyValue[] }\n  scope_spans?: GrpcScopeSpans[]\n}\n\ninterface GrpcExportRequest {\n  resource_spans?: GrpcResourceSpans[]\n}\n\nfunction bytesToHex(buf: Buffer | Uint8Array | undefined): string {\n  if (!buf) return ''\n  if (Buffer.isBuffer(buf)) return buf.toString('hex')\n  // protobufjs hands back plain Uint8Arrays in environments where its Buffer\n  // detection misses; hex-encode those too rather than silently emptying the\n  // span's identity (#468 — empty traceId/spanId killed every protobuf span).\n  if (buf instanceof Uint8Array) return Buffer.from(buf).toString('hex')\n  return ''\n}\n\nfunction nanosToString(n: string | number | undefined): string {\n  if (n === undefined || n === null) return '0'\n  return typeof n === 'string' ? n : String(n)\n}\n\nfunction reshapeAttributes(\n  attrs: GrpcKeyValue[] | undefined,\n): OtlpTracesRequest['resourceSpans'] extends Array<infer R>\n  ? R extends { resource?: { attributes?: infer A } }\n    ? A\n    : never\n  : never {\n  // Map snake_case oneof fields to the camelCase the JSON path expects.\n  const out = (attrs ?? []).map((kv) => ({\n    key: kv.key ?? '',\n    value: kv.value\n      ? {\n          stringValue: kv.value.string_value,\n          boolValue: kv.value.bool_value,\n          intValue: kv.value.int_value,\n          doubleValue: kv.value.double_value,\n          arrayValue: kv.value.array_value\n            ? {\n                values: (kv.value.array_value.values ?? []).map((v) => ({\n                  stringValue: v.string_value,\n                  boolValue: v.bool_value,\n                  intValue: v.int_value,\n                  doubleValue: v.double_value,\n                })),\n              }\n            : undefined,\n        }\n      : undefined,\n  }))\n  return out as never\n}\n\nexport function reshapeGrpcRequest(req: GrpcExportRequest): OtlpTracesRequest {\n  return {\n    resourceSpans: (req.resource_spans ?? []).map((rs) => ({\n      resource: rs.resource ? { attributes: reshapeAttributes(rs.resource.attributes) } : undefined,\n      scopeSpans: (rs.scope_spans ?? []).map((ss) => ({\n        spans: (ss.spans ?? []).map((s) => ({\n          traceId: bytesToHex(s.trace_id),\n          spanId: bytesToHex(s.span_id),\n          parentSpanId: s.parent_span_id ? bytesToHex(s.parent_span_id) : undefined,\n          name: s.name,\n          kind: s.kind,\n          startTimeUnixNano: nanosToString(s.start_time_unix_nano),\n          endTimeUnixNano: nanosToString(s.end_time_unix_nano),\n          attributes: reshapeAttributes(s.attributes),\n          events: (s.events ?? []).map((e) => ({\n            name: e.name,\n            timeUnixNano: nanosToString(e.time_unix_nano),\n            attributes: reshapeAttributes(e.attributes),\n          })),\n          status: s.status ? { code: s.status.code, message: s.status.message } : undefined,\n        })),\n      })),\n    })),\n  }\n}\n\n// Find the bundled .proto tree at packages/core/proto/. The dev server runs\n// from the source tree (tsx); the built bundles run from dist/. tsup keeps\n// source layout, so __dirname-relative resolution works for both — we look two\n// levels up from this file.\nfunction resolveProtoRoot(): string {\n  // Built output (CJS) sets __dirname natively; ESM build is bundled by tsup\n  // and keeps a dirname injection. import.meta.url is the safe bet.\n  const here = path.dirname(fileURLToPath(import.meta.url))\n  // src/ → packages/core/proto/, dist/ → packages/core/proto/.\n  return path.resolve(here, '..', 'proto')\n}\n\nfunction loadTraceService(): grpc.ServiceDefinition {\n  const protoRoot = resolveProtoRoot()\n  const def = protoLoader.loadSync(\n    'opentelemetry/proto/collector/trace/v1/trace_service.proto',\n    {\n      keepCase: true,\n      longs: String,\n      enums: Number,\n      defaults: true,\n      oneofs: true,\n      includeDirs: [protoRoot],\n    },\n  )\n  const pkg = grpc.loadPackageDefinition(def) as unknown as {\n    opentelemetry: {\n      proto: {\n        collector: {\n          trace: {\n            v1: {\n              TraceService: { service: grpc.ServiceDefinition }\n            }\n          }\n        }\n      }\n    }\n  }\n  return pkg.opentelemetry.proto.collector.trace.v1.TraceService.service\n}\n\nexport interface OtelGrpcReceiver {\n  // Bound address (host:port) once .start() has resolved. Useful for tests.\n  address: string\n  // Stop accepting new requests, shut down the server.\n  stop: () => Promise<void>\n}\n\nexport async function startOtelGrpcReceiver(\n  opts: BuildOtelGrpcReceiverOptions & { host?: string; port?: number },\n): Promise<OtelGrpcReceiver> {\n  const server = new grpc.Server()\n  const service = loadTraceService()\n\n  const requiresAuth = !opts.trustProxy && !!opts.authToken && opts.authToken.length > 0\n  const expectedHeader = requiresAuth ? `Bearer ${opts.authToken}` : ''\n\n  server.addService(service, {\n    Export: (\n      call: grpc.ServerUnaryCall<GrpcExportRequest, unknown>,\n      callback: grpc.sendUnaryData<{ partial_success: object }>,\n    ) => {\n      // ADR-073 §4 — same bearer shape as the HTTP receiver, carried in the\n      // `authorization` gRPC metadata header. Constant-time comparison.\n      if (requiresAuth) {\n        const meta = call.metadata.get('authorization')\n        const got = meta.length > 0 ? String(meta[0]) : ''\n        const a = Buffer.from(got, 'utf8')\n        const b = Buffer.from(expectedHeader, 'utf8')\n        const ok = a.length === b.length && timingSafeEqual(a, b)\n        if (!ok) {\n          callback({ code: grpc.status.UNAUTHENTICATED, message: 'unauthorized' })\n          return\n        }\n      }\n      void (async () => {\n        try {\n          const reshaped = reshapeGrpcRequest(call.request ?? {})\n          const spans: ParsedSpan[] = parseOtlpRequest(reshaped)\n          for (const span of spans) {\n            await opts.onSpan(span)\n          }\n          callback(null, { partial_success: {} })\n        } catch (err) {\n          callback({\n            code: grpc.status.INTERNAL,\n            message: err instanceof Error ? err.message : String(err),\n          })\n        }\n      })()\n    },\n  })\n\n  const host = opts.host ?? '0.0.0.0'\n  const port = opts.port ?? 4317\n\n  const boundPort = await new Promise<number>((resolve, reject) => {\n    server.bindAsync(`${host}:${port}`, grpc.ServerCredentials.createInsecure(), (err, p) => {\n      if (err) return reject(err)\n      resolve(p)\n    })\n  })\n\n  return {\n    address: `${host}:${boundPort}`,\n    stop: () =>\n      new Promise<void>((resolve) => {\n        server.tryShutdown(() => resolve())\n      }),\n  }\n}\n","import path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport Fastify, { type FastifyInstance } from 'fastify'\nimport protobuf from 'protobufjs'\nimport { mountBearerAuth } from './auth.js'\n\n// OTLP/HTTP receiver. Listens on /v1/traces and decodes the JSON wire format\n// (collector's `otlphttp` exporter with `encoding: json`). Each span is\n// flattened into a ParsedSpan and handed to the configured handler. The\n// handler is the seam #8 wires its edge mapper into; #7 itself stays decoupled\n// from graph mutation.\n\nexport interface ParsedSpan {\n  service: string\n  // True when the resource carried a `service.name` attribute. False (or\n  // omitted, for legacy producers) routes the span to `service:unidentified`\n  // in the resolved project and trips a once-per-session-per-project warning\n  // on the ingest side (issue #374). OTel spec requires SDKs to set\n  // `service.name`, but customised exporters can omit it — diagnostic\n  // visibility beats silent drop. Field is optional so test fixtures that\n  // hand-construct ParsedSpan with a known service.name don't need to set\n  // a flag they don't care about; the receiver always sets it.\n  // See docs/contracts/otlp-routing.md §Fallback when `resource.service.name`\n  // is missing.\n  resourceServiceNamePresent?: boolean\n  traceId: string\n  spanId: string\n  parentSpanId?: string\n  name: string\n  kind?: number\n  startTimeUnixNano: string\n  endTimeUnixNano: string\n  // ISO8601 derived from startTimeUnixNano. Production paths (lastObserved on\n  // OBSERVED edges) read this so the recorded time reflects when the span fired,\n  // not when the receiver received it. Undefined only when startTimeUnixNano is\n  // missing or unparseable — handler falls back to wall-clock in that case.\n  // See docs/contracts/otel-ingest.md §lastObserved-from-span-time.\n  startTimeIso?: string\n  // bigint so the 9-digit-nanos arithmetic doesn't lose precision on long traces.\n  durationNanos: bigint\n  // Deployment environment from `deployment.environment(.name)`. Span attrs\n  // win over resource attrs (per-span overrides a resource-wide declaration);\n  // the canonical `deployment.environment.name` (OTel SC v1.27+) wins over\n  // the compat form `deployment.environment`. Literal `'unknown'` is the\n  // honest fallback when no env signal is present anywhere on the span or\n  // its resource. See ADR-074 §2 / docs/contracts/env-dimension.md.\n  env: string\n  attributes: Record<string, AttributeValue>\n  // Convenience accessors for the attributes #8 cares about.\n  dbSystem?: string\n  dbName?: string\n  // Messaging semconv (OTel). `messaging.system` names the broker family\n  // (kafka, rabbitmq, redis, …); the destination is the topic/queue the span\n  // produced to or consumed from — the canonical `messaging.destination.name`\n  // (SC v1.24+) with the legacy `messaging.destination` as fallback. handleSpan\n  // reads these to mint a PUBLISHES_TO (PRODUCER) or CONSUMES_FROM (CONSUMER)\n  // OBSERVED edge to the destination node, fusing with the static extractor's\n  // topic node. See docs/contracts/otel-ingest.md §Queue producers and consumers.\n  messagingSystem?: string\n  messagingDestination?: string\n  // GraphQL semconv (OTel). `graphql.operation.name` is the client-supplied\n  // operation name the execution span resolved (`GetUser`); `graphql.operation.type`\n  // is the operation kind (`query` / `mutation` / `subscription`). handleSpan reads\n  // both to mint an OBSERVED `CONTAINS` edge from the serving service to a\n  // per-operation node, recovering the operation-level topology that HTTP grain\n  // collapses onto `POST /graphql`. See docs/contracts/otel-ingest.md §GraphQL\n  // operations.\n  graphqlOperationName?: string\n  graphqlOperationType?: string\n  // gRPC RPC semconv (OTel). `rpc.system` names the RPC framework (`grpc`);\n  // `rpc.service` is the fully-qualified proto service (`orders.OrderService`)\n  // and `rpc.method` the bare method (`GetOrder`). The serving (SERVER) and\n  // calling (CLIENT) sides both carry these. handleSpan reads them off the\n  // serving span to mint an OBSERVED `CONTAINS` edge from the serving service to\n  // a per-method node, recovering the method-level topology gRPC's service-grain\n  // edge collapses — and keyed so the static `.proto` definition fuses onto the\n  // same node. See docs/contracts/otel-ingest.md §gRPC methods.\n  rpcSystem?: string\n  rpcService?: string\n  rpcMethod?: string\n  // WebSocket channel (OTel HTTP semconv). A WebSocket connection opens with an\n  // HTTP upgrade handshake: a SERVER `GET` whose `Upgrade` request header names\n  // `websocket`. That single span is the only reliable OBSERVED signal a channel\n  // exists — the message frames afterwards ride the socket, not more spans. This\n  // field carries the channel path off that upgrade span (`http.route` when the\n  // router templated it, else `url.path` / `http.target`), and is set only when\n  // the upgrade header is present. handleSpan reads it to mint a\n  // WebSocketChannelNode and an OBSERVED `CONNECTS_TO` edge from the serving\n  // service to it. See docs/contracts/otel-ingest.md §WebSocket channels.\n  websocketChannel?: string\n  // 0 = UNSET, 1 = OK, 2 = ERROR per OTLP. We only care that 2 means error.\n  statusCode?: number\n  errorMessage?: string\n  // Pre-extracted from a span event with name=\"exception\". OTLP SDKs record\n  // exceptions this way (richer than status.message). handleSpan reads these\n  // first, falling back to status.message and span.name. See\n  // docs/contracts/otel-ingest.md §exception-data-from-span-events.\n  exception?: {\n    type?: string\n    message?: string\n    stacktrace?: string\n  }\n}\n\nexport type AttributeValue =\n  | string\n  | number\n  | boolean\n  | bigint\n  | string[]\n  | number[]\n  | boolean[]\n  | null\n\nexport type SpanHandler = (span: ParsedSpan) => void | Promise<void>\n// Variant that receives the project the URL already resolved to. Used by the\n// project-scoped route mounted at `/projects/:project/v1/traces` (issue #367):\n// the receiver hands the URL-path project to the daemon directly so the\n// daemon never has to guess via `service.name`-against-registry matching.\nexport type ProjectSpanHandler = (project: string, span: ParsedSpan) => void | Promise<void>\n\nexport interface BuildOtelReceiverOptions {\n  onSpan: SpanHandler\n  // Synchronous handler for spans with statusCode === 2. The receiver awaits\n  // it before replying, so a write failure can return 500 → OTel SDK retries.\n  // Optional — wiring is expected to plumb appendErrorEvent here when error\n  // durability matters; ad-hoc receivers leave it undefined.\n  // See docs/contracts/otel-ingest.md §Error events.\n  onErrorSpanSync?: (span: ParsedSpan) => Promise<void>\n  // Project-scoped variants — used by the `/projects/:project/v1/traces`\n  // route. When unset the route is still mounted but falls through to the\n  // legacy `onSpan` / `onErrorSpanSync` handlers (the project name is then\n  // available to the consumer via the `OTEL_PROJECT_OVERRIDE` attribute on\n  // each parsed span; ad-hoc receivers commonly leave both unset).\n  onProjectSpan?: ProjectSpanHandler\n  onProjectErrorSpanSync?: (project: string, span: ParsedSpan) => Promise<void>\n  // Fastify body limit. OTLP batches can be large; default is 16 MB.\n  bodyLimit?: number\n  // ADR-073 §4 — bearer required on `/v1/traces`. Defaults to `NEAT_AUTH_TOKEN`\n  // when unset; `NEAT_OTEL_TOKEN` overrides at the call site so the REST and\n  // OTLP surfaces rotate on independent schedules.\n  authToken?: string\n  // Same shape as the REST middleware: skip the request-side check when an\n  // upstream reverse proxy already authenticated.\n  trustProxy?: boolean\n}\n\ninterface OtlpKeyValue {\n  key: string\n  value?: OtlpAnyValue\n}\n\ninterface OtlpAnyValue {\n  stringValue?: string\n  intValue?: string | number\n  doubleValue?: number\n  boolValue?: boolean\n  arrayValue?: { values?: OtlpAnyValue[] }\n  // kvlistValue / bytesValue are skipped — neither is on the demo path.\n}\n\ninterface OtlpStatus {\n  code?: number\n  message?: string\n}\n\ninterface OtlpEvent {\n  name?: string\n  timeUnixNano?: string\n  attributes?: OtlpKeyValue[]\n}\n\ninterface OtlpSpan {\n  traceId?: string\n  spanId?: string\n  parentSpanId?: string\n  name?: string\n  kind?: number\n  startTimeUnixNano?: string\n  endTimeUnixNano?: string\n  attributes?: OtlpKeyValue[]\n  events?: OtlpEvent[]\n  status?: OtlpStatus\n}\n\nfunction extractExceptionFromEvents(events: OtlpEvent[] | undefined): ParsedSpan['exception'] {\n  if (!events) return undefined\n  for (const ev of events) {\n    if (ev.name !== 'exception') continue\n    const attrs = attrsToRecord(ev.attributes)\n    const out: ParsedSpan['exception'] = {}\n    const t = attrs['exception.type']\n    const m = attrs['exception.message']\n    const s = attrs['exception.stacktrace']\n    if (typeof t === 'string') out.type = t\n    if (typeof m === 'string') out.message = m\n    if (typeof s === 'string') out.stacktrace = s\n    if (out.type || out.message || out.stacktrace) return out\n  }\n  return undefined\n}\n\ninterface OtlpScopeSpans {\n  spans?: OtlpSpan[]\n}\n\ninterface OtlpResourceSpans {\n  resource?: { attributes?: OtlpKeyValue[] }\n  scopeSpans?: OtlpScopeSpans[]\n}\n\nexport interface OtlpTracesRequest {\n  resourceSpans?: OtlpResourceSpans[]\n}\n\nfunction flattenAttribute(v: OtlpAnyValue | undefined): AttributeValue {\n  if (!v) return null\n  if (v.stringValue !== undefined) return v.stringValue\n  if (v.boolValue !== undefined) return v.boolValue\n  if (v.intValue !== undefined) {\n    return typeof v.intValue === 'string' ? Number(v.intValue) : v.intValue\n  }\n  if (v.doubleValue !== undefined) return v.doubleValue\n  if (v.arrayValue?.values) {\n    return v.arrayValue.values.map((x) => flattenAttribute(x)) as AttributeValue\n  }\n  return null\n}\n\nfunction attrsToRecord(attrs: OtlpKeyValue[] | undefined): Record<string, AttributeValue> {\n  const out: Record<string, AttributeValue> = {}\n  if (!attrs) return out\n  for (const kv of attrs) {\n    if (kv.key) out[kv.key] = flattenAttribute(kv.value)\n  }\n  return out\n}\n\nfunction durationNanos(start?: string, end?: string): bigint {\n  if (!start || !end) return 0n\n  try {\n    return BigInt(end) - BigInt(start)\n  } catch {\n    return 0n\n  }\n}\n\n// Convert OTLP's startTimeUnixNano (a base-10 string of nanoseconds since the\n// Unix epoch) to ISO8601. Returns undefined when the input is missing, zero,\n// or unparseable, so the caller can fall back to wall-clock without surfacing\n// a fake timestamp on the edge.\nexport function isoFromUnixNano(nanos: string | undefined): string | undefined {\n  if (!nanos || nanos === '0') return undefined\n  try {\n    const ms = Number(BigInt(nanos) / 1_000_000n)\n    if (!Number.isFinite(ms)) return undefined\n    return new Date(ms).toISOString()\n  } catch {\n    return undefined\n  }\n}\n\n// Resolve `deployment.environment` per ADR-074 §2. The four-step fallback\n// is: span-attr canonical form → span-attr compat form → resource-attr\n// canonical form → resource-attr compat form → literal `'unknown'`. The\n// literal `'unknown'` is the honest sentinel; defaulting to `'production'`\n// or `'development'` would bake an incorrect assumption into every span\n// from a workload that hasn't yet wired its env signal.\nconst ENV_ATTR_CANONICAL = 'deployment.environment.name'\nconst ENV_ATTR_COMPAT = 'deployment.environment'\nconst ENV_FALLBACK = 'unknown'\n\nfunction pickEnv(\n  spanAttrs: Record<string, AttributeValue>,\n  resourceAttrs: Record<string, AttributeValue>,\n): string {\n  for (const attrs of [spanAttrs, resourceAttrs]) {\n    for (const key of [ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT]) {\n      const v = attrs[key]\n      if (typeof v === 'string' && v.length > 0) return v\n    }\n  }\n  return ENV_FALLBACK\n}\n\n// The messaging destination (topic / queue / stream) a producer or consumer\n// span names. `messaging.destination.name` is the canonical semconv key\n// (SC v1.24+); `messaging.destination` is the older form some instrumentations\n// still emit. Prefer the canonical one, fall back to the legacy, and treat an\n// empty string as absent so an anonymous destination never keys a node.\nfunction messagingDestinationOf(\n  attrs: Record<string, AttributeValue>,\n): string | undefined {\n  for (const key of ['messaging.destination.name', 'messaging.destination']) {\n    const v = attrs[key]\n    if (typeof v === 'string' && v.length > 0) return v\n  }\n  return undefined\n}\n\n// True when the span's `Upgrade` request header names `websocket`. OTel HTTP\n// instrumentation captures configured request headers as `http.request.header.<key>`\n// with the key lower-cased and the value an array of strings (one per header\n// occurrence); older SDKs may write a bare string. A WebSocket handshake carries\n// `Upgrade: websocket`, so any element equal to `websocket` (case-insensitive)\n// marks the upgrade. Returns false when the header is absent — a plain `GET`\n// route span never mints a channel.\nfunction hasWebsocketUpgradeHeader(attrs: Record<string, AttributeValue>): boolean {\n  const v = attrs['http.request.header.upgrade']\n  const matches = (s: unknown): boolean =>\n    typeof s === 'string' && s.trim().toLowerCase() === 'websocket'\n  if (Array.isArray(v)) return v.some(matches)\n  return matches(v)\n}\n\n// The channel path an upgrade span opens onto. Prefer the templated route\n// (`http.route`, e.g. `/ws/:room`) so high-cardinality connection paths collapse\n// onto one channel node; fall back to the concrete request path (`url.path`, the\n// SC v1.21+ name, or the legacy `http.target`) with any query string trimmed.\n// Returns undefined when nothing carries a path — the branch then falls through\n// rather than minting a channel keyed on an empty string.\nfunction websocketChannelPathOf(attrs: Record<string, AttributeValue>): string | undefined {\n  const route = attrs['http.route']\n  if (typeof route === 'string' && route.length > 0) return route\n  for (const key of ['url.path', 'http.target']) {\n    const v = attrs[key]\n    if (typeof v === 'string' && v.length > 0) {\n      const q = v.indexOf('?')\n      const path = q === -1 ? v : v.slice(0, q)\n      if (path.length > 0) return path\n    }\n  }\n  return undefined\n}\n\n// The WebSocket channel this span serves, or undefined. Set only on the HTTP\n// upgrade handshake span — a request carrying `Upgrade: websocket` — so a plain\n// HTTP route span is never mistaken for a channel. See docs/contracts/otel-ingest.md\n// §WebSocket channels.\nfunction websocketChannelOf(attrs: Record<string, AttributeValue>): string | undefined {\n  if (!hasWebsocketUpgradeHeader(attrs)) return undefined\n  return websocketChannelPathOf(attrs)\n}\n\nexport function parseOtlpRequest(body: OtlpTracesRequest): ParsedSpan[] {\n  const out: ParsedSpan[] = []\n  for (const rs of body.resourceSpans ?? []) {\n    const resourceAttrs = attrsToRecord(rs.resource?.attributes)\n    // OTel spec requires SDKs to set `service.name`, but customised exporters\n    // can omit it. Missing `service.name` routes to `service:unidentified`\n    // in handleSpan + emits a once-per-session-per-project warning so the\n    // diagnostic stays visible (issue #374). Silent drop is not an option.\n    const rawServiceName = resourceAttrs['service.name']\n    const resourceServiceNamePresent =\n      typeof rawServiceName === 'string' && rawServiceName.length > 0\n    const service = resourceServiceNamePresent\n      ? (rawServiceName as string)\n      : 'unidentified'\n\n    for (const ss of rs.scopeSpans ?? []) {\n      for (const span of ss.spans ?? []) {\n        const attrs = attrsToRecord(span.attributes)\n        const parsed: ParsedSpan = {\n          service,\n          resourceServiceNamePresent,\n          traceId: span.traceId ?? '',\n          spanId: span.spanId ?? '',\n          parentSpanId: span.parentSpanId || undefined,\n          name: span.name ?? '',\n          kind: span.kind,\n          startTimeUnixNano: span.startTimeUnixNano ?? '0',\n          endTimeUnixNano: span.endTimeUnixNano ?? '0',\n          startTimeIso: isoFromUnixNano(span.startTimeUnixNano),\n          durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),\n          env: pickEnv(attrs, resourceAttrs),\n          attributes: attrs,\n          dbSystem: typeof attrs['db.system'] === 'string' ? (attrs['db.system'] as string) : undefined,\n          dbName: typeof attrs['db.name'] === 'string' ? (attrs['db.name'] as string) : undefined,\n          messagingSystem:\n            typeof attrs['messaging.system'] === 'string'\n              ? (attrs['messaging.system'] as string)\n              : undefined,\n          messagingDestination: messagingDestinationOf(attrs),\n          graphqlOperationName:\n            typeof attrs['graphql.operation.name'] === 'string' &&\n            (attrs['graphql.operation.name'] as string).length > 0\n              ? (attrs['graphql.operation.name'] as string)\n              : undefined,\n          graphqlOperationType:\n            typeof attrs['graphql.operation.type'] === 'string' &&\n            (attrs['graphql.operation.type'] as string).length > 0\n              ? (attrs['graphql.operation.type'] as string)\n              : undefined,\n          rpcSystem:\n            typeof attrs['rpc.system'] === 'string' &&\n            (attrs['rpc.system'] as string).length > 0\n              ? (attrs['rpc.system'] as string)\n              : undefined,\n          rpcService:\n            typeof attrs['rpc.service'] === 'string' &&\n            (attrs['rpc.service'] as string).length > 0\n              ? (attrs['rpc.service'] as string)\n              : undefined,\n          rpcMethod:\n            typeof attrs['rpc.method'] === 'string' &&\n            (attrs['rpc.method'] as string).length > 0\n              ? (attrs['rpc.method'] as string)\n              : undefined,\n          websocketChannel: websocketChannelOf(attrs),\n          statusCode: span.status?.code,\n          errorMessage: span.status?.message,\n          exception: extractExceptionFromEvents(span.events),\n        }\n        out.push(parsed)\n      }\n    }\n  }\n  return out\n}\n\nexport interface OtelReceiver {\n  app: FastifyInstance\n  // Resolves once every span enqueued so far has been handed to opts.onSpan.\n  // Test seam — production code never awaits this.\n  flushPending: () => Promise<void>\n}\n\n// Lazy-loaded protobuf decoder for ExportTraceServiceRequest. The bundled\n// .proto tree at packages/core/proto/ is shared with the gRPC receiver\n// (ADR-020). Cached after first load so successive receiver builds reuse it.\nlet exportTraceServiceRequestType: protobuf.Type | null = null\nlet exportTraceServiceResponseType: protobuf.Type | null = null\n\nfunction loadProtoRoot(): protobuf.Root {\n  const here = path.dirname(fileURLToPath(import.meta.url))\n  const protoRoot = path.resolve(here, '..', 'proto')\n  const root = new protobuf.Root()\n  root.resolvePath = (_origin, target) => path.resolve(protoRoot, target)\n  root.loadSync(\n    'opentelemetry/proto/collector/trace/v1/trace_service.proto',\n    { keepCase: true },\n  )\n  return root\n}\n\nfunction loadProtobufDecoder(): protobuf.Type {\n  if (exportTraceServiceRequestType) return exportTraceServiceRequestType\n  const root = loadProtoRoot()\n  exportTraceServiceRequestType = root.lookupType(\n    'opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest',\n  )\n  return exportTraceServiceRequestType\n}\n\nfunction loadProtobufResponseEncoder(): protobuf.Type {\n  if (exportTraceServiceResponseType) return exportTraceServiceResponseType\n  const root = loadProtoRoot()\n  exportTraceServiceResponseType = root.lookupType(\n    'opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse',\n  )\n  return exportTraceServiceResponseType\n}\n\n// Empty-partial-success response, encoded once and cached. Per ADR-033 the\n// receiver always reports \"all accepted\" today — there's no per-span reject\n// path. Caching the bytes avoids re-running the protobuf encoder per request.\nlet cachedProtobufResponseBody: Buffer | null = null\n\nfunction encodeProtobufResponseBody(): Buffer {\n  if (cachedProtobufResponseBody) return cachedProtobufResponseBody\n  const Type = loadProtobufResponseEncoder()\n  // `partial_success` left unset = empty submessage = \"everything accepted\".\n  // verify() returns null on success; the empty payload is always valid.\n  const msg = Type.create({})\n  const encoded = Type.encode(msg).finish()\n  cachedProtobufResponseBody = Buffer.from(encoded)\n  return cachedProtobufResponseBody\n}\n\nasync function decodeProtobufBody(buf: Buffer): Promise<OtlpTracesRequest> {\n  const Type = loadProtobufDecoder()\n  // Decode keeps the proto field names verbatim (keepCase: true), matching the\n  // GrpcExportRequest shape that reshapeGrpcRequest already understands.\n  // toObject() options mirror the gRPC receiver's proto-loader config\n  // (otel-grpc.ts: longs: String, enums: Number, bytes left as Buffers) so\n  // both protobuf paths hand reshapeGrpcRequest the identical shape. The old\n  // .toJSON() here rendered bytes as base64 strings (bytesToHex returned ''\n  // → empty trace/span IDs) and enums as name strings (\"SPAN_KIND_CLIENT\"\n  // never matches the numeric mint gate) — every http/protobuf span was\n  // accepted and then silently minted nothing (#468).\n  // Dynamic import sidesteps the circular module dep with otel-grpc.ts.\n  const decoded = Type.toObject(Type.decode(buf), {\n    longs: String,\n    enums: Number,\n  }) as Record<string, unknown>\n  const { reshapeGrpcRequest } = await import('./otel-grpc.js')\n  return reshapeGrpcRequest(decoded as never)\n}\n\nexport async function buildOtelReceiver(\n  opts: BuildOtelReceiverOptions,\n): Promise<FastifyInstance & { flushPending: () => Promise<void> }> {\n  const app = Fastify({\n    logger: false,\n    bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024,\n  })\n\n  // ADR-073 §4 — bearer on `/v1/traces`. `/health` stays unauthenticated via\n  // the default suffix list (the CI smoke and supervisors lean on it for\n  // liveness probes).\n  //\n  // A rejected OTLP POST is a silent failure on the sender's side — the app\n  // gets a bare 401 and its telemetry vanishes, so the operator sees an empty\n  // OBSERVED layer with no clue why. Emit a server-side warning when that\n  // happens, rate-limited to one line per interval so a chatty misconfigured\n  // exporter (they retry hard) can't flood the log. The plain REST 401 path\n  // stays quiet — that surface leaves this hook unset.\n  const REJECT_WARN_INTERVAL_MS = 60_000\n  let lastRejectWarnAt = 0\n  const warnRejectedOtlp = (): void => {\n    const now = Date.now()\n    if (now - lastRejectWarnAt < REJECT_WARN_INTERVAL_MS) return\n    lastRejectWarnAt = now\n    console.warn(\n      '[neatd] rejecting OTLP spans on /v1/traces — missing or invalid bearer token (set NEAT_OTEL_TOKEN on the instrumented app)',\n    )\n  }\n  mountBearerAuth(app, {\n    token: opts.authToken,\n    trustProxy: opts.trustProxy,\n    onReject: warnRejectedOtlp,\n  })\n\n  // Non-blocking ingest (ADR-033). The receiver replies 200 OK as soon as the\n  // body is parsed; mutation runs through this queue, drained on the next tick.\n  // OTel SDK exporters retry on timeout, so blocking ingest produces observable\n  // backpressure on the system being observed — ambient observation requires no\n  // observable effect.\n  const queue: ParsedSpan[] = []\n  let draining = false\n  let drainPromise: Promise<void> = Promise.resolve()\n\n  const drain = async (): Promise<void> => {\n    if (draining) return\n    draining = true\n    try {\n      while (queue.length > 0) {\n        const span = queue.shift()!\n        try {\n          await opts.onSpan(span)\n        } catch (err) {\n          console.warn(`[neat] otel handler error: ${(err as Error).message}`)\n        }\n      }\n    } finally {\n      draining = false\n    }\n  }\n\n  const enqueue = (spans: ParsedSpan[]): void => {\n    if (spans.length === 0) return\n    for (const s of spans) queue.push(s)\n    // Schedule on the next tick so the 200 response is on the wire before any\n    // mutation runs. Each call gets its own promise so flushPending() can wait\n    // on the latest drain cycle.\n    drainPromise = drainPromise.then(() => drain())\n  }\n\n  // Per-project queue is reusable across the project-scoped route — the\n  // project name rides in the URL, not on the span. Drain semantics mirror\n  // the global queue so flushPending() captures both.\n  const projectQueue: Array<{ project: string; span: ParsedSpan }> = []\n  let projectDraining = false\n  let projectDrainPromise: Promise<void> = Promise.resolve()\n  const drainProject = async (): Promise<void> => {\n    if (projectDraining) return\n    projectDraining = true\n    try {\n      while (projectQueue.length > 0) {\n        const { project, span } = projectQueue.shift()!\n        try {\n          if (opts.onProjectSpan) {\n            await opts.onProjectSpan(project, span)\n          } else {\n            await opts.onSpan(span)\n          }\n        } catch (err) {\n          console.warn(`[neat] otel handler error: ${(err as Error).message}`)\n        }\n      }\n    } finally {\n      projectDraining = false\n    }\n  }\n  const enqueueProject = (project: string, spans: ParsedSpan[]): void => {\n    if (spans.length === 0) return\n    for (const s of spans) projectQueue.push({ project, span: s })\n    projectDrainPromise = projectDrainPromise.then(() => drainProject())\n  }\n\n  // One-time-per-service-name deprecation warning for spans landing on the\n  // bare `/v1/traces` endpoint. Under one daemon per project (ADR-096) that\n  // route is the project's own ingest path and needs no migration, so the\n  // warning is gated on whether this receiver actually offers project-scoped\n  // routing: only a receiver wired with `onProjectSpan` (the multi-project\n  // daemon that mounts `/projects/<name>/v1/traces`) has somewhere to migrate\n  // an exporter to. A single-project receiver leaves the gate closed and never\n  // nags. Still once-per-name so a long-running daemon doesn't flood stderr\n  // while an operator migrates.\n  const offersProjectRouting = opts.onProjectSpan !== undefined\n  const legacyEndpointWarned = new Set<string>()\n  function warnLegacyEndpoint(serviceName: string): void {\n    if (!offersProjectRouting) return\n    if (legacyEndpointWarned.has(serviceName)) return\n    legacyEndpointWarned.add(serviceName)\n    console.warn(\n      `[neatd] received span on the global endpoint; migrate OTEL_EXPORTER_OTLP_TRACES_ENDPOINT to /projects/<name>/v1/traces (service.name=\"${serviceName}\").`,\n    )\n  }\n\n  // Shared body-decode + content-negotiation. Both `/v1/traces` and\n  // `/projects/:project/v1/traces` go through this so the protobuf/JSON\n  // dispatch stays in one place.\n  async function readOtlpBody(req: import('fastify').FastifyRequest): Promise<\n    | { ok: true; body: OtlpTracesRequest; flavor: 'json' | 'protobuf' }\n    | { ok: false; code: 400 | 415; error: string }\n  > {\n    const ct = (req.headers['content-type'] ?? '').toString().split(';')[0]!.trim().toLowerCase()\n    if (ct === 'application/x-protobuf') {\n      try {\n        const body = await decodeProtobufBody(req.body as Buffer)\n        return { ok: true, body, flavor: 'protobuf' }\n      } catch (err) {\n        return { ok: false, code: 400, error: `protobuf decode failed: ${(err as Error).message}` }\n      }\n    }\n    if (!ct || ct === 'application/json') {\n      return { ok: true, body: (req.body ?? {}) as OtlpTracesRequest, flavor: 'json' }\n    }\n    return { ok: false, code: 415, error: `unsupported content-type: ${ct}` }\n  }\n\n  function sendOtlpSuccess(reply: import('fastify').FastifyReply, flavor: 'json' | 'protobuf'): unknown {\n    if (flavor === 'protobuf') {\n      const buf = encodeProtobufResponseBody()\n      return reply\n        .code(200)\n        .header('content-type', 'application/x-protobuf')\n        .send(buf)\n    }\n    return reply\n      .code(200)\n      .header('content-type', 'application/json')\n      .send({ partialSuccess: {} })\n  }\n\n  // Buffer application/x-protobuf bodies as raw bytes; the route handler\n  // decodes them via the bundled .proto tree (ADR-020).\n  app.addContentTypeParser(\n    'application/x-protobuf',\n    { parseAs: 'buffer', bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024 },\n    (_req, body, done) => {\n      done(null, body)\n    },\n  )\n\n  app.get('/health', async () => ({ ok: true }))\n\n  app.post('/v1/traces', async (req, reply) => {\n    // Legacy global endpoint. Spans land here when an OTel exporter hasn't\n    // migrated to the project-scoped URL yet (issue #367); the daemon still\n    // routes by `service.name` against the registry. Per-service-name\n    // deprecation warning fires once so an operator notices without their\n    // stderr flooding.\n    const result = await readOtlpBody(req)\n    if (!result.ok) {\n      return reply.code(result.code).send({ error: result.error })\n    }\n    const spans = parseOtlpRequest(result.body)\n    for (const s of spans) warnLegacyEndpoint(s.service)\n    if (opts.onErrorSpanSync) {\n      try {\n        for (const span of spans) {\n          if (span.statusCode === 2) await opts.onErrorSpanSync(span)\n        }\n      } catch (err) {\n        return reply.code(500).send({\n          error: `error-event write failed: ${(err as Error).message}`,\n        })\n      }\n    }\n    enqueue(spans)\n    return sendOtlpSuccess(reply, result.flavor)\n  })\n\n  // Project-scoped route (issue #367). The URL `:project` carries the routing\n  // key, sidestepping the `service.name`-against-registry heuristic the legacy\n  // path uses. Spans get dispatched into the named project's ingest path\n  // directly; `OTEL_SERVICE_NAME` regains its proper semantic role of naming\n  // the ServiceNode inside the one project the URL already picked.\n  app.post<{ Params: { project: string } }>('/projects/:project/v1/traces', async (req, reply) => {\n    const project = req.params.project\n    const result = await readOtlpBody(req)\n    if (!result.ok) {\n      return reply.code(result.code).send({ error: result.error })\n    }\n    const spans = parseOtlpRequest(result.body)\n    if (opts.onProjectErrorSpanSync) {\n      try {\n        for (const span of spans) {\n          if (span.statusCode === 2) await opts.onProjectErrorSpanSync(project, span)\n        }\n      } catch (err) {\n        return reply.code(500).send({\n          error: `error-event write failed: ${(err as Error).message}`,\n        })\n      }\n    } else if (opts.onErrorSpanSync) {\n      try {\n        for (const span of spans) {\n          if (span.statusCode === 2) await opts.onErrorSpanSync(span)\n        }\n      } catch (err) {\n        return reply.code(500).send({\n          error: `error-event write failed: ${(err as Error).message}`,\n        })\n      }\n    }\n    enqueueProject(project, spans)\n    return sendOtlpSuccess(reply, result.flavor)\n  })\n\n  // Attach flushPending so tests can wait for the queue without exporting a\n  // separate handle. The cast goes through `unknown` because Fastify's typing\n  // is parameterised over the raw server type and the simple intersection\n  // confuses TS's structural narrowing.\n  const decorated = app as unknown as FastifyInstance & { flushPending: () => Promise<void> }\n  decorated.flushPending = async () => {\n    // Settle both drain chains, then loop until both queues are fully empty\n    // (a span enqueued mid-flush would otherwise be missed).\n    while (queue.length > 0 || draining || projectQueue.length > 0 || projectDraining) {\n      await Promise.all([drainPromise, projectDrainPromise])\n    }\n  }\n  return decorated\n}\n\n// How far the OTLP receiver steps before giving up, and the stride between\n// candidate ports. Matches the orchestrator's triple allocator (8 attempts,\n// stride 1) so the daemon's own bind and the pre-spawn allocation reach the\n// same free port under the same contention.\nconst OTLP_STEP_ATTEMPTS = 8\nconst OTLP_STEP_STRIDE = 1\n\n// Bind an OTLP receiver, stepping to the next free port when the requested one\n// is held (daemon.md §Binding — a held OTLP port steps, it does not crash the\n// daemon; project-daemon §3). The REST port is the daemon's identity and stays\n// fatal on collision, but every OTLP consumer resolves the port dynamically\n// from `daemon.json` `ports.otlp`, so stepping the receiver and recording the\n// port it actually bound keeps the OBSERVED layer alive instead of darking the\n// whole daemon on a foreign collector holding `:4318`. Returns the bound\n// address (host:port); callers read the real port back from it. A requested\n// port of `0` means \"let the kernel pick a free one\" — no collision is\n// possible, so no stepping happens. Non-`EADDRINUSE` failures (permission\n// denied) and an exhausted step window propagate to the caller unchanged.\nexport async function listenSteppingOtlp(\n  app: FastifyInstance,\n  requestedPort: number,\n  host: string,\n): Promise<string> {\n  let port = requestedPort\n  for (let attempt = 0; ; attempt++) {\n    try {\n      return await app.listen({ port, host })\n    } catch (err) {\n      const code = (err as NodeJS.ErrnoException).code\n      const canStep =\n        requestedPort !== 0 && code === 'EADDRINUSE' && attempt < OTLP_STEP_ATTEMPTS - 1\n      if (!canStep) throw err\n      console.warn(\n        `otel: OTLP port ${port} is in use, stepping to ${port + OTLP_STEP_STRIDE}`,\n      )\n      port += OTLP_STEP_STRIDE\n    }\n  }\n}\n\nexport function logSpanHandler(span: ParsedSpan): void {\n  const parent = span.parentSpanId ? span.parentSpanId.slice(0, 8) : '<root>'\n  const status = span.statusCode === 2 ? 'ERROR' : 'OK'\n  const db = span.dbSystem ? ` db=${span.dbSystem}/${span.dbName ?? '?'}` : ''\n  console.log(\n    `otel: ${span.service} ${span.name} parent=${parent} status=${status}${db}`,\n  )\n}\n","import path from 'node:path'\nimport { DEFAULT_PROJECT, getGraph } from './graph.js'\nimport { buildApi } from './api.js'\nimport { extractFromDirectory } from './extract.js'\nimport { loadGraphFromDisk, startPersistLoop } from './persist.js'\nimport { buildOtelReceiver } from './otel.js'\nimport { startOtelGrpcReceiver } from './otel-grpc.js'\nimport { makeSpanHandler, startStalenessLoop } from './ingest.js'\nimport { buildSearchIndex } from './search.js'\nimport { Projects, parseExtraProjects, pathsForProject } from './projects.js'\nimport { assertBindAuthority, readAuthEnv } from './auth.js'\n\nasync function bootProject(\n  registry: Projects,\n  name: string,\n  scanPath: string | undefined,\n  baseDir: string,\n): Promise<void> {\n  const paths = pathsForProject(name, baseDir)\n  const graph = getGraph(name)\n  await loadGraphFromDisk(graph, paths.snapshotPath)\n\n  if (scanPath) {\n    const r = await extractFromDirectory(graph, scanPath)\n    console.log(\n      `[${name}] extract: ${r.nodesAdded} new nodes, ${r.edgesAdded} new edges (graph total ${graph.order}/${graph.size})`,\n    )\n  } else {\n    console.log(`[${name}] loaded ${graph.order} nodes / ${graph.size} edges from snapshot`)\n  }\n\n  startPersistLoop(graph, paths.snapshotPath)\n  startStalenessLoop(graph, { staleEventsPath: paths.staleEventsPath })\n\n  const searchIndex = await buildSearchIndex(graph, {\n    cachePath: paths.embeddingsCachePath,\n  }).catch((err) => {\n    console.warn(\n      `[${name}] semantic_search: index build failed (${(err as Error).message}); falling back to inline substring`,\n    )\n    return undefined\n  })\n  if (searchIndex) {\n    console.log(`[${name}] semantic_search: ${searchIndex.provider} provider`)\n  }\n\n  registry.set(name, {\n    scanPath,\n    paths,\n    searchIndex,\n  })\n}\n\nasync function main(): Promise<void> {\n  const baseDirEnv = process.env.NEAT_OUT_DIR\n  const legacyOutPath = process.env.NEAT_OUT_PATH\n  const baseDir = baseDirEnv\n    ? path.resolve(baseDirEnv)\n    : legacyOutPath\n      ? path.resolve(path.dirname(legacyOutPath))\n      : path.resolve('./neat-out')\n\n  const defaultScanPath = path.resolve(process.env.NEAT_SCAN_PATH ?? './demo')\n  const registry = new Projects()\n\n  // Default project always exists. NEAT_SCAN_PATH still wires it to a scan\n  // root so existing single-project users see no behaviour change.\n  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir)\n\n  // Extra projects come from NEAT_PROJECTS=a,b,c. Their snapshots load from\n  // <baseDir>/<name>.json; they have no scan path by default (callers can\n  // POST /projects/<name>/graph/scan after wiring NEAT_PROJECT_SCAN_PATH_<NAME>).\n  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {\n    const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`\n    const projectScan = process.env[envKey]\n    await bootProject(registry, name, projectScan ? path.resolve(projectScan) : undefined, baseDir)\n  }\n\n  const host = process.env.HOST ?? '0.0.0.0'\n  const port = Number(process.env.PORT ?? 8080)\n  const otelPort = Number(process.env.OTEL_PORT ?? 4318)\n\n  // ADR-073 §3 — refuse to bind a public address without a token.\n  const auth = readAuthEnv()\n  assertBindAuthority(host, auth.authToken)\n\n  const app = await buildApi({\n    projects: registry,\n    authToken: auth.authToken,\n    trustProxy: auth.trustProxy,\n    publicRead: auth.publicRead,\n  })\n  await app.listen({ port, host })\n  console.log(`neat-core listening on http://${host}:${port}`)\n  console.log(`  base dir:      ${baseDir}`)\n  console.log(`  projects:      ${registry.list().join(', ')}`)\n\n  // OTel ingest stays single-project for now: spans always land in the\n  // default project's graph + errors log. Multi-project routing for spans\n  // is a future concern (would need a header / resource attr).\n  const defaultCtx = registry.get(DEFAULT_PROJECT)\n  if (defaultCtx) {\n    const onSpan = makeSpanHandler({\n      graph: defaultCtx.graph,\n      errorsPath: defaultCtx.paths.errorsPath,\n    })\n    const otelApp = await buildOtelReceiver({\n      onSpan,\n      authToken: auth.otelToken,\n      trustProxy: auth.trustProxy,\n    })\n    await otelApp.listen({ port: otelPort, host })\n    console.log(`neat-core OTLP receiver on http://${host}:${otelPort}/v1/traces`)\n\n    if (process.env.NEAT_OTLP_GRPC === 'true') {\n      const grpcPort = Number(process.env.NEAT_OTLP_GRPC_PORT ?? 4317)\n      const grpcReceiver = await startOtelGrpcReceiver({\n        onSpan,\n        host,\n        port: grpcPort,\n        authToken: auth.otelToken,\n        trustProxy: auth.trustProxy,\n      })\n      console.log(`neat-core OTLP/gRPC receiver on ${grpcReceiver.address}`)\n    }\n  }\n}\n\nmain().catch((err) => {\n  console.error(err)\n  process.exit(1)\n})\n","import GraphDefault from 'graphology'\nimport type { MultiDirectedGraph as MDGType } from 'graphology'\nimport type { GraphEdge, GraphNode } from '@neat.is/types'\n\n// graphology ships as a CJS bundle that does `module.exports = Graph` with\n// the other constructors attached as properties (`Graph.MultiDirectedGraph =\n// ...`). cjs-module-lexer can't see through that attachment, so a named\n// import like `import { MultiDirectedGraph } from 'graphology'` fails under\n// strict Node ESM (Node 22+ via tsx in particular). Pull the constructor off\n// the default export instead — same shape under tsx and tsup-bundled output.\ntype MultiDirectedGraphCtor = typeof MDGType\nconst MultiDirectedGraph: MultiDirectedGraphCtor = (\n  GraphDefault as unknown as { MultiDirectedGraph: MultiDirectedGraphCtor }\n).MultiDirectedGraph\n\n// Multi because two nodes can have edges of different types simultaneously\n// (e.g. CALLS and DEPENDS_ON between the same pair of services).\nexport type NeatGraph = MDGType<GraphNode, GraphEdge>\n\nexport const DEFAULT_PROJECT = 'default'\n\n// One graph per project. The map is the source of truth; getGraph() with no\n// arg or with 'default' hits the legacy single-project path so existing\n// callers keep working byte-for-byte (ADR-026).\nconst graphs = new Map<string, NeatGraph>()\n\nfunction makeGraph(): NeatGraph {\n  return new MultiDirectedGraph<GraphNode, GraphEdge>({ allowSelfLoops: false })\n}\n\nexport function getGraph(project: string = DEFAULT_PROJECT): NeatGraph {\n  let g = graphs.get(project)\n  if (!g) {\n    g = makeGraph()\n    graphs.set(project, g)\n  }\n  return g\n}\n\nexport function hasProject(project: string): boolean {\n  return graphs.has(project)\n}\n\nexport function listProjects(): string[] {\n  return [...graphs.keys()].sort()\n}\n\n// Reset a single project, or all of them when the arg is omitted. Tests use\n// the no-arg form between cases; runtime never calls it.\nexport function resetGraph(project?: string): void {\n  if (project === undefined) {\n    graphs.clear()\n    return\n  }\n  graphs.delete(project)\n}\n","import Fastify, {\n  type FastifyInstance,\n  type FastifyReply,\n  type FastifyRequest,\n} from 'fastify'\nimport cors from '@fastify/cors'\nimport type {\n  ErrorEvent,\n  GraphEdge,\n  GraphNode,\n  Policy,\n  PolicyViolation,\n  RegistryEntry,\n} from '@neat.is/types'\nimport { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from '@neat.is/types'\nimport type { DivergenceType } from '@neat.is/types'\nimport {\n  applyExtension,\n  describeProjectInstrumentation,\n  dryRunExtension,\n  listUninstrumented,\n  lookupInstrumentation,\n  rollbackExtension,\n} from './extend/index.js'\nimport { computeDivergences } from './divergences.js'\nimport {\n  evaluateAllPolicies,\n  loadPolicyFile,\n  PolicyViolationsLog,\n  selectApplicablePolicies,\n} from './policy.js'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT } from './graph.js'\nimport { extractFromDirectory } from './extract.js'\nimport { readErrorEvents, readStaleEvents } from './ingest.js'\nimport {\n  getBlastRadius,\n  getObservedDependencies,\n  getRootCause,\n  getTransitiveDependencies,\n  TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH,\n  TRANSITIVE_DEPENDENCIES_MAX_DEPTH,\n} from './traverse.js'\nimport { computeGraphDiff, loadSnapshotForDiff } from './diff.js'\nimport { mergeSnapshot } from './ingest.js'\nimport { SCHEMA_VERSION, type PersistedGraph } from './persist.js'\nimport type { SearchIndex } from './search.js'\nimport type { Projects, ProjectContext } from './projects.js'\nimport { Projects as ProjectsClass, pathsForProject } from './projects.js'\nimport { getProject as getRegistryProject, listProjects as listRegistryProjects } from './registry.js'\nimport { handleSse } from './streaming.js'\nimport { mountBearerAuth, readAuthEnv } from './auth.js'\n\nexport interface BuildApiOptions {\n  // Multi-project shape. Optional — when absent we synthesise a single-\n  // project registry from the legacy fields below so existing callers\n  // (mainly tests) keep working unchanged.\n  projects?: Projects\n  startedAt?: number\n\n  // Legacy single-project shape. Mapped to project=`default` if `projects`\n  // isn't provided.\n  graph?: NeatGraph\n  scanPath?: string\n  errorsPath?: string\n  staleEventsPath?: string\n  searchIndex?: SearchIndex\n\n  // ADR-073 §3 — bearer token required on `/api/*` and `/events`. Undefined\n  // leaves the middleware off (loopback-only callers; the bind-authority\n  // gate in startDaemon refuses to bind publicly without one).\n  authToken?: string\n  // ADR-073 §3 — when the operator runs behind a reverse proxy that already\n  // authenticates the request, the daemon-side check is bypassed.\n  trustProxy?: boolean\n  // ADR-073 §3 amendment — public-read mode. When `true`, GET / HEAD / OPTIONS\n  // bypass the bearer check; writes still require it. OTLP ingest is gated\n  // independently and is unaffected by this flag.\n  publicRead?: boolean\n  // Issue #340 — per-project bootstrap status. When provided, project-scoped\n  // routes for projects still extracting return 503 with `{ready: false}`\n  // instead of 404.\n  bootstrap?: {\n    status: (name: string) => 'bootstrapping' | 'active' | 'broken' | undefined\n    list: () => Array<{ name: string; status: 'bootstrapping' | 'active' | 'broken'; elapsedMs: number }>\n  }\n  // ADR-096 §4/§5 — the per-project daemon's identity. When set, this daemon\n  // serves exactly this one project: `GET /projects` reports only it (the\n  // dashboard pins to \"the project this daemon serves,\" not a machine-wide\n  // list), and the daemon-wide `/health` carries it at the top level for the\n  // spawn-reuse identity check. Absent → the legacy multi-project daemon, whose\n  // `/projects` is the machine-wide registry passthrough.\n  singleProject?: { name: string; path: string }\n}\n\ninterface SerializedGraph {\n  nodes: GraphNode[]\n  edges: GraphEdge[]\n}\n\nfunction serializeGraph(graph: NeatGraph): SerializedGraph {\n  const nodes: GraphNode[] = []\n  graph.forEachNode((_id, attrs) => {\n    nodes.push(attrs)\n  })\n  const edges: GraphEdge[] = []\n  graph.forEachEdge((_id, attrs) => {\n    edges.push(attrs)\n  })\n  return { nodes, edges }\n}\n\nfunction projectFromReq(req: FastifyRequest, singleProject?: string): string {\n  // `:project` is optional in the URL — the request hits either\n  // /projects/:project/X or the unprefixed /X.\n  const params = req.params as { project?: string }\n  const named = params.project\n  // ADR-096 §4 — \"the daemon is the project.\" On a per-project daemon a bare\n  // /X needs no project name to disambiguate: it means this daemon's one\n  // project. The legacy `default` alias maps to it too, so an agent wired with\n  // only NEAT_CORE_URL (no project arg, no NEAT_DEFAULT_PROJECT) reaches the\n  // real project without naming it. An explicit real name still routes as\n  // given. A non-single-project daemon keeps coercing a missing param to the\n  // `default` project.\n  if (singleProject) {\n    return named === undefined || named === DEFAULT_PROJECT ? singleProject : named\n  }\n  return named ?? DEFAULT_PROJECT\n}\n\nfunction resolveProject(\n  registry: Projects,\n  req: FastifyRequest,\n  reply: FastifyReply,\n  bootstrap?: BuildApiOptions['bootstrap'],\n  singleProject?: string,\n): ProjectContext | null {\n  const name = projectFromReq(req, singleProject)\n  const ctx = registry.get(name)\n  if (!ctx) {\n    // Issue #340 — registered but still bootstrapping: surface 503 so the\n    // probe consumer knows the project is real and incoming, not missing.\n    const phase = bootstrap?.status(name)\n    if (phase === 'bootstrapping') {\n      void reply.code(503).send({ ready: false, project: name, status: 'bootstrapping' })\n      return null\n    }\n    if (phase === 'broken') {\n      void reply.code(503).send({ ready: false, project: name, status: 'broken' })\n      return null\n    }\n    void reply.code(404).send({ error: 'project not found', project: name })\n    return null\n  }\n  return ctx\n}\n\nfunction buildLegacyRegistry(opts: BuildApiOptions): Projects {\n  if (opts.projects) return opts.projects\n  if (!opts.graph) {\n    throw new Error('buildApi: either `projects` or `graph` must be provided')\n  }\n  const registry = new ProjectsClass()\n  // pathsForProject only matters here for the snapshot/embeddings paths\n  // routes never read; the ingest paths come from explicit options below.\n  const paths = pathsForProject(DEFAULT_PROJECT, '')\n  registry.set(DEFAULT_PROJECT, {\n    graph: opts.graph,\n    scanPath: opts.scanPath,\n    paths: {\n      snapshotPath: paths.snapshotPath,\n      errorsPath: opts.errorsPath ?? paths.errorsPath,\n      staleEventsPath: opts.staleEventsPath ?? paths.staleEventsPath,\n      embeddingsCachePath: paths.embeddingsCachePath,\n      policyViolationsPath: paths.policyViolationsPath,\n    },\n    searchIndex: opts.searchIndex,\n  })\n  return registry\n}\n\ninterface RouteContext {\n  registry: Projects\n  startedAt: number\n  // Where the routes are getting mounted. `'root'` is the legacy unprefixed\n  // mount that historically resolved every request to the `default` project;\n  // `'project'` is the `/projects/:project` plugin scope where the project\n  // segment is always present. The /health handler branches on this so the\n  // root mount can answer daemon-wide while the scoped mount stays\n  // per-project (issue #343).\n  scope: 'root' | 'project'\n  // Legacy callers passed `errorsPath`/`staleEventsPath` explicitly and\n  // expected absent values to disable the read. Track that intent so the\n  // /incidents handlers don't accidentally read a phantom file.\n  errorsPathFor: (ctx: ProjectContext) => string | undefined\n  staleEventsPathFor: (ctx: ProjectContext) => string | undefined\n  // policy.json lives at the project root (per ADR-042 §File location), not\n  // under neat-out/. Routes that read it map a project context to the path.\n  policyFilePathFor: (ctx: ProjectContext) => string | undefined\n  // Issue #340 — flips per-project routes from 404 to 503 while a slot is\n  // still extracting.\n  bootstrap?: BuildApiOptions['bootstrap']\n  // ADR-096 §4 — the per-project daemon's one project. When set, unprefixed\n  // (and `default`-named) requests resolve to it instead of the `default`\n  // project. Absent for the legacy multi-project daemon.\n  singleProject?: string\n}\n\n// Registers every project-scoped route on `scope`. Called twice from\n// buildApi: once on the root app (so /graph etc. land at default), once\n// inside a `register(_, { prefix: '/projects/:project' })` plugin so the\n// same handlers run when the URL names a project explicitly.\nfunction registerRoutes(scope: FastifyInstance, ctx: RouteContext): void {\n  const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx\n\n  // SSE event stream (ADR-051 #1). Dual-mounted: hits /events for default\n  // project and /projects/:project/events when scoped. The handler keeps\n  // the connection open and writes one frame per bus envelope whose\n  // `project` matches.\n  scope.get<{ Params: { project?: string } }>('/events', (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    handleSse(req, reply, { project: proj.name })\n  })\n\n  // Per-project /health stays scoped. The unscoped `/health` at the root\n  // mount is handled by the daemon-wide handler below (issue #343) —\n  // daemon-wide readiness mustn't pivot on whether the `default` project\n  // exists. The scoped variant carries the bootstrap-aware shape from\n  // issue #340.\n  if (ctx.scope === 'project') {\n    scope.get<{ Params: { project?: string } }>('/health', async (req, reply) => {\n      const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n      if (!proj) return\n      const uptimeMs = Date.now() - startedAt\n      return {\n        ok: true,\n        project: proj.name,\n        uptimeMs,\n        // Legacy fields kept additively. The web shell's StatusBar reads\n        // nodeCount / edgeCount; ADR-061's HealthResponseSchema validates\n        // the canonical triple and lets the extras pass through.\n        uptime: Math.floor(uptimeMs / 1000),\n        nodeCount: proj.graph.order,\n        edgeCount: proj.graph.size,\n        lastUpdated: new Date().toISOString(),\n      }\n    })\n  }\n\n  scope.get<{ Params: { project?: string } }>('/graph', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    return serializeGraph(proj.graph)\n  })\n\n  scope.get<{ Params: { project?: string; id: string } }>(\n    '/graph/node/:id',\n    async (req, reply) => {\n      const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n      if (!proj) return\n      const { id } = req.params\n      if (!proj.graph.hasNode(id)) {\n        return reply.code(404).send({ error: 'node not found', id })\n      }\n      return { node: proj.graph.getNodeAttributes(id) as GraphNode }\n    },\n  )\n\n  scope.get<{ Params: { project?: string; id: string } }>(\n    '/graph/edges/:id',\n    async (req, reply) => {\n      const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n      if (!proj) return\n      const { id } = req.params\n      if (!proj.graph.hasNode(id)) {\n        return reply.code(404).send({ error: 'node not found', id })\n      }\n      const inbound = proj.graph\n        .inboundEdges(id)\n        .map((e) => proj.graph.getEdgeAttributes(e) as GraphEdge)\n      const outbound = proj.graph\n        .outboundEdges(id)\n        .map((e) => proj.graph.getEdgeAttributes(e) as GraphEdge)\n      return { inbound, outbound }\n    },\n  )\n\n  // Transitive dependencies (issue #144). BFS outbound to depth N, returning\n  // a flat list with distance + edgeType + provenance per dependency.\n  // Default depth 3, max 10. The MCP get_dependencies tool calls this.\n  scope.get<{\n    Params: { project?: string; nodeId: string }\n    Querystring: { depth?: string }\n  }>('/graph/dependencies/:nodeId', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const { nodeId } = req.params\n    if (!proj.graph.hasNode(nodeId)) {\n      return reply.code(404).send({ error: 'node not found', id: nodeId })\n    }\n    const depth = req.query.depth ? Number(req.query.depth) : TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH\n    if (!Number.isFinite(depth) || depth < 1 || depth > TRANSITIVE_DEPENDENCIES_MAX_DEPTH) {\n      return reply.code(400).send({\n        error: `depth must be an integer in [1, ${TRANSITIVE_DEPENDENCIES_MAX_DEPTH}]`,\n      })\n    }\n    return getTransitiveDependencies(proj.graph, nodeId, depth)\n  })\n\n  // Observed-only dependencies (issue #578). The runtime CALLS a node makes,\n  // file-grained — and, for a ServiceNode, the OBSERVED edges of the files it\n  // owns, since the call-site processor lands them on files, not the service\n  // root. REST parity for the MCP get_observed_dependencies tool (issue #593);\n  // the CLI observed-dependencies verb reads it too.\n  scope.get<{ Params: { project?: string; nodeId: string } }>(\n    '/graph/observed-dependencies/:nodeId',\n    async (req, reply) => {\n      const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n      if (!proj) return\n      const { nodeId } = req.params\n      if (!proj.graph.hasNode(nodeId)) {\n        return reply.code(404).send({ error: 'node not found', id: nodeId })\n      }\n      return getObservedDependencies(proj.graph, nodeId)\n    },\n  )\n\n  // Divergence query — the thesis surface (ADR-060). Read-only, derived,\n  // dual-mounted via registerRoutes. Query params filter the result set;\n  // body shape is DivergenceResult.\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { type?: string; minConfidence?: string; node?: string }\n  }>('/graph/divergences', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    let typeFilter: Set<DivergenceType> | undefined\n    if (req.query.type) {\n      const candidates = req.query.type\n        .split(',')\n        .map((s) => s.trim())\n        .filter((s) => s.length > 0)\n      const parsed: DivergenceType[] = []\n      for (const c of candidates) {\n        const r = DivergenceTypeSchema.safeParse(c)\n        if (!r.success) {\n          return reply.code(400).send({\n            error: `unknown divergence type \"${c}\"`,\n            allowed: DivergenceTypeSchema.options,\n          })\n        }\n        parsed.push(r.data)\n      }\n      typeFilter = new Set(parsed)\n    }\n    let minConfidence: number | undefined\n    if (req.query.minConfidence !== undefined) {\n      const n = Number(req.query.minConfidence)\n      if (!Number.isFinite(n) || n < 0 || n > 1) {\n        return reply.code(400).send({\n          error: 'minConfidence must be a number in [0, 1]',\n        })\n      }\n      minConfidence = n\n    }\n    return computeDivergences(proj.graph, {\n      ...(typeFilter ? { type: typeFilter } : {}),\n      ...(minConfidence !== undefined ? { minConfidence } : {}),\n      ...(req.query.node ? { node: req.query.node } : {}),\n    })\n  })\n\n  // ADR-061 envelope rule: list endpoints return { count, total, events }.\n  // `total` is the size of the underlying collection; `count` is the size of\n  // the slice we're handing back. The web shell counts on both.\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { limit?: string }\n  }>('/incidents', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const epath = errorsPathFor(proj)\n    if (!epath) return { count: 0, total: 0, events: [] }\n    const events = await readErrorEvents(epath)\n    const total = events.length\n    const limit = req.query.limit ? Number(req.query.limit) : 50\n    const safeLimit =\n      Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 50\n    const sliced = events.slice(0, safeLimit)\n    return { count: sliced.length, total, events: sliced }\n  })\n\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { limit?: string; edgeType?: string }\n  }>('/stale-events', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const spath = staleEventsPathFor(proj)\n    if (!spath) return { count: 0, total: 0, events: [] }\n    const events = await readStaleEvents(spath)\n    const filtered = req.query.edgeType\n      ? events.filter((e) => e.edgeType === req.query.edgeType)\n      : events\n    const ordered = [...filtered].reverse()\n    const total = ordered.length\n    const limit = req.query.limit ? Number(req.query.limit) : 50\n    const sliced = ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50)\n    return { count: sliced.length, total, events: sliced }\n  })\n\n  // One handler, two paths: `/incidents/:nodeId` is the original REST name and\n  // `/graph/incident-history/:nodeId` mirrors the MCP get_incident_history tool\n  // so the two surfaces answer under matching names (issue #593).\n  const incidentHistoryHandler = async (\n    req: FastifyRequest<{ Params: { project?: string; nodeId: string } }>,\n    reply: FastifyReply,\n  ): Promise<{ count: number; total: number; events: ErrorEvent[] } | undefined> => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const { nodeId } = req.params\n    if (!proj.graph.hasNode(nodeId)) {\n      reply.code(404).send({ error: 'node not found', id: nodeId })\n      return\n    }\n    const epath = errorsPathFor(proj)\n    if (!epath) return { count: 0, total: 0, events: [] }\n    const events = await readErrorEvents(epath)\n    const filtered = events.filter(\n      (e) =>\n        e.affectedNode === nodeId || e.service === nodeId.replace(/^service:/, ''),\n    )\n    return { count: filtered.length, total: filtered.length, events: filtered }\n  }\n  scope.get<{ Params: { project?: string; nodeId: string } }>(\n    '/incidents/:nodeId',\n    incidentHistoryHandler,\n  )\n  scope.get<{ Params: { project?: string; nodeId: string } }>(\n    '/graph/incident-history/:nodeId',\n    incidentHistoryHandler,\n  )\n\n  scope.get<{\n    Params: { project?: string; nodeId: string }\n    Querystring: { errorId?: string }\n  }>('/graph/root-cause/:nodeId', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const { nodeId } = req.params\n    if (!proj.graph.hasNode(nodeId)) {\n      return reply.code(404).send({ error: 'node not found', id: nodeId })\n    }\n    // Load the incident store so root-cause can localize an in-process failure\n    // that never crossed a graph edge (#584). When errorId is supplied the\n    // named incident colours the reason; the full set is the fallback the graph\n    // walk consults when no edge carried an incompatibility.\n    const epath = errorsPathFor(proj)\n    const incidents = epath ? await readErrorEvents(epath) : []\n    let errorEvent: ErrorEvent | undefined\n    if (req.query.errorId) {\n      errorEvent = incidents.find((e) => e.id === req.query.errorId)\n      if (!errorEvent) {\n        return reply\n          .code(404)\n          .send({ error: 'error event not found', id: req.query.errorId })\n      }\n    }\n    const result = getRootCause(proj.graph, nodeId, errorEvent, incidents)\n    if (!result) return reply.code(404).send({ error: 'no root cause found', id: nodeId })\n    return result\n  })\n\n  scope.get<{\n    Params: { project?: string; nodeId: string }\n    Querystring: { depth?: string }\n  }>('/graph/blast-radius/:nodeId', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const { nodeId } = req.params\n    if (!proj.graph.hasNode(nodeId)) {\n      return reply.code(404).send({ error: 'node not found', id: nodeId })\n    }\n    const depth = req.query.depth ? Number(req.query.depth) : undefined\n    if (depth !== undefined && (!Number.isFinite(depth) || depth < 0)) {\n      return reply.code(400).send({ error: 'depth must be a non-negative number' })\n    }\n    return getBlastRadius(proj.graph, nodeId, depth)\n  })\n\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { q?: string; limit?: string }\n  }>('/search', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const raw = (req.query.q ?? '').trim()\n    if (!raw) return reply.code(400).send({ error: 'query parameter `q` is required' })\n    const limit = req.query.limit ? Number(req.query.limit) : undefined\n    const safeLimit =\n      limit !== undefined && Number.isFinite(limit) && limit > 0 ? limit : undefined\n    if (proj.searchIndex) {\n      const result = await proj.searchIndex.search(raw, safeLimit)\n      return {\n        query: result.query,\n        provider: result.provider,\n        matches: result.matches.map((m) => ({ ...m.node, score: m.score })),\n      }\n    }\n    const q = raw.toLowerCase()\n    const matches: (GraphNode & { score: number })[] = []\n    proj.graph.forEachNode((id, attrs) => {\n      const name = (attrs as { name?: string }).name ?? ''\n      if (id.toLowerCase().includes(q) || name.toLowerCase().includes(q)) {\n        matches.push({ ...(attrs as GraphNode), score: 1 })\n      }\n    })\n    return {\n      query: q,\n      provider: 'substring' as const,\n      matches: matches.slice(0, safeLimit),\n    }\n  })\n\n  scope.get<{ Params: { project?: string }; Querystring: { against?: string } }>(\n    '/graph/diff',\n    async (req, reply) => {\n      const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n      if (!proj) return\n      const against = req.query.against\n      if (!against) {\n        return reply.code(400).send({ error: 'query parameter `against` is required' })\n      }\n      try {\n        const snapshot = await loadSnapshotForDiff(against)\n        return computeGraphDiff(proj.graph, snapshot)\n      } catch (err) {\n        return reply\n          .code(400)\n          .send({ error: 'failed to load snapshot', against, detail: (err as Error).message })\n      }\n    },\n  )\n\n  // Snapshot push (ADR-074 §1). `neat sync` POSTs a snapshot here; we merge\n  // it into the live graph through `mergeSnapshot`, which preserves any\n  // accumulated OBSERVED edges per the Rule 2 coexistence contract. Body\n  // shape is the same JSON `persist.ts` writes to disk. Dual-mounted at\n  // `/snapshot` and `/projects/:project/snapshot` via registerRoutes.\n  scope.post<{\n    Params: { project?: string }\n    Body: { snapshot?: PersistedGraph }\n  }>('/snapshot', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const body = req.body\n    if (!body || typeof body !== 'object' || !body.snapshot) {\n      return reply\n        .code(400)\n        .send({ error: 'request body must be { snapshot: <persisted-graph> }' })\n    }\n    const snap = body.snapshot\n    if (typeof snap.schemaVersion !== 'number' || snap.schemaVersion !== SCHEMA_VERSION) {\n      return reply.code(400).send({\n        error: `unsupported snapshot schemaVersion ${snap.schemaVersion} (expected ${SCHEMA_VERSION})`,\n      })\n    }\n    try {\n      const result = mergeSnapshot(proj.graph, snap)\n      return {\n        project: proj.name,\n        nodesAdded: result.nodesAdded,\n        edgesAdded: result.edgesAdded,\n        nodeCount: proj.graph.order,\n        edgeCount: proj.graph.size,\n      }\n    } catch (err) {\n      return reply.code(400).send({\n        error: 'snapshot merge failed',\n        details: (err as Error).message,\n      })\n    }\n  })\n\n  scope.post<{ Params: { project?: string } }>('/graph/scan', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply\n        .code(409)\n        .send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    const result = await extractFromDirectory(proj.graph, proj.scanPath)\n    return {\n      project: proj.name,\n      scanned: proj.scanPath,\n      nodesAdded: result.nodesAdded,\n      edgesAdded: result.edgesAdded,\n      nodeCount: proj.graph.order,\n      edgeCount: proj.graph.size,\n    }\n  })\n\n  // Policy surface (ADR-045 / contract #18). /policies returns the parsed\n  // policy.json; /policies/violations is the persistent log; /policies/check\n  // is dry-run evaluation. All dual-mounted via registerRoutes.\n  scope.get<{ Params: { project?: string } }>('/policies', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const policyPath = ctx.policyFilePathFor(proj)\n    if (!policyPath) {\n      // No policy file configured for this project — return the empty file\n      // shape so consumers don't have to special-case \"no policies yet.\"\n      return { version: 1, policies: [] }\n    }\n    try {\n      const policies = await loadPolicyFile(policyPath)\n      return { version: 1, policies }\n    } catch (err) {\n      return reply.code(400).send({\n        error: 'policy.json failed to parse',\n        details: (err as Error).message,\n      })\n    }\n  })\n\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { severity?: string; policyId?: string }\n  }>('/policies/violations', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const log = new PolicyViolationsLog(proj.paths.policyViolationsPath)\n    let violations = await log.readAll()\n    if (req.query.severity) {\n      const sev = PolicySeveritySchema.safeParse(req.query.severity)\n      if (!sev.success) {\n        return reply.code(400).send({\n          error: 'invalid severity',\n          details: sev.error.format(),\n        })\n      }\n      violations = violations.filter((v) => v.severity === sev.data)\n    }\n    if (req.query.policyId) {\n      violations = violations.filter((v) => v.policyId === req.query.policyId)\n    }\n    return { violations }\n  })\n\n  // Soft guardrail read path (ADR-108 / policies-soft-guardrail.md). Returns\n  // the policies APPLICABLE to a node — the rules an agent should be aware of\n  // while working there. This INFORMS; it never blocks and carries no verdict.\n  // Matching is a direct subject/region match (see selectApplicablePolicies),\n  // not the full overlay traversal (ADR-105 §5), which is still unbuilt.\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { node?: string }\n  }>('/policies/applicable', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const nodeId = req.query.node\n    if (!nodeId) {\n      return reply.code(400).send({ error: 'missing required query param \"node\"' })\n    }\n    const policyPath = ctx.policyFilePathFor(proj)\n    let policies: Policy[] = []\n    if (policyPath) {\n      try {\n        policies = await loadPolicyFile(policyPath)\n      } catch (err) {\n        return reply.code(400).send({\n          error: 'policy.json failed to parse',\n          details: (err as Error).message,\n        })\n      }\n    }\n    const applicable = selectApplicablePolicies(proj.graph, policies, nodeId)\n    return { node: nodeId, applicable }\n  })\n\n  scope.post<{\n    Params: { project?: string }\n    Body: { hypotheticalAction?: unknown }\n  }>('/policies/check', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const parsed = PoliciesCheckBodySchema.safeParse(req.body ?? {})\n    if (!parsed.success) {\n      return reply.code(400).send({\n        error: 'invalid /policies/check body',\n        details: parsed.error.format(),\n      })\n    }\n\n    const policyPath = ctx.policyFilePathFor(proj)\n    let policies: Policy[] = []\n    if (policyPath) {\n      try {\n        policies = await loadPolicyFile(policyPath)\n      } catch (err) {\n        return reply.code(400).send({\n          error: 'policy.json failed to parse',\n          details: (err as Error).message,\n        })\n      }\n    }\n\n    // No hypothetical → return current violations against the live graph.\n    // With a hypothetical → simulate the action against a deep-copy graph\n    // (avoids mutation authority concerns), evaluate, return the delta.\n    const evalCtx = { now: () => Date.now() }\n    if (!parsed.data.hypotheticalAction) {\n      const violations = evaluateAllPolicies(proj.graph, policies, evalCtx)\n      const blocking = violations.filter((v) => v.onViolation === 'block')\n      return { allowed: blocking.length === 0, violations }\n    }\n\n    // For now the dry-run simulation re-uses evaluateAllPolicies on the\n    // current graph. Full hypothetical simulation (e.g. \"what if I added\n    // this OBSERVED edge?\") is the v0.2.4-δ scope; #117 ships the surface,\n    // #118 fills in the action shapes' simulation logic.\n    const violations = evaluateAllPolicies(proj.graph, policies, evalCtx)\n    const blocking = violations.filter((v) => v.onViolation === 'block')\n    return {\n      allowed: blocking.length === 0,\n      hypotheticalAction: parsed.data.hypotheticalAction,\n      violations,\n    } as { allowed: boolean; hypotheticalAction: unknown; violations: PolicyViolation[] }\n  })\n\n  // ── /extend — surgical instrumentation tools (ADR-081, ADR-086) ─────────\n  // Six routes. Three read-only (list-uninstrumented, lookup, describe),\n  // three operative (apply, dry-run, rollback). File-scope restricted per\n  // the extend-skill contract: only instrumentation/otel-init files and\n  // package.json. Dual-mounted via registerRoutes.\n\n  scope.get<{ Params: { project?: string } }>('/extend/list-uninstrumented', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply.code(409).send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    try {\n      const results = await listUninstrumented({ project: proj.name, scanPath: proj.scanPath })\n      return { libraries: results }\n    } catch (err) {\n      return reply.code(500).send({ error: (err as Error).message })\n    }\n  })\n\n  scope.get<{\n    Params: { project?: string }\n    Querystring: { library?: string; version?: string }\n  }>('/extend/lookup', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    const { library, version } = req.query\n    if (!library) {\n      return reply.code(400).send({ error: 'query parameter `library` is required' })\n    }\n    const result = lookupInstrumentation(library, version)\n    if (!result) {\n      return reply.code(404).send({ error: 'library not found in registry', library })\n    }\n    return result\n  })\n\n  scope.get<{ Params: { project?: string } }>('/extend/describe', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply.code(409).send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    try {\n      const state = await describeProjectInstrumentation({ project: proj.name, scanPath: proj.scanPath })\n      return state\n    } catch (err) {\n      return reply.code(500).send({ error: (err as Error).message })\n    }\n  })\n\n  scope.post<{\n    Params: { project?: string }\n    Body: { library?: string; instrumentation_package?: string; version?: string; registration_snippet?: string }\n  }>('/extend/apply', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply.code(409).send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    const { library, instrumentation_package, version, registration_snippet } = req.body ?? {}\n    if (!library || !instrumentation_package || !version || !registration_snippet) {\n      return reply.code(400).send({ error: 'body must include library, instrumentation_package, version, registration_snippet' })\n    }\n    try {\n      const result = await applyExtension(\n        { project: proj.name, scanPath: proj.scanPath },\n        { library, instrumentation_package, version, registration_snippet },\n      )\n      return result\n    } catch (err) {\n      return reply.code(500).send({ error: (err as Error).message })\n    }\n  })\n\n  scope.post<{\n    Params: { project?: string }\n    Body: { library?: string; instrumentation_package?: string; version?: string; registration_snippet?: string }\n  }>('/extend/dry-run', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply.code(409).send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    const { library, instrumentation_package, version, registration_snippet } = req.body ?? {}\n    if (!library || !instrumentation_package || !version || !registration_snippet) {\n      return reply.code(400).send({ error: 'body must include library, instrumentation_package, version, registration_snippet' })\n    }\n    try {\n      const result = await dryRunExtension(\n        { project: proj.name, scanPath: proj.scanPath },\n        { library, instrumentation_package, version, registration_snippet },\n      )\n      return result\n    } catch (err) {\n      return reply.code(500).send({ error: (err as Error).message })\n    }\n  })\n\n  scope.post<{\n    Params: { project?: string }\n    Body: { library?: string }\n  }>('/extend/rollback', async (req, reply) => {\n    const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject)\n    if (!proj) return\n    if (!proj.scanPath) {\n      return reply.code(409).send({ error: 'scan path not configured for this project', project: proj.name })\n    }\n    const { library } = req.body ?? {}\n    if (!library) {\n      return reply.code(400).send({ error: 'body must include library' })\n    }\n    try {\n      const result = await rollbackExtension(\n        { project: proj.name, scanPath: proj.scanPath },\n        { library },\n      )\n      return result\n    } catch (err) {\n      return reply.code(500).send({ error: (err as Error).message })\n    }\n  })\n}\n\nexport async function buildApi(opts: BuildApiOptions): Promise<FastifyInstance> {\n  const app = Fastify({ logger: false })\n  await app.register(cors, { origin: true })\n\n  // ADR-073 §3/§4 — `buildApi` owns auth enforcement so every listener that\n  // serves the REST surface gets the same gate, whoever builds it. When a\n  // caller doesn't pass auth options explicitly, they resolve from the\n  // environment (`NEAT_AUTH_TOKEN` / `NEAT_AUTH_PROXY` / `NEAT_PUBLIC_READ`)\n  // through the single `readAuthEnv` reader. A caller that does pass them\n  // wins, so the daemon and `serve` keep their explicit wiring; a caller that\n  // omits them — `neat watch` — inherits the same token rather than serving\n  // open by omission. The bind-host loopback refusal stays with the binding\n  // caller via `assertBindAuthority`, which reads the same env.\n  const env = readAuthEnv()\n  const authToken = opts.authToken ?? env.authToken\n  const trustProxy = opts.trustProxy ?? env.trustProxy\n  const publicRead = opts.publicRead ?? env.publicRead\n\n  // ADR-073 §3 — bearer middleware sits ahead of every route handler. No-op\n  // when the resolved token is undefined; loopback-only callers (the laptop\n  // dev path) hit that branch. `publicRead` opens GET / HEAD / OPTIONS to\n  // anonymous callers while keeping writes gated.\n  mountBearerAuth(app, {\n    token: authToken,\n    trustProxy,\n    publicRead,\n  })\n\n  // ADR-073 §3 amendment — `/api/config` is always unauthenticated. The web\n  // shell hits it before any bearer-carrying request to learn which mode the\n  // daemon is in. Exposes exactly two booleans — `publicRead` and\n  // `authProxy` — and nothing else; no project list, no version, no env.\n  app.get('/api/config', async () => ({\n    publicRead: publicRead === true,\n    authProxy: trustProxy === true,\n  }))\n\n  const startedAt = opts.startedAt ?? Date.now()\n  const registry = buildLegacyRegistry(opts)\n\n  const legacyErrorsExplicit = !opts.projects && opts.errorsPath !== undefined\n  const legacyStaleExplicit = !opts.projects && opts.staleEventsPath !== undefined\n\n  const errorsPathFor = (proj: ProjectContext): string | undefined => {\n    if (proj.name === DEFAULT_PROJECT && !opts.projects) {\n      return legacyErrorsExplicit ? opts.errorsPath : undefined\n    }\n    return proj.paths.errorsPath\n  }\n  const staleEventsPathFor = (proj: ProjectContext): string | undefined => {\n    if (proj.name === DEFAULT_PROJECT && !opts.projects) {\n      return legacyStaleExplicit ? opts.staleEventsPath : undefined\n    }\n    return proj.paths.staleEventsPath\n  }\n\n  // policy.json lives at the project's scanPath root per ADR-042. Without a\n  // scanPath we have nowhere to read it from — those projects show as\n  // \"no policies configured\" via the empty-file response.\n  const policyFilePathFor = (proj: ProjectContext): string | undefined => {\n    if (!proj.scanPath) return undefined\n    return `${proj.scanPath}/policy.json`\n  }\n\n  const routeCtx: RouteContext = {\n    registry,\n    startedAt,\n    scope: 'root',\n    errorsPathFor,\n    staleEventsPathFor,\n    policyFilePathFor,\n    bootstrap: opts.bootstrap,\n    singleProject: opts.singleProject?.name,\n  }\n\n  // Daemon-wide /health (issue #343). Per ADR-049 the daemon is the unit of\n  // readiness; the response answers \"is this process up and what's it\n  // currently serving?\" without depending on a `default` project. Probes —\n  // orchestrator, kubelet readiness, systemd, supervisors — read this.\n  //\n  // The `projects` array surfaces every slot the daemon knows about. When\n  // the bootstrap tracker is wired (issue #340), the per-entry `status`\n  // and `elapsedMs` come from there so the orchestrator's wait loop can\n  // tell `bootstrapping` apart from `active` without per-project probes.\n  app.get('/health', async () => {\n    const uptimeMs = Date.now() - startedAt\n    const bootstrapList = opts.bootstrap?.list() ?? []\n    const byName = new Map(bootstrapList.map((p) => [p.name, p]))\n    const names = new Set<string>([\n      ...registry.list(),\n      ...bootstrapList.map((p) => p.name),\n    ])\n    const projects = [...names].sort().map((name) => {\n      const proj = registry.get(name)\n      const tracked = byName.get(name)\n      return {\n        name,\n        nodeCount: proj?.graph.order ?? 0,\n        edgeCount: proj?.graph.size ?? 0,\n        ...(tracked ? { status: tracked.status, elapsedMs: tracked.elapsedMs } : {}),\n      }\n    })\n    return {\n      ok: true,\n      uptimeMs,\n      // ADR-096 §7 — a per-project daemon carries its project at the top level\n      // so a spawn can confirm a daemon found on a reused port is serving THIS\n      // project before reusing it. The legacy multi-project daemon omits it and\n      // is matched against the `projects` array instead.\n      ...(opts.singleProject ? { project: opts.singleProject.name } : {}),\n      projects,\n    }\n  })\n\n  // ADR-096 §4 — \"the daemon is the project.\" A per-project daemon reports only\n  // its own project here, so the dashboard pins to the project this daemon\n  // serves rather than to whichever machine-registered project happens to be\n  // active. The legacy multi-project daemon hands back the machine-level\n  // registry (ADR-048) — the one documented bare-array GET. Either way the\n  // response is `Array<RegistryEntry>`, which the dashboard and the CLI's\n  // bare-verb resolver treat as a list primitive.\n  app.get('/projects', async (_req, reply) => {\n    if (opts.singleProject) {\n      const phase = opts.bootstrap?.status(opts.singleProject.name)\n      const entry: RegistryEntry = {\n        name: opts.singleProject.name,\n        path: opts.singleProject.path,\n        registeredAt: new Date(startedAt).toISOString(),\n        languages: [],\n        // The daemon is serving this project, so it's active unless its\n        // bootstrap broke. ('bootstrapping' still reads as active — the project\n        // is the one to land on; its graph route answers 503 until ready.)\n        status: phase === 'broken' ? 'broken' : 'active',\n      }\n      return [entry]\n    }\n    try {\n      return await listRegistryProjects()\n    } catch (err) {\n      return reply.code(500).send({\n        error: 'failed to read project registry',\n        details: (err as Error).message,\n      })\n    }\n  })\n\n  // Singular project lookup (ADR-061 #7). Distinct route from the\n  // `/projects/:project/...` dual-mount prefix because Fastify matches on\n  // the full path; the trailing-segment-less request lands here.\n  app.get<{ Params: { project: string } }>('/projects/:project', async (req, reply) => {\n    try {\n      const entry = await getRegistryProject(req.params.project)\n      if (!entry) {\n        return reply\n          .code(404)\n          .send({ error: 'project not found', project: req.params.project })\n      }\n      return { project: entry }\n    } catch (err) {\n      return reply.code(500).send({\n        error: 'failed to read project registry',\n        details: (err as Error).message,\n      })\n    }\n  })\n\n  // Default mount: /graph, /incidents, etc. resolve to project=default.\n  // `/health` at this scope is the daemon-wide handler registered above,\n  // not the per-project one (issue #343).\n  registerRoutes(app, routeCtx)\n\n  // Project-scoped mount: same handlers, URL params include `:project`,\n  // and `/health` answers per project.\n  await app.register(\n    async (scope) => {\n      registerRoutes(scope, { ...routeCtx, scope: 'project' })\n    },\n    { prefix: '/projects/:project' },\n  )\n\n  return app\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { resolve as registryResolve, list as registryList } from '@neat.is/instrumentation-registry'\nimport {\n  detectPackageManager,\n  runPackageManagerInstall,\n} from '../installers/package-manager.js'\n\nexport interface ExtendContext {\n  project: string\n  scanPath: string\n}\n\nexport interface LibraryCoverageResult {\n  library: string\n  coverage: 'bundled' | 'first-party' | 'third-party' | 'http-only' | 'gap'\n  installedVersion?: string\n  instrumentation_package?: string\n  package_version?: string\n  registration?: string\n  notes?: string\n}\n\nexport interface ProjectInstrumentationState {\n  hookFiles: string[]\n  envNeat: boolean\n  installedDeps: Record<string, string>\n}\n\nexport interface ExtensionApplyResult {\n  library: string\n  filesTouched: string[]\n  depsAdded: string[]\n  installOutput: string\n  alreadyApplied: boolean\n}\n\nexport interface ExtensionDiff {\n  library: string\n  filesTouched: string[]\n  depsToAdd: string[]\n  packageJsonPatch: object\n  templatePatch: string\n}\n\ninterface PackageJson {\n  name?: string\n  dependencies?: Record<string, string>\n  devDependencies?: Record<string, string>\n  [key: string]: unknown\n}\n\ninterface ExtendLogEntry {\n  timestamp: string\n  project: string\n  library: string\n  instrumentation_package: string\n  version: string\n  registration_snippet: string\n  filesTouched: string[]\n  depsAdded: string[]\n  installOutput: string\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n  try {\n    await fs.access(p)\n    return true\n  } catch {\n    return false\n  }\n}\n\nasync function readPackageJson(scanPath: string): Promise<PackageJson> {\n  const pkgPath = path.join(scanPath, 'package.json')\n  const raw = await fs.readFile(pkgPath, 'utf8')\n  return JSON.parse(raw) as PackageJson\n}\n\nasync function findHookFiles(scanPath: string): Promise<string[]> {\n  const entries = await fs.readdir(scanPath)\n  return entries\n    .filter(\n      (e) =>\n        (e.startsWith('instrumentation') || e.startsWith('otel-init')) &&\n        /\\.(ts|js)$/.test(e),\n    )\n    .sort()\n}\n\nfunction extendLogPath(): string {\n  return process.env.NEAT_EXTEND_LOG ?? path.join(os.homedir(), '.neat', 'extend-log.ndjson')\n}\n\nasync function appendExtendLog(entry: ExtendLogEntry): Promise<void> {\n  const logPath = extendLogPath()\n  await fs.mkdir(path.dirname(logPath), { recursive: true })\n  await fs.appendFile(logPath, JSON.stringify(entry) + '\\n', 'utf8')\n}\n\n// Insert `snippet` into `fileContent` at the instrumentation array.\n// Tries __INSTRUMENTATION_BLOCK__ first, then last instrumentations.push(,\n// then before new NodeSDK(. Returns null if no insertion point is found.\nfunction splicedContent(fileContent: string, snippet: string): string | null {\n  if (fileContent.includes('__INSTRUMENTATION_BLOCK__')) {\n    return fileContent.replace('__INSTRUMENTATION_BLOCK__', `${snippet}\\n__INSTRUMENTATION_BLOCK__`)\n  }\n\n  const lines = fileContent.split('\\n')\n\n  let lastPushIdx = -1\n  for (let i = 0; i < lines.length; i++) {\n    if (lines[i].includes('instrumentations.push(')) lastPushIdx = i\n  }\n  if (lastPushIdx >= 0) {\n    lines.splice(lastPushIdx + 1, 0, snippet)\n    return lines.join('\\n')\n  }\n\n  for (let i = 0; i < lines.length; i++) {\n    if (lines[i].includes('new NodeSDK(')) {\n      lines.splice(i, 0, snippet)\n      return lines.join('\\n')\n    }\n  }\n\n  return null\n}\n\nexport async function listUninstrumented(ctx: ExtendContext): Promise<LibraryCoverageResult[]> {\n  const pkg = await readPackageJson(ctx.scanPath)\n  const allDeps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }\n\n  const results: LibraryCoverageResult[] = []\n  for (const [library, installedVersion] of Object.entries(allDeps)) {\n    const entry = registryResolve(library, installedVersion)\n    if (!entry) continue\n    if (entry.coverage === 'bundled' || entry.coverage === 'http-only') continue\n    results.push({\n      library,\n      coverage: entry.coverage,\n      installedVersion,\n      instrumentation_package: entry.instrumentation_package,\n      package_version: entry.package_version,\n      registration: entry.registration,\n      notes: entry.notes,\n    })\n  }\n  return results\n}\n\nexport function lookupInstrumentation(\n  library: string,\n  installedVersion?: string,\n): LibraryCoverageResult | null {\n  const entry = registryResolve(library, installedVersion)\n  if (!entry) return null\n  return {\n    library: entry.library,\n    coverage: entry.coverage,\n    instrumentation_package: entry.instrumentation_package,\n    package_version: entry.package_version,\n    registration: entry.registration,\n    notes: entry.notes,\n  }\n}\n\nexport async function describeProjectInstrumentation(\n  ctx: ExtendContext,\n): Promise<ProjectInstrumentationState> {\n  const hookFiles = await findHookFiles(ctx.scanPath)\n  const envNeat = await fileExists(path.join(ctx.scanPath, '.env.neat'))\n\n  const registryInstrPackages = new Set<string>(\n    registryList()\n      .map((e) => e.instrumentation_package)\n      .filter((p): p is string => !!p),\n  )\n\n  const pkg = await readPackageJson(ctx.scanPath)\n  const allDeps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }\n\n  const installedDeps: Record<string, string> = {}\n  for (const [key, version] of Object.entries(allDeps)) {\n    if (key.startsWith('@opentelemetry/') || registryInstrPackages.has(key)) {\n      installedDeps[key] = version\n    }\n  }\n\n  return { hookFiles, envNeat, installedDeps }\n}\n\nexport async function applyExtension(\n  ctx: ExtendContext,\n  args: {\n    library: string\n    instrumentation_package: string\n    version: string\n    registration_snippet: string\n  },\n  options?: {\n    runInstall?: typeof runPackageManagerInstall\n  },\n): Promise<ExtensionApplyResult> {\n  const hookFiles = await findHookFiles(ctx.scanPath)\n\n  if (hookFiles.length === 0) {\n    throw new Error(\n      `No instrumentation hook files found in ${ctx.scanPath}. Run \\`neat init\\` first.`,\n    )\n  }\n\n  // Idempotency check: scan all hook files for the snippet\n  for (const file of hookFiles) {\n    const content = await fs.readFile(path.join(ctx.scanPath, file), 'utf8')\n    if (content.includes(args.registration_snippet)) {\n      return { library: args.library, filesTouched: [], depsAdded: [], installOutput: '', alreadyApplied: true }\n    }\n  }\n\n  const primaryFile = hookFiles[0]!\n  const primaryPath = path.join(ctx.scanPath, primaryFile)\n  const filesTouched: string[] = []\n  const depsAdded: string[] = []\n\n  // 1. Add dep to package.json if not already present\n  const pkgPath = path.join(ctx.scanPath, 'package.json')\n  const pkg = await readPackageJson(ctx.scanPath)\n  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {\n    pkg.dependencies = { ...(pkg.dependencies ?? {}), [args.instrumentation_package]: args.version }\n    await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\\n', 'utf8')\n    filesTouched.push('package.json')\n    depsAdded.push(`${args.instrumentation_package}@${args.version}`)\n  }\n\n  // 2. Splice registration snippet into hook file\n  const hookContent = await fs.readFile(primaryPath, 'utf8')\n  const patched = splicedContent(hookContent, args.registration_snippet)\n  if (!patched) {\n    throw new Error(\n      `Could not find instrumentation insertion point in ${primaryFile}. ` +\n        'Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.',\n    )\n  }\n  await fs.writeFile(primaryPath, patched, 'utf8')\n  filesTouched.push(primaryFile)\n\n  // 3. Run package manager install\n  const cmd = await detectPackageManager(ctx.scanPath)\n  const installer = options?.runInstall ?? runPackageManagerInstall\n  const install = await installer(cmd)\n  const installOutput =\n    install.exitCode === 0\n      ? `${cmd.pm} install succeeded`\n      : install.stderr || `${cmd.pm} install failed (exit ${install.exitCode})`\n\n  // 4. Log the apply\n  await appendExtendLog({\n    timestamp: new Date().toISOString(),\n    project: ctx.project,\n    library: args.library,\n    instrumentation_package: args.instrumentation_package,\n    version: args.version,\n    registration_snippet: args.registration_snippet,\n    filesTouched,\n    depsAdded,\n    installOutput,\n  })\n\n  return { library: args.library, filesTouched, depsAdded, installOutput, alreadyApplied: false }\n}\n\nexport async function dryRunExtension(\n  ctx: ExtendContext,\n  args: {\n    library: string\n    instrumentation_package: string\n    version: string\n    registration_snippet: string\n  },\n): Promise<ExtensionDiff> {\n  const hookFiles = await findHookFiles(ctx.scanPath)\n\n  if (hookFiles.length === 0) {\n    return {\n      library: args.library,\n      filesTouched: [],\n      depsToAdd: [],\n      packageJsonPatch: {},\n      templatePatch: \"No hook files found. Run 'neat init' first.\",\n    }\n  }\n\n  for (const file of hookFiles) {\n    const content = await fs.readFile(path.join(ctx.scanPath, file), 'utf8')\n    if (content.includes(args.registration_snippet)) {\n      return {\n        library: args.library,\n        filesTouched: [],\n        depsToAdd: [],\n        packageJsonPatch: {},\n        templatePatch: 'Already applied — no changes would be made.',\n      }\n    }\n  }\n\n  const primaryFile = hookFiles[0]!\n  const filesTouched: string[] = []\n  const depsToAdd: string[] = []\n  let packageJsonPatch: object = {}\n  let templatePatch = ''\n\n  const pkg = await readPackageJson(ctx.scanPath)\n  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {\n    packageJsonPatch = { dependencies: { [args.instrumentation_package]: args.version } }\n    depsToAdd.push(`${args.instrumentation_package}@${args.version}`)\n    filesTouched.push('package.json')\n  }\n\n  const hookContent = await fs.readFile(path.join(ctx.scanPath, primaryFile), 'utf8')\n  const patched = splicedContent(hookContent, args.registration_snippet)\n  if (patched) {\n    filesTouched.push(primaryFile)\n    templatePatch = `+ ${args.registration_snippet}`\n  } else {\n    templatePatch = 'Could not find insertion point in hook file.'\n  }\n\n  return { library: args.library, filesTouched, depsToAdd, packageJsonPatch, templatePatch }\n}\n\nexport async function rollbackExtension(\n  ctx: ExtendContext,\n  args: { library: string },\n): Promise<{ undone: boolean; message: string }> {\n  const logPath = extendLogPath()\n\n  if (!(await fileExists(logPath))) {\n    return { undone: false, message: 'no apply found for library' }\n  }\n\n  const raw = await fs.readFile(logPath, 'utf8')\n  const entries: ExtendLogEntry[] = raw\n    .trim()\n    .split('\\n')\n    .filter(Boolean)\n    .map((line) => JSON.parse(line) as ExtendLogEntry)\n\n  const match = [...entries]\n    .reverse()\n    .find((e) => e.project === ctx.project && e.library === args.library)\n\n  if (!match) {\n    return { undone: false, message: 'no apply found for library' }\n  }\n\n  // Remove dep from package.json\n  const pkgPath = path.join(ctx.scanPath, 'package.json')\n  if (await fileExists(pkgPath)) {\n    const pkg = await readPackageJson(ctx.scanPath)\n    if (pkg.dependencies?.[match.instrumentation_package]) {\n      const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies\n      pkg.dependencies = rest\n      await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\\n', 'utf8')\n    }\n  }\n\n  // Remove the registration snippet from hook files (no re-install — user owns the lockfile)\n  const hookFiles = await findHookFiles(ctx.scanPath)\n  for (const file of hookFiles) {\n    const filePath = path.join(ctx.scanPath, file)\n    const content = await fs.readFile(filePath, 'utf8')\n    if (content.includes(match.registration_snippet)) {\n      const filtered = content\n        .split('\\n')\n        .filter((line) => !line.includes(match.registration_snippet))\n        .join('\\n')\n      await fs.writeFile(filePath, filtered, 'utf8')\n      break\n    }\n  }\n\n  return {\n    undone: true,\n    message: `rolled back ${match.library} (${match.instrumentation_package})`,\n  }\n}\n","/**\n * Package-manager detection + install invocation.\n *\n * Issue #381 — the apply phase adds dependencies to package.json but\n * relies on the operator to run `npm install` afterwards. The v0.4.5\n * smoke surfaced this as a hard regression on Brief: the installer\n * adds `@opentelemetry/sdk-node` (and `@prisma/instrumentation` when\n * Prisma is in deps) and the next `npm run dev` fails with\n * `Cannot find module '@opentelemetry/sdk-node'` before the OTel SDK\n * even gets a chance to load.\n *\n * ADR-046's \"lockfiles never touched\" rule is about NEAT not directly\n * editing lockfile contents; letting the user's own package manager\n * update them as a side effect of `<pm> install` is consistent with\n * that contract. The clarification is documented in\n * docs/contracts/sdk-install.md.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { spawn } from 'node:child_process'\n\nexport type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm'\n\nexport interface PackageManagerCommand {\n  pm: PackageManager\n  // The directory the install command runs in. Monorepo workspaces install\n  // from the lockfile root, not the per-package directory, so detection\n  // walks up from `serviceDir` and reports the lockfile-owning parent.\n  cwd: string\n  // The argv passed to spawn(). Each entry is one positional token. Selected\n  // to keep install output quiet on the happy path; failures still print\n  // because stderr is streamed verbatim.\n  args: string[]\n}\n\n// Lockfile basename → package manager + the install args we run for it.\n// Priority order — first match wins when multiple lockfiles coexist (rare,\n// but `package-lock.json` alongside `pnpm-lock.yaml` happens during a\n// migration). Bun leads because a project that opted into Bun deliberately\n// rejects npm; pnpm and yarn follow for similar reasons; npm is the default\n// when nothing else applies.\nconst LOCKFILE_PRIORITY: ReadonlyArray<{\n  lockfile: string\n  pm: PackageManager\n  args: string[]\n}> = [\n  { lockfile: 'bun.lockb', pm: 'bun', args: ['install', '--no-summary'] },\n  { lockfile: 'pnpm-lock.yaml', pm: 'pnpm', args: ['install', '--no-summary'] },\n  { lockfile: 'yarn.lock', pm: 'yarn', args: ['install', '--silent'] },\n  {\n    lockfile: 'package-lock.json',\n    pm: 'npm',\n    args: ['install', '--no-audit', '--no-fund', '--prefer-offline'],\n  },\n]\n\n// Default when no lockfile is present anywhere in the ancestor chain — a\n// fresh project the operator hasn't installed yet. npm is the safe pick:\n// it's available on every Node install and produces a `package-lock.json`\n// the user can later swap out for pnpm/yarn/bun without losing fidelity.\nconst NPM_FALLBACK_ARGS = ['install', '--no-audit', '--no-fund', '--prefer-offline']\n\nasync function exists(p: string): Promise<boolean> {\n  try {\n    await fs.access(p)\n    return true\n  } catch {\n    return false\n  }\n}\n\n// Resolve the package-manager command for a service directory. Walks up from\n// `serviceDir` looking for a lockfile — the first ancestor that has one\n// owns the install. Monorepos (npm/pnpm/yarn workspaces, Bun workspaces)\n// land their lockfile at the workspace root, so this returns the root cwd\n// even when `serviceDir` is a per-package subdir.\n//\n// No lockfile anywhere → npm at the service dir. A fresh `create-next-app`\n// run sits in this bucket (no install yet, no lockfile to read).\nexport async function detectPackageManager(\n  serviceDir: string,\n): Promise<PackageManagerCommand> {\n  let dir = path.resolve(serviceDir)\n  const stops = new Set<string>()\n  for (let i = 0; i < 64; i++) {\n    if (stops.has(dir)) break\n    stops.add(dir)\n    for (const candidate of LOCKFILE_PRIORITY) {\n      const lockPath = path.join(dir, candidate.lockfile)\n      if (await exists(lockPath)) {\n        return { pm: candidate.pm, cwd: dir, args: [...candidate.args] }\n      }\n    }\n    const parent = path.dirname(dir)\n    if (parent === dir) break\n    dir = parent\n  }\n  return { pm: 'npm', cwd: path.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] }\n}\n\nexport interface PackageManagerInvocation {\n  pm: PackageManager\n  cwd: string\n  args: string[]\n  // 0 → success; non-zero → install failed. Reported in the orchestrator\n  // summary so the operator can act before the daemon goes hunting for\n  // spans that never arrive.\n  exitCode: number\n  // Stderr captured from the child process, trimmed. Empty on success.\n  // Surfaces the underlying failure cause without dumping the entire\n  // install log into the summary.\n  stderr: string\n}\n\n// Run the install command and resolve with the outcome. Stdout is dropped\n// (`<pm> install --silent`-style flags already keep it short); stderr is\n// captured so a failure can be relayed up to the orchestrator's summary.\nexport async function runPackageManagerInstall(\n  cmd: PackageManagerCommand,\n): Promise<PackageManagerInvocation> {\n  return new Promise((resolve) => {\n    const child = spawn(cmd.pm, cmd.args, {\n      cwd: cmd.cwd,\n      // Inherit PATH + HOME so the user's installed managers resolve.\n      env: process.env,\n      // `false` keeps the parent in control of cleanup if the orchestrator\n      // exits before install finishes. Cross-platform-safe.\n      shell: false,\n      stdio: ['ignore', 'ignore', 'pipe'],\n    })\n    let stderr = ''\n    child.stderr?.on('data', (chunk: Buffer) => {\n      stderr += chunk.toString('utf8')\n    })\n    child.on('error', (err) => {\n      resolve({\n        pm: cmd.pm,\n        cwd: cmd.cwd,\n        args: cmd.args,\n        exitCode: 127,\n        stderr: stderr + `\\n${err.message}`,\n      })\n    })\n    child.on('close', (code) => {\n      resolve({\n        pm: cmd.pm,\n        cwd: cmd.cwd,\n        args: cmd.args,\n        exitCode: code ?? 1,\n        stderr: stderr.trim(),\n      })\n    })\n  })\n}\n","// computeDivergences — the thesis surface, derived (ADR-060).\n//\n// Walks the live graph and surfaces the five locked divergence shapes:\n// missing-observed, missing-extracted, version-mismatch, host-mismatch,\n// and compat-violation. Pure: no I/O, no mutation, no async. The function\n// operates on a NeatGraph reference and returns a fresh DivergenceResult\n// each call — there is no persistence (binding rule 2).\n//\n// Mutation authority (ADR-030 / contract #3) is locked to ingest.ts and\n// extract/*; this module reads only. The contract test\n// `packages/core/test/audits/contracts.test.ts` enforces it.\n\nimport type {\n  CompatRuleRef,\n  Divergence,\n  DivergenceResult,\n  DivergenceType,\n  EdgeTypeValue,\n  GraphEdge,\n  GraphNode,\n  ServiceNode,\n} from '@neat.is/types'\nimport {\n  databaseId,\n  DivergenceResultSchema,\n  EdgeType,\n  NodeType,\n  parseEdgeId,\n  Provenance,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport {\n  checkCompatibility,\n  checkDeprecatedApi,\n  compatPairs,\n  deprecatedApis,\n} from './compat.js'\nimport { confidenceForEdge } from './traverse.js'\n\nexport interface DivergenceQueryOpts {\n  // Filter the result to a subset of divergence types. Undefined keeps all\n  // five. Empty set returns nothing.\n  type?: ReadonlySet<DivergenceType>\n  // Drop divergences below this confidence threshold. Undefined keeps all.\n  minConfidence?: number\n  // Scope to divergences that involve this node (as source or target).\n  node?: string\n}\n\n// (source, target, type) → which provenance variants are present. Each\n// bucket is the unit the missing-observed / missing-extracted detectors\n// operate over.\ninterface EdgeBucket {\n  source: string\n  target: string\n  type: GraphEdge['type']\n  extracted?: GraphEdge\n  observed?: GraphEdge\n  inferred?: GraphEdge\n  stale?: GraphEdge\n}\n\nfunction bucketKey(source: string, target: string, type: string): string {\n  return `${type}|${source}|${target}`\n}\n\nfunction bucketEdges(graph: NeatGraph): Map<string, EdgeBucket> {\n  const buckets = new Map<string, EdgeBucket>()\n  graph.forEachEdge((id, attrs) => {\n    const e = attrs as GraphEdge\n    const parsed = parseEdgeId(id)\n    // parseEdgeId can fall through to EXTRACTED for unknown shapes — fall\n    // back to the edge's own provenance when the id doesn't parse cleanly.\n    const provenance = parsed?.provenance ?? e.provenance\n    const key = bucketKey(e.source, e.target, e.type)\n    const cur =\n      buckets.get(key) ?? { source: e.source, target: e.target, type: e.type }\n    switch (provenance) {\n      case Provenance.EXTRACTED:\n        cur.extracted = e\n        break\n      case Provenance.OBSERVED:\n        cur.observed = e\n        break\n      case Provenance.INFERRED:\n        cur.inferred = e\n        break\n      default:\n        // STALE rides on what used to be an OBSERVED edge — the id format\n        // stays OBSERVED per identity.ts, so this branch is mostly defensive.\n        if (e.provenance === Provenance.STALE) cur.stale = e\n    }\n    buckets.set(key, cur)\n  })\n  return buckets\n}\n\nfunction nodeIsFrontier(graph: NeatGraph, nodeId: string): boolean {\n  if (!graph.hasNode(nodeId)) return false\n  const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n  return attrs.type === NodeType.FrontierNode\n}\n\n// A WebSocketChannelNode is minted OBSERVED-only from the HTTP upgrade span\n// (ADR-125): a channel is known from observation, never from static extraction,\n// so it has no declared twin to diverge against. Its edge is a `CONNECTS_TO`,\n// which is in the OBSERVABLE_EDGE_TYPES allowlist, so an OBSERVED-only\n// `service ──CONNECTS_TO──▶ ws-channel` would otherwise flag a spurious\n// `missing-extracted`. Suppressing it where the target is a channel node is\n// signal-preserving — there is no static edge that \"should\" exist — not\n// signal-hiding. See docs/contracts/divergence-query.md.\nfunction nodeIsWebsocketChannel(graph: NeatGraph, nodeId: string): boolean {\n  if (!graph.hasNode(nodeId)) return false\n  const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n  return attrs.type === NodeType.WebSocketChannelNode\n}\n\nfunction clampConfidence(n: number): number {\n  if (!Number.isFinite(n)) return 0\n  return Math.max(0, Math.min(1, n))\n}\n\nfunction reasonForMissingObserved(source: string, target: string, type: string): string {\n  return `Code declares ${source} → ${target} (${type}) but no production traffic has been observed for this edge.`\n}\n\nfunction reasonForMissingExtracted(source: string, target: string, type: string): string {\n  return `Production observed ${source} → ${target} (${type}) but static analysis did not surface this edge.`\n}\n\nconst RECOMMENDATION_MISSING_OBSERVED =\n  'Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.'\nconst RECOMMENDATION_MISSING_EXTRACTED =\n  '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.'\nconst RECOMMENDATION_HOST_MISMATCH =\n  'Check environment-specific config overrides — the runtime host differs from what static configuration declares.'\n\n// ADR-066 §4 — reweight against graded confidence.\n//\n// `missing-extracted` (OBSERVED-led) cascades from the OBSERVED edge's\n// graded confidence (signal-block grade per ADR-066 §2). `missing-observed`\n// weights by the EXTRACTED edge's graded confidence (per-extractor grade\n// per ADR-066 §1). Sub-floor EXTRACTED candidates never enter the graph\n// (precision floor, §3) so what surfaces here is backed by structural or\n// verified-call-site evidence.\n//\n// Falls back to confidenceForEdge for legacy edges loaded from a pre-v0.3.4\n// snapshot that don't carry a stored `confidence` field.\nfunction gradedConfidence(edge: GraphEdge): number {\n  if (typeof edge.confidence === 'number') return clampConfidence(edge.confidence)\n  return clampConfidence(confidenceForEdge(edge))\n}\n\n// Edge types production can actually emit as OBSERVED traffic — the CALLS\n// family plus cross-service and connection edges. `missing-observed` only\n// makes sense for these: an EXTRACTED edge of one of these types with no\n// OBSERVED twin is a real \"code declares it, production never ran it\" finding.\n//\n// Structural / static-only edge types (IMPORTS, CONFIGURED_BY, CONTAINS,\n// DEPENDS_ON, RUNS_ON) have no runtime span behind them, so there is never an\n// OBSERVED twin to be \"missing\" — measuring their tiers would report every\n// import and every config wire as a missing-observed divergence, which is\n// noise, not signal. Keep this an allowlist (not a denylist) so a new\n// structural edge type stays out of the missing-observed surface by default\n// until it is deliberately added here (divergence-query.md — the five locked\n// types compare CALLS-family edges at the shared grain).\nconst OBSERVABLE_EDGE_TYPES: ReadonlySet<EdgeTypeValue> = new Set([\n  EdgeType.CALLS,\n  EdgeType.CONNECTS_TO,\n  EdgeType.PUBLISHES_TO,\n  EdgeType.CONSUMES_FROM,\n])\n\nfunction detectMissingDivergences(\n  graph: NeatGraph,\n  bucket: EdgeBucket,\n): Divergence[] {\n  const out: Divergence[] = []\n\n  // CONTAINS is structural ownership (service → file), not a declared-vs-\n  // observed relationship — comparing its tiers would surface an OTel-only\n  // file node as a spurious missing-extracted finding (file-awareness.md §2).\n  // Divergence compares CALLS-family edges at the shared grain (§7).\n  if (bucket.type === EdgeType.CONTAINS) return out\n\n  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {\n    // Skip when the would-be target is a FrontierNode — those represent\n    // unresolved span peers, not real entities we expect OBSERVED traffic\n    // to. The coexistence contract is between EXTRACTED and OBSERVED on\n    // real nodes; FRONTIER is unknown territory.\n    if (!nodeIsFrontier(graph, bucket.target)) {\n      // ADR-066 §4 — weight by the EXTRACTED edge's graded confidence.\n      // Substring/hostname-shape candidates already dropped at the precision\n      // floor; what remains is structural or verified-call-site evidence.\n      out.push({\n        type: 'missing-observed',\n        source: bucket.source,\n        target: bucket.target,\n        edgeType: bucket.type,\n        extracted: bucket.extracted,\n        confidence: gradedConfidence(bucket.extracted),\n        reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),\n        recommendation: RECOMMENDATION_MISSING_OBSERVED,\n      })\n    }\n  }\n\n  if (bucket.observed && !bucket.extracted && !nodeIsWebsocketChannel(graph, bucket.target)) {\n    // ADR-066 §4 — cascade from the OBSERVED edge's graded confidence.\n    // OBSERVED-led finding; the headline divergence type. A WebSocketChannelNode\n    // target is skipped: it is OBSERVED-only by design (ADR-125), so an\n    // OBSERVED-only CONNECTS_TO onto it is expected, not a missing-extracted\n    // divergence — mirrors the CONTAINS exclusion above, keyed on target type.\n    out.push({\n      type: 'missing-extracted',\n      source: bucket.source,\n      target: bucket.target,\n      edgeType: bucket.type,\n      observed: bucket.observed,\n      confidence: gradedConfidence(bucket.observed),\n      reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),\n      recommendation: RECOMMENDATION_MISSING_EXTRACTED,\n    })\n  }\n\n  return out\n}\n\n// Returns the declared host of the service's static DB target, when\n// recoverable. ServiceNode.dbConnectionTarget is the static-extraction\n// surface for \"this service connects to X\" — `X` is host[:port] or a\n// docker-compose-style service name. Empty / undefined means we have no\n// EXTRACTED host to compare against and host-mismatch can't fire.\nfunction declaredHostFor(svc: ServiceNode): string | null {\n  const raw = svc.dbConnectionTarget?.trim()\n  if (!raw) return null\n  // Strip a trailing port if present so it lines up with DatabaseNode.host\n  // (ADR-028 §6 — DatabaseNode id excludes port).\n  const colon = raw.lastIndexOf(':')\n  if (colon === -1) return raw\n  const port = raw.slice(colon + 1)\n  if (/^\\d+$/.test(port)) return raw.slice(0, colon)\n  return raw\n}\n\nfunction hasExtractedConfiguredBy(graph: NeatGraph, svcId: string): boolean {\n  for (const edgeId of graph.outboundEdges(svcId)) {\n    const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n    if (e.type === EdgeType.CONFIGURED_BY && e.provenance === Provenance.EXTRACTED) {\n      return true\n    }\n  }\n  return false\n}\n\nfunction detectHostMismatch(\n  graph: NeatGraph,\n  svcId: string,\n  svc: ServiceNode,\n): Divergence[] {\n  const declaredHost = declaredHostFor(svc)\n  if (!declaredHost) return []\n  if (!hasExtractedConfiguredBy(graph, svcId)) return []\n\n  const out: Divergence[] = []\n  for (const edgeId of graph.outboundEdges(svcId)) {\n    const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n    if (edge.type !== EdgeType.CONNECTS_TO) continue\n    if (edge.provenance !== Provenance.OBSERVED) continue\n    const target = graph.getNodeAttributes(edge.target) as GraphNode\n    if (target.type !== NodeType.DatabaseNode) continue\n    const observedHost = target.host?.trim()\n    if (!observedHost) continue\n    if (observedHost === declaredHost) continue\n\n    out.push({\n      type: 'host-mismatch',\n      source: svcId,\n      target: edge.target,\n      extractedHost: declaredHost,\n      observedHost,\n      confidence: clampConfidence(confidenceForEdge(edge)),\n      reason: `Config declares ${svcId} connects to ${declaredHost}; production connects to ${observedHost}.`,\n      recommendation: RECOMMENDATION_HOST_MISMATCH,\n    })\n  }\n  return out\n}\n\nfunction detectCompatDivergences(\n  graph: NeatGraph,\n  svcId: string,\n  svc: ServiceNode,\n): Divergence[] {\n  const out: Divergence[] = []\n  const deps = svc.dependencies ?? {}\n\n  for (const edgeId of graph.outboundEdges(svcId)) {\n    const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n    if (edge.type !== EdgeType.CONNECTS_TO) continue\n    if (edge.provenance !== Provenance.OBSERVED) continue\n    const target = graph.getNodeAttributes(edge.target) as GraphNode\n    if (target.type !== NodeType.DatabaseNode) continue\n\n    // Driver-engine compat. Definitive — when a rule fires it's a\n    // version-mismatch with confidence 1.0.\n    for (const pair of compatPairs()) {\n      if (pair.engine !== target.engine) continue\n      const declared = deps[pair.driver]\n      if (!declared) continue\n      const result = checkCompatibility(\n        pair.driver,\n        declared,\n        target.engine,\n        target.engineVersion,\n      )\n      if (!result.compatible && result.reason) {\n        out.push({\n          type: 'version-mismatch',\n          source: svcId,\n          target: edge.target,\n          extractedVersion: declared,\n          observedVersion: target.engineVersion,\n          compatibility: 'incompatible',\n          confidence: 1.0,\n          reason: result.reason,\n          recommendation: result.minDriverVersion\n            ? `Upgrade ${pair.driver} to >= ${result.minDriverVersion}.`\n            : `Update the ${pair.driver} driver to a version compatible with ${target.engine} ${target.engineVersion}.`,\n        })\n      }\n    }\n\n    // Deprecated-api compat. Broader than version-mismatch — surfaces as\n    // compat-violation. Driver-engine rules above already covered the\n    // \"version is too low\" shape; deprecated covers \"version is too high\n    // / no longer supported.\"\n    for (const rule of deprecatedApis()) {\n      const declared = deps[rule.package]\n      if (!declared) continue\n      const result = checkDeprecatedApi(rule, declared)\n      if (!result.compatible && result.reason) {\n        const ruleRef: CompatRuleRef = {\n          kind: rule.kind ?? 'deprecated-api',\n          reason: result.reason,\n          package: rule.package,\n        }\n        out.push({\n          type: 'compat-violation',\n          source: svcId,\n          target: edge.target,\n          rule: ruleRef,\n          observed: edge,\n          confidence: 1.0,\n          reason: result.reason,\n          recommendation: `Replace deprecated ${rule.package}@${declared} with a supported version.`,\n        })\n      }\n    }\n  }\n  return out\n}\n\nfunction involvesNode(d: Divergence, nodeId: string): boolean {\n  return d.source === nodeId || d.target === nodeId\n}\n\n// A single service<->DB host drift lights up three ways: the host-mismatch\n// itself, a missing-extracted on the observed DB node (the OBSERVED CONNECTS_TO\n// edge went to the *wrong* host, so it has no EXTRACTED twin), and a\n// missing-observed on the declared DB node (the EXTRACTED wire to the declared\n// host was never driven because traffic went elsewhere). The two missing-*\n// findings are just the halves of the drift the host-mismatch already names in\n// full, so they collapse into it — one divergence per distinct problem, so\n// blast-radius counts stay honest (issue #591). Pass 1 and Pass 2 run\n// independently, so this reconciles them after both have emitted.\nfunction suppressHostMismatchHalves(all: Divergence[]): Divergence[] {\n  const observedHalf = new Set<string>() // `${source}->${target}` of the observed DB edge\n  const declaredHalf = new Set<string>() // declared DB node id\n  for (const d of all) {\n    if (d.type !== 'host-mismatch') continue\n    observedHalf.add(`${d.source}->${d.target}`)\n    declaredHalf.add(databaseId(d.extractedHost))\n  }\n  if (observedHalf.size === 0) return all\n  return all.filter((d) => {\n    if (\n      d.type === 'missing-extracted' &&\n      observedHalf.has(`${d.source}->${d.target}`)\n    ) {\n      return false\n    }\n    if (d.type === 'missing-observed' && declaredHalf.has(d.target)) return false\n    return true\n  })\n}\n\nexport function computeDivergences(\n  graph: NeatGraph,\n  opts: DivergenceQueryOpts = {},\n): DivergenceResult {\n  const all: Divergence[] = []\n\n  // Pass 1 — bucket every edge and emit missing-observed / missing-extracted.\n  const buckets = bucketEdges(graph)\n  for (const bucket of buckets.values()) {\n    for (const d of detectMissingDivergences(graph, bucket)) all.push(d)\n  }\n\n  // Pass 2 — per-service host + compat rules.\n  graph.forEachNode((nodeId, attrs) => {\n    const n = attrs as GraphNode\n    if (n.type !== NodeType.ServiceNode) return\n    const svc = n as ServiceNode\n    for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d)\n    for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d)\n  })\n\n  // Reconcile the two passes: a fired host-mismatch already tells the whole\n  // service<->DB drift story, so drop the redundant missing-* halves (#591).\n  const reconciled = suppressHostMismatchHalves(all)\n\n  // Filter + sort. Higher confidence first; within the same confidence,\n  // stable on (type, source, target) so callers see deterministic output.\n  let filtered = reconciled\n  if (opts.type) {\n    const allowed = opts.type\n    filtered = filtered.filter((d) => allowed.has(d.type))\n  }\n  if (opts.minConfidence !== undefined) {\n    const threshold = opts.minConfidence\n    filtered = filtered.filter((d) => d.confidence >= threshold)\n  }\n  if (opts.node) {\n    const target = opts.node\n    filtered = filtered.filter((d) => involvesNode(d, target))\n  }\n\n  // ADR-066 §4 / §5 — confidence desc; missing-extracted leads\n  // missing-observed at equal confidence (OBSERVED-led tiebreaker); then\n  // stable on (type, source, target).\n  const TYPE_LEADERSHIP: Record<DivergenceType, number> = {\n    'missing-extracted': 0,\n    'missing-observed': 1,\n    'version-mismatch': 2,\n    'host-mismatch': 3,\n    'compat-violation': 4,\n  }\n  filtered.sort((a, b) => {\n    if (b.confidence !== a.confidence) return b.confidence - a.confidence\n    const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type]\n    if (lead !== 0) return lead\n    if (a.type !== b.type) return a.type.localeCompare(b.type)\n    if (a.source !== b.source) return a.source.localeCompare(b.source)\n    return a.target.localeCompare(b.target)\n  })\n\n  return DivergenceResultSchema.parse({\n    divergences: filtered,\n    totalAffected: filtered.length,\n    computedAt: new Date().toISOString(),\n  })\n}\n","import { promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport semver from 'semver'\nimport compatData from '../compat.json' with { type: 'json' }\n\nexport interface CompatibilityResult {\n  compatible: boolean\n  reason?: string\n  minDriverVersion?: string\n}\n\nexport interface CompatPair {\n  kind?: 'driver-engine'\n  driver: string\n  engine: string\n  minDriverVersion: string\n  // The driver constraint only kicks in once the engine is at this major or higher.\n  // Older engines (e.g. PostgreSQL 13) accept the older driver fine.\n  minEngineVersion?: string\n  reason: string\n}\n\nexport interface NodeEngineConstraint {\n  kind?: 'node-engine'\n  package: string\n  packageMinVersion?: string\n  minNodeVersion: string\n  reason: string\n}\n\nexport interface PackageConflict {\n  kind?: 'package-conflict'\n  package: string\n  packageMinVersion?: string\n  requires: { name: string; minVersion: string }\n  reason: string\n}\n\nexport interface DeprecatedApi {\n  kind?: 'deprecated-api'\n  package: string\n  packageMaxVersion?: string\n  reason: string\n}\n\nexport interface CompatMatrix {\n  pairs: CompatPair[]\n  nodeEngineConstraints?: NodeEngineConstraint[]\n  packageConflicts?: PackageConflict[]\n  deprecatedApis?: DeprecatedApi[]\n}\n\nconst bundledMatrix = compatData as CompatMatrix\nlet mergedMatrix: CompatMatrix | null = null\nlet remoteLoadAttempted = false\n\nconst REMOTE_CACHE_DIR = path.join(os.homedir(), '.neat')\nconst REMOTE_CACHE_PATH = path.join(REMOTE_CACHE_DIR, 'compat-cache.json')\nconst REMOTE_TTL_MS = 24 * 60 * 60 * 1000\n\ninterface RemoteCacheFile {\n  fetchedAt: string\n  url: string\n  matrix: CompatMatrix\n}\n\n// Engines like Postgres/MySQL only carry a major in the version field, so semver\n// won't always parse them cleanly. Compare as integers when both sides look like\n// majors; otherwise fall back to semver.coerce.\nfunction engineMeetsThreshold(engineVersion: string, threshold: string): boolean {\n  const e = parseInt(engineVersion, 10)\n  const t = parseInt(threshold, 10)\n  if (Number.isFinite(e) && Number.isFinite(t)) return e >= t\n\n  const ec = semver.coerce(engineVersion)\n  const tc = semver.coerce(threshold)\n  if (ec && tc) return semver.gte(ec, tc)\n\n  return false\n}\n\nexport function checkCompatibility(\n  driver: string,\n  driverVersion: string,\n  engine: string,\n  engineVersion: string,\n): CompatibilityResult {\n  const matrix = currentMatrix()\n  const pair = matrix.pairs.find((p) => p.driver === driver && p.engine === engine)\n  if (!pair) return { compatible: true }\n\n  if (pair.minEngineVersion && !engineMeetsThreshold(engineVersion, pair.minEngineVersion)) {\n    return { compatible: true }\n  }\n\n  const driverCoerced = semver.coerce(driverVersion)\n  if (!driverCoerced) return { compatible: true }\n\n  if (semver.lt(driverCoerced, pair.minDriverVersion)) {\n    return {\n      compatible: false,\n      reason: pair.reason,\n      minDriverVersion: pair.minDriverVersion,\n    }\n  }\n\n  return { compatible: true }\n}\n\nexport interface NodeEngineCheck {\n  compatible: boolean\n  reason?: string\n  requiredNodeVersion?: string\n}\n\n// True when `serviceNodeRange` (a service's `engines.node`) is guaranteed to\n// admit `requiredNodeVersion`. We use a permissive semver compare via `coerce`\n// — exact ranges like \">=20\" parse fine, exotic ones like \"^20 || ^22\" pass as\n// long as semver can resolve them. If the range can't be parsed at all, we\n// don't claim a conflict — under-flag rather than over-flag.\nfunction rangeAdmitsVersion(serviceNodeRange: string, requiredNodeVersion: string): boolean {\n  try {\n    const required = semver.coerce(requiredNodeVersion)\n    if (!required) return true\n    // Is every version that satisfies the service's range >= required? If yes,\n    // the service guarantees the requirement; if not, there's at least one\n    // admissible Node version that won't satisfy the dep — that's the\n    // conflict.\n    return semver.subset(serviceNodeRange, `>=${required.version}`, {\n      includePrerelease: false,\n    })\n  } catch {\n    return true\n  }\n}\n\nexport function checkNodeEngineConstraint(\n  constraint: NodeEngineConstraint,\n  declaredPackageVersion: string | undefined,\n  serviceNodeRange: string | undefined,\n): NodeEngineCheck {\n  if (constraint.packageMinVersion && declaredPackageVersion) {\n    const v = semver.coerce(declaredPackageVersion)\n    if (v && semver.lt(v, constraint.packageMinVersion)) {\n      return { compatible: true }\n    }\n  }\n  if (!serviceNodeRange) {\n    return { compatible: true }\n  }\n  if (rangeAdmitsVersion(serviceNodeRange, constraint.minNodeVersion)) {\n    return { compatible: true }\n  }\n  return {\n    compatible: false,\n    reason: constraint.reason,\n    requiredNodeVersion: constraint.minNodeVersion,\n  }\n}\n\nexport interface PackageConflictCheck {\n  compatible: boolean\n  reason?: string\n  requires?: { name: string; minVersion: string }\n  foundVersion?: string\n}\n\nexport function checkPackageConflict(\n  conflict: PackageConflict,\n  declaredPackageVersion: string | undefined,\n  declaredRequiredVersion: string | undefined,\n): PackageConflictCheck {\n  if (!declaredPackageVersion) return { compatible: true }\n  if (conflict.packageMinVersion) {\n    const v = semver.coerce(declaredPackageVersion)\n    if (v && semver.lt(v, conflict.packageMinVersion)) {\n      return { compatible: true }\n    }\n  }\n  if (!declaredRequiredVersion) {\n    return {\n      compatible: false,\n      reason: conflict.reason,\n      requires: conflict.requires,\n    }\n  }\n  const requiredCoerced = semver.coerce(declaredRequiredVersion)\n  if (!requiredCoerced) return { compatible: true }\n  if (semver.lt(requiredCoerced, conflict.requires.minVersion)) {\n    return {\n      compatible: false,\n      reason: conflict.reason,\n      requires: conflict.requires,\n      foundVersion: declaredRequiredVersion,\n    }\n  }\n  return { compatible: true }\n}\n\nexport function checkDeprecatedApi(\n  rule: DeprecatedApi,\n  declaredVersion: string | undefined,\n): { compatible: boolean; reason?: string } {\n  if (declaredVersion === undefined) return { compatible: true }\n  if (rule.packageMaxVersion) {\n    const v = semver.coerce(declaredVersion)\n    const max = semver.coerce(rule.packageMaxVersion)\n    if (v && max && semver.gt(v, max)) return { compatible: true }\n  }\n  return { compatible: false, reason: rule.reason }\n}\n\nfunction currentMatrix(): CompatMatrix {\n  return mergedMatrix ?? bundledMatrix\n}\n\nfunction mergeMatrices(a: CompatMatrix, b: CompatMatrix): CompatMatrix {\n  return {\n    pairs: [...a.pairs, ...(b.pairs ?? [])],\n    nodeEngineConstraints: [\n      ...(a.nodeEngineConstraints ?? []),\n      ...(b.nodeEngineConstraints ?? []),\n    ],\n    packageConflicts: [...(a.packageConflicts ?? []), ...(b.packageConflicts ?? [])],\n    deprecatedApis: [...(a.deprecatedApis ?? []), ...(b.deprecatedApis ?? [])],\n  }\n}\n\nasync function readRemoteCache(url: string): Promise<CompatMatrix | null> {\n  try {\n    const raw = await fs.readFile(REMOTE_CACHE_PATH, 'utf8')\n    const parsed = JSON.parse(raw) as RemoteCacheFile\n    if (parsed.url !== url) return null\n    const age = Date.now() - new Date(parsed.fetchedAt).getTime()\n    if (age > REMOTE_TTL_MS) return null\n    return parsed.matrix\n  } catch {\n    return null\n  }\n}\n\nasync function writeRemoteCache(url: string, matrix: CompatMatrix): Promise<void> {\n  const file: RemoteCacheFile = {\n    fetchedAt: new Date().toISOString(),\n    url,\n    matrix,\n  }\n  try {\n    await fs.mkdir(REMOTE_CACHE_DIR, { recursive: true })\n    await fs.writeFile(REMOTE_CACHE_PATH, JSON.stringify(file), 'utf8')\n  } catch (err) {\n    console.warn(`[neat] failed to cache compat matrix: ${(err as Error).message}`)\n  }\n}\n\n// Loads the bundled matrix and, if `NEAT_COMPAT_URL` is set, merges in a\n// remote extension. Falls back to a fresh fetch when the on-disk cache is\n// stale (24h TTL) or missing. Returns the merged matrix; subsequent calls are\n// memoised.\n//\n// Async because the fetch happens lazily on first use. Extract phase 2 awaits\n// this before iterating pairs; everything else goes through the sync\n// `currentMatrix()` view, which is fine because by the time CLI / traversal\n// runs, extraction has already loaded.\nexport async function ensureCompatLoaded(): Promise<CompatMatrix> {\n  if (mergedMatrix) return mergedMatrix\n  if (remoteLoadAttempted) {\n    mergedMatrix = bundledMatrix\n    return mergedMatrix\n  }\n  remoteLoadAttempted = true\n\n  const url = process.env.NEAT_COMPAT_URL\n  if (!url) {\n    mergedMatrix = bundledMatrix\n    return mergedMatrix\n  }\n\n  const cached = await readRemoteCache(url)\n  if (cached) {\n    mergedMatrix = mergeMatrices(bundledMatrix, cached)\n    return mergedMatrix\n  }\n\n  try {\n    const res = await fetch(url)\n    if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)\n    const remote = (await res.json()) as CompatMatrix\n    await writeRemoteCache(url, remote)\n    mergedMatrix = mergeMatrices(bundledMatrix, remote)\n    return mergedMatrix\n  } catch (err) {\n    console.warn(\n      `[neat] NEAT_COMPAT_URL fetch failed (${(err as Error).message}); using bundled matrix only`,\n    )\n    mergedMatrix = bundledMatrix\n    return mergedMatrix\n  }\n}\n\n// Reset the merged-matrix memo. Intended for tests so each test starts with a\n// freshly loaded matrix.\nexport function resetCompatMatrix(): void {\n  mergedMatrix = null\n  remoteLoadAttempted = false\n}\n\nexport function compatPairs(): readonly CompatPair[] {\n  return currentMatrix().pairs\n}\n\nexport function nodeEngineConstraints(): readonly NodeEngineConstraint[] {\n  return currentMatrix().nodeEngineConstraints ?? []\n}\n\nexport function packageConflicts(): readonly PackageConflict[] {\n  return currentMatrix().packageConflicts ?? []\n}\n\nexport function deprecatedApis(): readonly DeprecatedApi[] {\n  return currentMatrix().deprecatedApis ?? []\n}\n","{\n  \"pairs\": [\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"pg\",\n      \"engine\": \"postgresql\",\n      \"minDriverVersion\": \"8.0.0\",\n      \"minEngineVersion\": \"14\",\n      \"reason\": \"PostgreSQL 14+ requires scram-sha-256 auth by default; pg < 8.0.0 only speaks md5.\"\n    },\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"mysql2\",\n      \"engine\": \"mysql\",\n      \"minDriverVersion\": \"3.0.0\",\n      \"minEngineVersion\": \"8\",\n      \"reason\": \"MySQL 8 defaults to caching_sha2_password; mysql2 < 3.0.0 doesn't negotiate it.\"\n    },\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"mongoose\",\n      \"engine\": \"mongodb\",\n      \"minDriverVersion\": \"7.0.0\",\n      \"minEngineVersion\": \"7\",\n      \"reason\": \"MongoDB 7 drops legacy wire-protocol opcodes that mongoose < 7.0.0 still emits.\"\n    },\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"psycopg2\",\n      \"engine\": \"postgresql\",\n      \"minDriverVersion\": \"2.9.0\",\n      \"minEngineVersion\": \"14\",\n      \"reason\": \"PostgreSQL 14+ requires scram-sha-256 auth by default; psycopg2 < 2.9.0 only speaks md5.\"\n    },\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"pymongo\",\n      \"engine\": \"mongodb\",\n      \"minDriverVersion\": \"4.0.0\",\n      \"minEngineVersion\": \"7\",\n      \"reason\": \"MongoDB 7 drops legacy wire-protocol opcodes that pymongo < 4.0.0 still emits.\"\n    },\n    {\n      \"kind\": \"driver-engine\",\n      \"driver\": \"mysql-connector-python\",\n      \"engine\": \"mysql\",\n      \"minDriverVersion\": \"8.0.0\",\n      \"minEngineVersion\": \"8\",\n      \"reason\": \"MySQL 8 defaults to caching_sha2_password; mysql-connector-python < 8.0.0 doesn't negotiate it.\"\n    }\n  ],\n  \"nodeEngineConstraints\": [\n    {\n      \"kind\": \"node-engine\",\n      \"package\": \"vitest\",\n      \"packageMinVersion\": \"2.0.0\",\n      \"minNodeVersion\": \"18.0.0\",\n      \"reason\": \"vitest >= 2.0 drops Node 16 support; requires Node 18+.\"\n    },\n    {\n      \"kind\": \"node-engine\",\n      \"package\": \"next\",\n      \"packageMinVersion\": \"14.0.0\",\n      \"minNodeVersion\": \"18.17.0\",\n      \"reason\": \"Next 14+ requires Node 18.17+ (uses APIs introduced in that minor).\"\n    },\n    {\n      \"kind\": \"node-engine\",\n      \"package\": \"@modelcontextprotocol/sdk\",\n      \"packageMinVersion\": \"1.0.0\",\n      \"minNodeVersion\": \"18.0.0\",\n      \"reason\": \"@modelcontextprotocol/sdk >= 1 requires Node 18+ (web-streams polyfill removed).\"\n    }\n  ],\n  \"packageConflicts\": [\n    {\n      \"kind\": \"package-conflict\",\n      \"package\": \"@tanstack/react-query\",\n      \"packageMinVersion\": \"5.0.0\",\n      \"requires\": {\n        \"name\": \"react\",\n        \"minVersion\": \"18.0.0\"\n      },\n      \"reason\": \"@tanstack/react-query 5+ uses useSyncExternalStore — only available in React 18+.\"\n    },\n    {\n      \"kind\": \"package-conflict\",\n      \"package\": \"react-router-dom\",\n      \"packageMinVersion\": \"7.0.0\",\n      \"requires\": {\n        \"name\": \"react\",\n        \"minVersion\": \"18.0.0\"\n      },\n      \"reason\": \"react-router-dom 7+ requires React 18+.\"\n    },\n    {\n      \"kind\": \"package-conflict\",\n      \"package\": \"next\",\n      \"packageMinVersion\": \"14.0.0\",\n      \"requires\": {\n        \"name\": \"react\",\n        \"minVersion\": \"18.2.0\"\n      },\n      \"reason\": \"Next.js 14+ requires React 18.2+.\"\n    }\n  ],\n  \"deprecatedApis\": [\n    {\n      \"kind\": \"deprecated-api\",\n      \"package\": \"request\",\n      \"packageMaxVersion\": \"2.88.2\",\n      \"reason\": \"request is deprecated; use undici, node-fetch, or axios instead.\"\n    },\n    {\n      \"kind\": \"deprecated-api\",\n      \"package\": \"node-uuid\",\n      \"reason\": \"node-uuid is deprecated; use the `uuid` package.\"\n    }\n  ]\n}\n","import type {\n  BlastRadiusAffectedNode,\n  BlastRadiusResult,\n  DatabaseNode,\n  ErrorEvent,\n  GraphEdge,\n  GraphNode,\n  ObservedDependenciesResult,\n  RootCauseResult,\n  ServiceNode,\n  TransitiveDependenciesResult,\n  TransitiveDependency,\n} from '@neat.is/types'\nimport {\n  BlastRadiusResultSchema,\n  EdgeType,\n  NodeType,\n  ObservedDependenciesResultSchema,\n  PROV_RANK,\n  Provenance,\n  RootCauseResultSchema,\n  TransitiveDependenciesResultSchema,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport {\n  checkCompatibility,\n  checkNodeEngineConstraint,\n  checkPackageConflict,\n  compatPairs,\n  nodeEngineConstraints,\n  packageConflicts,\n} from './compat.js'\n\n// Contract anchors (see /docs/contracts.md + docs/contracts/provenance.md):\n//   * Rule 2 — Coexistence: walk by provenance priority, never collapse edges.\n//   * Rule 3 — FrontierNodes terminate traversal — edges to/from FrontierNodes\n//     are skipped, not merely deprioritized. If a node's only neighbour is a\n//     FrontierNode, traversal stops there. ADR-068 makes node-type the gating\n//     property, independent of edge provenance.\n//   * Rule 5 — Validate results against RootCauseResultSchema /\n//     BlastRadiusResultSchema before returning.\n//   * Rule 8 — No demo-name hardcoding: driver/engine identifiers come from\n//     node properties + compatPairs(), never literals.\n//   * ADR-029 — PROV_RANK is the canonical provenance ranking, imported\n//     from @neat.is/types so consumers (traversal, MCP, policies) all agree.\n\nconst ROOT_CAUSE_MAX_DEPTH = 5\nconst BLAST_RADIUS_DEFAULT_DEPTH = 10\n\nfunction isFrontierNode(graph: NeatGraph, nodeId: string): boolean {\n  if (!graph.hasNode(nodeId)) return false\n  const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n  return attrs.type === NodeType.FrontierNode\n}\n\n// Resolve a node on the walk path to the ServiceNode that carries the compat\n// evidence (declared dependencies + node engine). A ServiceNode resolves to\n// itself; a FileNode resolves to its owning service via the inbound\n// `service ──CONTAINS──▶ file` edge (file-awareness.md §2) — in a file-first\n// graph the caller on the path is a FileNode, but the dependency declaration\n// lives on the service that owns it. Anything else has no service to resolve\n// to. Returns the resolved ServiceNode's id + attributes, or null.\nfunction resolveOwningService(\n  graph: NeatGraph,\n  nodeId: string,\n): { id: string; svc: ServiceNode } | null {\n  if (!graph.hasNode(nodeId)) return null\n  const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n  if (attrs.type === NodeType.ServiceNode) {\n    return { id: nodeId, svc: attrs as ServiceNode }\n  }\n  if (attrs.type === NodeType.FileNode) {\n    for (const edgeId of graph.inboundEdges(nodeId)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (e.type !== EdgeType.CONTAINS) continue\n      const owner = graph.getNodeAttributes(e.source) as GraphNode\n      if (owner.type === NodeType.ServiceNode) {\n        return { id: e.source, svc: owner as ServiceNode }\n      }\n    }\n  }\n  return null\n}\n\n// Multiple edges between the same pair coexist by provenance (EXTRACTED next to\n// OBSERVED next to INFERRED). Traversal walks the system as the graph \"sees it\n// best\", so for any neighbour pair we pick the highest-provenance edge.\n// Edges connecting to FrontierNodes are skipped at the node level (ADR-068):\n// FrontierNodes are unresolved peers, traversal terminates at them rather than\n// pretending the path continues into unknown territory.\nfunction bestEdgeBySource(graph: NeatGraph, edgeIds: string[]): Map<string, GraphEdge> {\n  const best = new Map<string, GraphEdge>()\n  for (const id of edgeIds) {\n    const e = graph.getEdgeAttributes(id) as GraphEdge\n    if (isFrontierNode(graph, e.source)) continue\n    const cur = best.get(e.source)\n    if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {\n      best.set(e.source, e)\n    }\n  }\n  return best\n}\n\nfunction bestEdgeByTarget(graph: NeatGraph, edgeIds: string[]): Map<string, GraphEdge> {\n  const best = new Map<string, GraphEdge>()\n  for (const id of edgeIds) {\n    const e = graph.getEdgeAttributes(id) as GraphEdge\n    if (isFrontierNode(graph, e.target)) continue\n    const cur = best.get(e.target)\n    if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {\n      best.set(e.target, e)\n    }\n  }\n  return best\n}\n\n// Per-edge confidence is provenance × volume × recency × cleanliness.\n//   * provenance gives a ceiling: OBSERVED 1.0, INFERRED 0.7, EXTRACTED 0.5,\n//     STALE 0.3.\n//   * volume: log-scaled span count, saturating quickly so 1 span ≈ 0.55 and\n//     ~1k spans ≈ 1.0.\n//   * recency: 1.0 within an hour; decays toward 0.5 by 24h, toward 0.3 past.\n//   * cleanliness: error rate above ~10% pulls the score down — a flapping\n//     edge with thousands of spans shouldn't outrank a clean low-traffic one.\n// Bounded to [0, 1]. Walks of multiple edges multiply per-edge confidences.\nconst PROVENANCE_CEILING: Record<string, number> = {\n  OBSERVED: 1.0,\n  INFERRED: 0.7,\n  EXTRACTED: 0.5,\n  STALE: 0.3,\n}\n\nfunction volumeWeight(spanCount: number | undefined): number {\n  if (!spanCount || spanCount <= 0) return 0.5\n  // log10 saturating around ~1000 spans → ~1.0.\n  const w = 0.5 + Math.log10(spanCount + 1) / 3\n  return Math.min(1, w)\n}\n\nfunction recencyWeight(ageMs: number | undefined): number {\n  if (ageMs === undefined) return 0.8\n  const hour = 60 * 60 * 1000\n  if (ageMs <= hour) return 1.0\n  if (ageMs <= 24 * hour) {\n    const t = (ageMs - hour) / (23 * hour)\n    return 1.0 - 0.5 * t\n  }\n  return 0.3\n}\n\nfunction cleanlinessWeight(spanCount: number | undefined, errorCount: number | undefined): number {\n  if (!spanCount || spanCount <= 0) return 1\n  const rate = (errorCount ?? 0) / spanCount\n  if (rate <= 0.01) return 1\n  if (rate >= 0.5) return 0.3\n  return 1 - rate * 1.4\n}\n\nexport function confidenceForEdge(edge: GraphEdge, now = Date.now()): number {\n  const ceiling = PROVENANCE_CEILING[edge.provenance] ?? 0.5\n\n  // No runtime signal yet → the provenance ceiling is all we have. This keeps\n  // EXTRACTED-only graphs returning the same coarse 0.3/0.5/0.7/1.0 ladder\n  // they always have, while letting OBSERVED edges with real OTel data move\n  // off the ceiling once ingest starts populating signal counters.\n  const spanCount = edge.signal?.spanCount ?? edge.callCount\n  const ageMs = edge.signal?.lastObservedAgeMs ?? lastObservedAge(edge, now)\n  if (spanCount === undefined && ageMs === undefined && edge.signal === undefined) {\n    return ceiling\n  }\n\n  const v = volumeWeight(spanCount)\n  const r = recencyWeight(ageMs)\n  const c = cleanlinessWeight(spanCount, edge.signal?.errorCount)\n  return Math.max(0, Math.min(1, ceiling * v * r * c))\n}\n\nfunction lastObservedAge(edge: GraphEdge, now: number): number | undefined {\n  if (!edge.lastObserved) return undefined\n  const t = Date.parse(edge.lastObserved)\n  if (!Number.isFinite(t)) return undefined\n  return Math.max(0, now - t)\n}\n\n// Path-level confidence is the *product* of per-edge confidences (ADR-036).\n// Each hop is independent evidence and uncertainty compounds — a 3-hop path\n// of edges at confidence 0.8 each gives 0.512, not 0.8. Multiplying punishes\n// long walks accordingly, which is the contract's intent: traversal should\n// surface the cumulative trust the graph actually has, not the weakest link\n// alone.\nfunction confidenceFromMix(edges: GraphEdge[], now = Date.now()): number {\n  if (edges.length === 0) return 1.0\n  let product = 1\n  for (const e of edges) {\n    product *= confidenceForEdge(e, now)\n  }\n  return Math.max(0, Math.min(1, product))\n}\n\ninterface Walk {\n  path: string[]\n  edges: GraphEdge[]\n}\n\n// DFS along incoming edges from start, depth-bounded. Returns the longest path\n// reachable, picking best-provenance edges per neighbour pair so the walk\n// reflects the system as the graph knows it most reliably.\nfunction longestIncomingWalk(graph: NeatGraph, start: string, maxDepth: number): Walk {\n  let best: Walk = { path: [start], edges: [] }\n  const visited = new Set<string>([start])\n\n  function step(node: string, path: string[], edges: GraphEdge[]): void {\n    if (path.length > best.path.length) {\n      best = { path: [...path], edges: [...edges] }\n    }\n    if (path.length - 1 >= maxDepth) return\n\n    const incoming = bestEdgeBySource(graph, graph.inboundEdges(node))\n    for (const [srcId, edge] of incoming) {\n      if (visited.has(srcId)) continue\n      visited.add(srcId)\n      path.push(srcId)\n      edges.push(edge)\n      step(srcId, path, edges)\n      path.pop()\n      edges.pop()\n      visited.delete(srcId)\n    }\n  }\n\n  step(start, [start], [])\n  return best\n}\n\n// Per-shape match result. Each shape walks the same incoming `walk.path` but\n// looks for a different class of incompatibility. Adding a new shape (e.g. a\n// future ConfigNode \"missing required env var\" rule) is one entry in\n// `rootCauseShapes` plus its match function — no restructure to getRootCause.\ninterface RootCauseMatch {\n  rootCauseNode: string\n  rootCauseReason: string\n  fixRecommendation?: string\n}\n\ntype RootCauseShape = (\n  graph: NeatGraph,\n  origin: GraphNode,\n  walk: Walk,\n) => RootCauseMatch | null\n\n// DatabaseNode origin → driver/engine compat (the original v0.1.x behavior,\n// preserved verbatim). The walk ignores non-ServiceNodes; the first upstream\n// service whose declared driver fails compat against the origin DB's\n// (engine, engineVersion) wins.\nfunction databaseRootCauseShape(\n  graph: NeatGraph,\n  origin: GraphNode,\n  walk: Walk,\n): RootCauseMatch | null {\n  const targetDb = origin as DatabaseNode\n  // Pairs that could possibly hit on this engine — narrowed once outside the\n  // walk so we don't re-scan the matrix for every service we visit.\n  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine)\n  if (candidatePairs.length === 0) return null\n\n  for (const id of walk.path) {\n    // The compat carrier is a service: a ServiceNode resolves to itself, a\n    // FileNode on the path resolves to its owning service via CONTAINS\n    // (file-awareness.md §2). In a file-first graph the caller on the walk is\n    // the FileNode that holds the CALLS edge, but the declared driver lives on\n    // the service that owns it.\n    const owner = resolveOwningService(graph, id)\n    if (!owner) continue\n    const { id: serviceId, svc } = owner\n    const deps = svc.dependencies ?? {}\n    for (const pair of candidatePairs) {\n      const declared = deps[pair.driver]\n      if (!declared) continue\n      const result = checkCompatibility(\n        pair.driver,\n        declared,\n        targetDb.engine,\n        targetDb.engineVersion,\n      )\n      if (!result.compatible) {\n        return {\n          rootCauseNode: serviceId,\n          rootCauseReason: result.reason ?? 'incompatible driver',\n          ...(result.minDriverVersion\n            ? {\n                fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`,\n              }\n            : {}),\n        }\n      }\n    }\n  }\n  return null\n}\n\n// ServiceNode origin → node-engine + package-conflict shapes from compat.ts.\n// The check is over each ServiceNode along the incoming walk (the origin\n// itself + any upstream callers): a node-engine constraint failing against\n// the service's `engines.node`, or a package-conflict where a declared dep\n// requires a peer at a higher version than the service has.\nfunction serviceRootCauseShape(\n  graph: NeatGraph,\n  _origin: GraphNode,\n  walk: Walk,\n): RootCauseMatch | null {\n  for (const id of walk.path) {\n    // ServiceNode → itself; FileNode → owning service via CONTAINS\n    // (file-awareness.md §2). The compat evidence (declared deps, node engine)\n    // lives on the service, even when the caller on the walk is a file.\n    const owner = resolveOwningService(graph, id)\n    if (!owner) continue\n    const { id: serviceId, svc } = owner\n    const deps = svc.dependencies ?? {}\n    const serviceNodeEngine = svc.nodeEngine\n\n    for (const constraint of nodeEngineConstraints()) {\n      const declared = deps[constraint.package]\n      if (!declared) continue\n      const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine)\n      if (!result.compatible && result.reason) {\n        return {\n          rootCauseNode: serviceId,\n          rootCauseReason: result.reason,\n          ...(result.requiredNodeVersion\n            ? {\n                fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`,\n              }\n            : {}),\n        }\n      }\n    }\n\n    for (const conflict of packageConflicts()) {\n      const declared = deps[conflict.package]\n      if (!declared) continue\n      const requiredDeclared = deps[conflict.requires.name]\n      const result = checkPackageConflict(conflict, declared, requiredDeclared)\n      if (!result.compatible && result.reason) {\n        return {\n          rootCauseNode: serviceId,\n          rootCauseReason: result.reason,\n          fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`,\n        }\n      }\n    }\n  }\n  return null\n}\n\n// FileNode origin → resolve the file to its owning service (file-awareness.md\n// §2) and run the service shape. In a file-first graph an error can land on a\n// FileNode (the file that holds the failing CALLS edge); the incompatibility,\n// if any, is still a property of the service that owns the file's declared\n// dependencies. The owning service is folded into the origin's position so the\n// service shape scans it alongside the upstream walk.\nfunction fileRootCauseShape(\n  graph: NeatGraph,\n  origin: GraphNode,\n  walk: Walk,\n): RootCauseMatch | null {\n  const owner = resolveOwningService(graph, origin.id)\n  if (!owner) return null\n  return serviceRootCauseShape(graph, owner.svc, walk)\n}\n\n// Dispatch by origin node type per ADR-037. Origin types not present here\n// (InfraNode, ConfigNode, FrontierNode) cleanly return null — getRootCause\n// needs an explicit shape to know what an \"incompatibility\" looks like for\n// that origin, and those types don't have one yet.\nconst rootCauseShapes: Partial<Record<GraphNode['type'], RootCauseShape>> = {\n  [NodeType.DatabaseNode]: databaseRootCauseShape,\n  [NodeType.ServiceNode]: serviceRootCauseShape,\n  [NodeType.FileNode]: fileRootCauseShape,\n}\n\nexport function getRootCause(\n  graph: NeatGraph,\n  errorNodeId: string,\n  errorEvent?: ErrorEvent,\n  incidents?: ErrorEvent[],\n): RootCauseResult | null {\n  if (!graph.hasNode(errorNodeId)) return null\n  const origin = graph.getNodeAttributes(errorNodeId) as GraphNode\n  const shape = rootCauseShapes[origin.type]\n\n  if (shape) {\n    const walk = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH)\n    const match = shape(graph, origin, walk)\n    if (match) {\n      const reason = errorEvent\n        ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})`\n        : match.rootCauseReason\n\n      // Schema-validate before return (ADR-036, #139). A drift in the result\n      // shape becomes a runtime throw at the call site rather than a silently\n      // malformed payload reaching MCP / REST consumers.\n      return RootCauseResultSchema.parse({\n        rootCauseNode: match.rootCauseNode,\n        rootCauseReason: reason,\n        traversalPath: walk.path,\n        edgeProvenances: walk.edges.map((e) => e.provenance),\n        confidence: confidenceFromMix(walk.edges),\n        fixRecommendation: match.fixRecommendation,\n      })\n    }\n  }\n\n  // A service surfacing a failure may be the entry point of a cross-service\n  // 500 that actually originates downstream. Nothing calls the entry service,\n  // so the incoming walk above is empty — but its own OBSERVED CALLS edge to\n  // the callee carries the failure. Follow that outbound failing CALLS chain to\n  // the real culprit's handler before self-attributing the caller's mislabelled\n  // CLIENT span (#589). Only null-returns here when no downstream call is\n  // failing, i.e. the failure is in process at the origin.\n  if (origin.type === NodeType.ServiceNode) {\n    const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent)\n    if (crossService) return crossService\n  }\n\n  // No graph edge carried an incompatibility and no downstream call is failing —\n  // but a service can fail in process (a 500 thrown inside its own handler)\n  // without that failure ever crossing an edge, so the walk above sees a\n  // healthy-looking node. The recorded incident store is the OBSERVED evidence\n  // the graph can't carry: it localizes the failure to the file:line / route the\n  // failing span captured. Consulting it here keeps root-cause useful for the\n  // in-process case instead of reporting \"healthy\" over a pile of 500s (#584).\n  return rootCauseFromIncidents(errorNodeId, incidents, errorEvent)\n}\n\n// OBSERVED-grade confidence for an incident-localized cause. The incident is a\n// real captured runtime fact (where the failure surfaced), but it names the\n// surface, not a proven upstream incompatibility — so it sits below an\n// edge-walked compat result yet well above an EXTRACTED guess.\nconst INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6\n\n// Match an incident to the queried node the same way the REST incident-history\n// read does (api.ts): an exact affectedNode hit, or a service match when the\n// node is the service the incident was recorded against. A file-grained\n// affectedNode (file:<svc>:<path>) still matches the owning service this way.\nfunction incidentMatchesNode(ev: ErrorEvent, nodeId: string): boolean {\n  return ev.affectedNode === nodeId || ev.service === nodeId.replace(/^service:/, '')\n}\n\n// A failure localized to a node through the incident store: which node carries\n// the cause, the human reason, the file the failure surfaced in (when the\n// incident captured a `code.*` call site), and the derived fix.\ninterface IncidentLocalization {\n  rootCauseNode: string\n  rootCauseReason: string\n  // The FileNode the failure surfaced in, present only when the incident\n  // localized to a file grain. Callers walk node → file as a single OBSERVED\n  // hop when this is set.\n  fileNode?: string\n  fixRecommendation?: string\n}\n\n// Pick the most recent incident affecting `nodeId` and localize the failure to\n// the file:line / route it captured. Returns null when no incident touches the\n// node. Shared by the in-process fallback and the cross-service chain (#589) so\n// both describe a culprit's handler the same way.\nfunction localizeFromIncidents(\n  nodeId: string,\n  incidents: ErrorEvent[] | undefined,\n  errorEvent: ErrorEvent | undefined,\n): IncidentLocalization | null {\n  const pool = incidents && incidents.length > 0 ? incidents : errorEvent ? [errorEvent] : []\n  const relevant = pool.filter((ev) => incidentMatchesNode(ev, nodeId))\n  if (relevant.length === 0) return null\n\n  // Most recent incident is the representative; ISO timestamps sort lexically.\n  const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0]!\n  const attrs = latest.attributes ?? {}\n  const filepath = typeof attrs['code.filepath'] === 'string' ? attrs['code.filepath'] : undefined\n  const lineno = typeof attrs['code.lineno'] === 'number' ? attrs['code.lineno'] : undefined\n  const route = typeof attrs['http.route'] === 'string' ? attrs['http.route'] : undefined\n  const location = filepath ? `${filepath}${lineno !== undefined ? `:${lineno}` : ''}` : undefined\n\n  // Count the incidents of *this* failure mode, not every incident on the node.\n  // The reason names one message (`latest.errorMessage`); pairing it with the\n  // node's total incident count reads as though that one error happened N times\n  // when the node may be failing several different ways. Scope the count to the\n  // records sharing this message so \"3 recorded incidents\" means three of the\n  // failure the reason actually describes (issue #624).\n  const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage)\n  const count = sameMode.length\n  const tail = count > 1 ? ` (${count} recorded incidents)` : ' (1 recorded incident)'\n  const reasonParts = [`${latest.service}: ${latest.errorMessage}`]\n  if (location) reasonParts.push(`surfaced at ${location}`)\n  const rootCauseReason = `${reasonParts.join(' — ')}${tail}`\n\n  // When the incident localized to a file (affectedNode is a file id), name\n  // that file as the root cause. The \"edge\" the caller walks is the captured\n  // runtime attribution; OBSERVED is honest because the file came from a real\n  // `code.*` on the failing span. Otherwise the cause sits on the node itself.\n  const localizesToFile = latest.affectedNode !== nodeId && latest.affectedNode.startsWith('file:')\n  const fileNode = localizesToFile ? latest.affectedNode : undefined\n\n  const fixRecommendation = location\n    ? `Inspect ${location}${route ? ` handling ${route}` : ''}`\n    : route\n      ? `Inspect ${latest.service}'s handler for ${route}`\n      : undefined\n\n  return {\n    rootCauseNode: fileNode ?? nodeId,\n    rootCauseReason,\n    ...(fileNode ? { fileNode } : {}),\n    ...(fixRecommendation ? { fixRecommendation } : {}),\n  }\n}\n\n// Build a root-cause result from the recorded incident store when the graph\n// walk found nothing. Localizes the failure to the queried node itself (or the\n// file it surfaced in). Returns null when no incident touches the node — the\n// honest \"nothing to say\" answer.\nfunction rootCauseFromIncidents(\n  nodeId: string,\n  incidents: ErrorEvent[] | undefined,\n  errorEvent: ErrorEvent | undefined,\n): RootCauseResult | null {\n  const loc = localizeFromIncidents(nodeId, incidents, errorEvent)\n  if (!loc) return null\n\n  const traversalPath = loc.fileNode ? [nodeId, loc.fileNode] : [nodeId]\n  const edgeProvenances = loc.fileNode ? [Provenance.OBSERVED] : []\n\n  return RootCauseResultSchema.parse({\n    rootCauseNode: loc.rootCauseNode,\n    rootCauseReason: loc.rootCauseReason,\n    traversalPath,\n    edgeProvenances,\n    confidence: INCIDENT_ROOT_CAUSE_CONFIDENCE,\n    ...(loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}),\n  })\n}\n\n// A CALLS edge counts as failing when its OBSERVED signal recorded at least one\n// error. This is the signal the cross-service chain follows: the caller's call\n// to the callee returned a 5xx (#589).\nfunction isFailingCallEdge(e: GraphEdge): boolean {\n  return e.type === EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0\n}\n\n// Every node id that can originate an outbound CALLS edge on a service's behalf:\n// the service itself, plus each FileNode it CONTAINS. A file-first graph anchors\n// the caller's CALLS edge on the call-site file (file-awareness.md §4), so an\n// entry service's failing call may hang off one of its files, not the bare\n// service node.\nfunction callSourcesForService(graph: NeatGraph, serviceId: string): string[] {\n  const ids = [serviceId]\n  for (const edgeId of graph.outboundEdges(serviceId)) {\n    const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n    if (e.type !== EdgeType.CONTAINS) continue\n    const tgt = graph.getNodeAttributes(e.target) as GraphNode\n    if (tgt.type === NodeType.FileNode) ids.push(e.target)\n  }\n  return ids\n}\n\n// Did edge `e` to service `id` beat the current best failing call? Most recorded\n// errors win; ties break on PROV_RANK, then target id — deterministic.\nfunction failingCallDominates(\n  e: GraphEdge,\n  id: string,\n  curEdge: GraphEdge,\n  curId: string,\n): boolean {\n  const ec = e.signal?.errorCount ?? 0\n  const cc = curEdge.signal?.errorCount ?? 0\n  if (ec !== cc) return ec > cc\n  if (PROV_RANK[e.provenance] !== PROV_RANK[curEdge.provenance]) {\n    return PROV_RANK[e.provenance] > PROV_RANK[curEdge.provenance]\n  }\n  return id < curId\n}\n\n// The dominant failing outbound CALLS from a service: among the service's own\n// edges and those of the files it owns, the failing CALLS edge to another\n// service with the most recorded errors. Returns the next-hop service id and the\n// edge, or null when no downstream call is failing — meaning the failure is in\n// process here, not relayed from deeper.\nfunction dominantFailingCall(\n  graph: NeatGraph,\n  serviceId: string,\n  visited: Set<string>,\n): { nextService: string; edge: GraphEdge } | null {\n  let best: { nextService: string; edge: GraphEdge } | null = null\n  for (const src of callSourcesForService(graph, serviceId)) {\n    for (const edgeId of graph.outboundEdges(src)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (!isFailingCallEdge(e)) continue\n      if (isFrontierNode(graph, e.target)) continue\n      const owner = resolveOwningService(graph, e.target)\n      if (!owner || visited.has(owner.id)) continue\n      if (!best || failingCallDominates(e, owner.id, best.edge, best.nextService)) {\n        best = { nextService: owner.id, edge: e }\n      }\n    }\n  }\n  return best\n}\n\n// Walk the failing CALLS chain outbound from an entry service to the deepest\n// still-failing callee — the service whose own downstream calls are clean and\n// whose handler therefore threw (#589). Returns the path of service ids, the\n// failing edges along it, and the culprit, or null when nothing downstream is\n// failing.\nfunction followFailingCallChain(\n  graph: NeatGraph,\n  originServiceId: string,\n  maxDepth: number,\n): { path: string[]; edges: GraphEdge[]; culprit: string } | null {\n  const path = [originServiceId]\n  const edges: GraphEdge[] = []\n  const visited = new Set<string>([originServiceId])\n  let current = originServiceId\n\n  for (let depth = 0; depth < maxDepth; depth++) {\n    const hop = dominantFailingCall(graph, current, visited)\n    if (!hop) break\n    path.push(hop.nextService)\n    edges.push(hop.edge)\n    visited.add(hop.nextService)\n    current = hop.nextService\n  }\n\n  if (edges.length === 0) return null\n  return { path, edges, culprit: current }\n}\n\n// Localize a cross-service failure (#589). An entry ServiceNode surfaces a 500\n// that originates downstream: follow the failing CALLS chain to the culprit and\n// describe its handler, never the caller's mis-attributed CLIENT span. Returns\n// null when no outbound call is failing — the failure is in process here and the\n// caller falls through to the origin's own incident store.\nfunction crossServiceRootCause(\n  graph: NeatGraph,\n  originId: string,\n  incidents: ErrorEvent[] | undefined,\n  errorEvent: ErrorEvent | undefined,\n): RootCauseResult | null {\n  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH)\n  if (!chain) return null\n\n  const culprit = chain.culprit\n  const path = [...chain.path]\n  const edgeProvenances = chain.edges.map((e) => e.provenance)\n\n  // Cross-service confidence cascades over the failing CALLS edges and the\n  // incident-localization hop, so it lands below an edge-walked compat result.\n  const baseConfidence = confidenceFromMix(chain.edges)\n  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE))\n\n  const loc = localizeFromIncidents(culprit, incidents, errorEvent)\n  if (loc) {\n    let rootCauseNode = culprit\n    if (loc.fileNode) {\n      path.push(loc.fileNode)\n      edgeProvenances.push(Provenance.OBSERVED)\n      rootCauseNode = loc.fileNode\n    }\n    return RootCauseResultSchema.parse({\n      rootCauseNode,\n      rootCauseReason: loc.rootCauseReason,\n      traversalPath: path,\n      edgeProvenances,\n      confidence,\n      ...(loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}),\n    })\n  }\n\n  // No recorded incident for the culprit — still better than blaming the caller.\n  // Name the culprit service and read the reason off the failing edge.\n  const lastEdge = chain.edges[chain.edges.length - 1]!\n  const errs = lastEdge.signal?.errorCount ?? 0\n  const culpritName = culprit.replace(/^service:/, '')\n  return RootCauseResultSchema.parse({\n    rootCauseNode: culprit,\n    rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? '' : 's'})`,\n    traversalPath: path,\n    edgeProvenances,\n    confidence,\n    fixRecommendation: `Inspect ${culpritName}'s failing handler`,\n  })\n}\n\n// BFS along *inbound* edges from origin — the origin's dependents, i.e. what\n// breaks if the origin changes or fails (get-blast-radius.md, superseding\n// ADR-038's outbound direction). An edge `A ──depends-on──▶ B` means A breaks\n// when B changes, so the blast radius of B walks back along inbound edges to A\n// and everything that transitively depends on it. For an inbound edge the\n// neighbour is the edge's `source` (the dependent), so selection uses\n// bestEdgeBySource — the same machinery getRootCause walks inbound with.\n// Records each reachable dependent with the shortest distance back to origin\n// and the provenance of the edge that brought us to it. A sink (a database,\n// shared lib, leaf util) has no outbound edges but does have inbound ones, so\n// this is what makes its blast radius non-empty.\nexport function getBlastRadius(\n  graph: NeatGraph,\n  nodeId: string,\n  maxDepth = BLAST_RADIUS_DEFAULT_DEPTH,\n): BlastRadiusResult {\n  if (!graph.hasNode(nodeId)) {\n    return BlastRadiusResultSchema.parse({ origin: nodeId, affectedNodes: [], totalAffected: 0 })\n  }\n\n  // Each frame carries its full predecessor chain so the affected-node payload\n  // can surface `path` (origin → ... → nodeId) and `confidence` (cascaded over\n  // every edge along that path). The BFS visits each reachable node once on\n  // its shortest-distance path; later frames at greater distance are dropped.\n  interface Frame {\n    nodeId: string\n    distance: number\n    path: string[]\n    pathEdges: GraphEdge[]\n  }\n\n  const seen = new Map<string, BlastRadiusAffectedNode>()\n  const queue: Frame[] = [{ nodeId, distance: 0, path: [nodeId], pathEdges: [] }]\n  const enqueued = new Set<string>([nodeId])\n\n  while (queue.length > 0) {\n    const frame = queue.shift()!\n    if (frame.distance > 0 && frame.pathEdges.length > 0) {\n      const lastEdge = frame.pathEdges[frame.pathEdges.length - 1]!\n      seen.set(frame.nodeId, {\n        nodeId: frame.nodeId,\n        distance: frame.distance,\n        edgeProvenance: lastEdge.provenance,\n        path: frame.path,\n        confidence: confidenceFromMix(frame.pathEdges),\n      })\n    }\n    if (frame.distance >= maxDepth) continue\n\n    const incoming = bestEdgeBySource(graph, graph.inboundEdges(frame.nodeId))\n    for (const [srcId, edge] of incoming) {\n      if (enqueued.has(srcId)) continue\n      enqueued.add(srcId)\n      queue.push({\n        nodeId: srcId,\n        distance: frame.distance + 1,\n        path: [...frame.path, srcId],\n        pathEdges: [...frame.pathEdges, edge],\n      })\n    }\n  }\n\n  const affectedNodes = [...seen.values()].sort(\n    (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n  )\n  return BlastRadiusResultSchema.parse({\n    origin: nodeId,\n    affectedNodes,\n    totalAffected: affectedNodes.length,\n  })\n}\n\n// Default + max depth for transitive get_dependencies (issue #144). Default\n// 3 keeps the output legible at the agent layer; the contract caps the\n// caller-supplied value at 10 to prevent BFS blow-up on dense graphs.\nexport const TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH = 3\nexport const TRANSITIVE_DEPENDENCIES_MAX_DEPTH = 10\n\n// Transitive get_dependencies (ADR-039 / #144). BFS outbound from origin to\n// `depth` hops, returning a flat list with distance, edgeType, and provenance\n// per dependency. Origin is never in the list. Direct-only consumers pass\n// depth=1; the MCP get_dependencies tool defaults to 3.\n//\n// Reuses bestEdgeByTarget (FRONTIER filtered, PROV_RANK-best per pair) so\n// dedup behavior matches the rest of traversal. Result is schema-validated\n// before return per ADR-036 §Result schema validation.\nexport function getTransitiveDependencies(\n  graph: NeatGraph,\n  nodeId: string,\n  depth: number = TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH,\n): TransitiveDependenciesResult {\n  if (!graph.hasNode(nodeId)) {\n    return TransitiveDependenciesResultSchema.parse({\n      origin: nodeId,\n      depth,\n      dependencies: [],\n      total: 0,\n    })\n  }\n\n  interface Frame {\n    nodeId: string\n    distance: number\n    edge: GraphEdge | null\n  }\n\n  const seen = new Map<string, TransitiveDependency>()\n  const queue: Frame[] = [{ nodeId, distance: 0, edge: null }]\n  const enqueued = new Set<string>([nodeId])\n\n  while (queue.length > 0) {\n    const frame = queue.shift()!\n    if (frame.distance > 0 && frame.edge) {\n      seen.set(frame.nodeId, {\n        nodeId: frame.nodeId,\n        distance: frame.distance,\n        edgeType: frame.edge.type,\n        provenance: frame.edge.provenance,\n      })\n    }\n    if (frame.distance >= depth) continue\n\n    const outgoing = bestEdgeByTarget(graph, graph.outboundEdges(frame.nodeId))\n    for (const [tgtId, edge] of outgoing) {\n      if (enqueued.has(tgtId)) continue\n      enqueued.add(tgtId)\n      queue.push({ nodeId: tgtId, distance: frame.distance + 1, edge })\n    }\n  }\n\n  const dependencies = [...seen.values()].sort(\n    (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n  )\n  return TransitiveDependenciesResultSchema.parse({\n    origin: nodeId,\n    depth,\n    dependencies,\n    total: dependencies.length,\n  })\n}\n\n// Observed-only dependencies (issue #578). \"What does this node actually call at\n// runtime?\" — its OBSERVED outbound edges, file-grained.\n//\n// The subtlety the previous edges-only query missed: the call-site processor\n// lands OBSERVED CALLS on the FileNode that made the call, not on the owning\n// ServiceNode (file-awareness §4). So a query that starts at a ServiceNode sees\n// only its structural `CONTAINS` edges and reports \"no runtime traffic,\" while\n// the real dependency sits one hop away on a file it owns. When the origin is a\n// ServiceNode we therefore also read the OBSERVED outbound of the FileNodes it\n// `CONTAINS` and surface those file→target edges. This is not a service rollup\n// (file-awareness §3): the edges stay file-grained with the owning file as the\n// source; the service is only the grouping the query entered through.\n//\n// `observed` and `inboundObservedCount` separate two cases the old copy\n// conflated: a pure receiver — a node runtime hits but which calls nothing\n// downstream — has zero dependencies yet is plainly seen by OTel, so the caller\n// must not ask \"is OTel running?\" at it. That question is honest only when there\n// is no OBSERVED traffic at all and EXTRACTED outbound edges exist\n// (`hasExtractedOutbound`).\nexport function getObservedDependencies(\n  graph: NeatGraph,\n  nodeId: string,\n): ObservedDependenciesResult {\n  if (!graph.hasNode(nodeId)) {\n    return ObservedDependenciesResultSchema.parse({\n      origin: nodeId,\n      dependencies: [],\n      observed: false,\n      inboundObservedCount: 0,\n      hasExtractedOutbound: false,\n    })\n  }\n\n  const attrs = graph.getNodeAttributes(nodeId) as GraphNode\n\n  // The origin plus, when it's a service, the files it owns — the set of nodes\n  // whose OBSERVED edges belong to \"what this thing does at runtime.\"\n  const scope: string[] = [nodeId]\n  if (attrs.type === NodeType.ServiceNode) {\n    for (const edgeId of graph.outboundEdges(nodeId)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (e.type !== EdgeType.CONTAINS) continue\n      const owned = graph.getNodeAttributes(e.target) as GraphNode\n      if (owned.type === NodeType.FileNode) scope.push(e.target)\n    }\n  }\n\n  const dependencies: GraphEdge[] = []\n  const seenEdge = new Set<string>()\n  let hasExtractedOutbound = false\n  for (const src of scope) {\n    for (const edgeId of graph.outboundEdges(src)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      // CONTAINS is structural ownership, never a runtime dependency.\n      if (e.type === EdgeType.CONTAINS) continue\n      if (e.provenance === Provenance.OBSERVED) {\n        if (!seenEdge.has(e.id)) {\n          seenEdge.add(e.id)\n          dependencies.push(e)\n        }\n      } else if (e.provenance === Provenance.EXTRACTED) {\n        hasExtractedOutbound = true\n      }\n    }\n  }\n\n  // Was this node (or a file it owns) seen receiving traffic? Counting OBSERVED\n  // inbound edges is the pure-receiver signal — the \"hit N times, calls nothing\"\n  // shape that must read differently from \"never observed.\"\n  let inboundObservedCount = 0\n  for (const tgt of scope) {\n    for (const edgeId of graph.inboundEdges(tgt)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (e.type === EdgeType.CONTAINS) continue\n      if (e.provenance === Provenance.OBSERVED) inboundObservedCount += 1\n    }\n  }\n\n  dependencies.sort(\n    (a, b) =>\n      a.target.localeCompare(b.target) ||\n      a.source.localeCompare(b.source) ||\n      a.id.localeCompare(b.id),\n  )\n\n  return ObservedDependenciesResultSchema.parse({\n    origin: nodeId,\n    dependencies,\n    observed: dependencies.length > 0 || inboundObservedCount > 0,\n    inboundObservedCount,\n    hasExtractedOutbound,\n  })\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type {\n  ApplicablePolicy,\n  GraphEdge,\n  GraphNode,\n  Policy,\n  PolicyAction,\n  PolicyFile,\n  PolicyRule,\n  PolicySeverity,\n  PolicyViolation,\n  ServiceNode,\n} from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  PolicyFileSchema,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT } from './graph.js'\nimport {\n  checkCompatibility,\n  checkDeprecatedApi,\n  checkNodeEngineConstraint,\n  checkPackageConflict,\n  compatPairs,\n  deprecatedApis,\n  nodeEngineConstraints,\n  packageConflicts,\n} from './compat.js'\nimport { emitNeatEvent } from './events.js'\nimport { getBlastRadius } from './traverse.js'\n\n// Policy evaluation engine (ADR-043). The entry point evaluateAllPolicies is\n// pure: same graph + same policies → same violations. Per-rule-type dispatch\n// via the policyEvaluators table. Adding a new rule type means one new\n// evaluator entry plus the schema entry in @neat.is/types/policy.ts.\n//\n// Deterministic violation ids per ADR-043: ${policy.id}:${context}. The\n// context is shape-specific (nodeId, edgeId, or composite). The\n// policy-violations.ndjson writer skips on duplicate ids.\n\nexport interface EvaluationContext {\n  // Wall-clock provider. Tests pin this; production uses Date.now.\n  now: () => number\n}\n\ninterface RuleEvaluatorArgs<T extends PolicyRule = PolicyRule> {\n  graph: NeatGraph\n  policy: Policy\n  rule: T\n  ctx: EvaluationContext\n}\n\ntype RuleEvaluator<T extends PolicyRule = PolicyRule> = (\n  args: RuleEvaluatorArgs<T>,\n) => PolicyViolation[]\n\n// Severity-driven default action per ADR-044.\nconst DEFAULT_ACTION_BY_SEVERITY: Record<PolicySeverity, PolicyAction> = {\n  info: 'log',\n  warning: 'alert',\n  error: 'alert',\n  critical: 'block',\n}\n\nexport function resolveOnViolation(policy: Policy): PolicyAction {\n  return policy.onViolation ?? DEFAULT_ACTION_BY_SEVERITY[policy.severity]\n}\n\nfunction makeViolation(\n  policy: Policy,\n  rule: PolicyRule,\n  contextSuffix: string,\n  message: string,\n  subject: PolicyViolation['subject'],\n  ctx: EvaluationContext,\n): PolicyViolation {\n  return {\n    id: `${policy.id}:${contextSuffix}`,\n    policyId: policy.id,\n    policyName: policy.name,\n    severity: policy.severity,\n    onViolation: resolveOnViolation(policy),\n    ruleType: rule.type,\n    subject,\n    message,\n    observedAt: new Date(ctx.now()).toISOString(),\n  }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Per-rule-type evaluators\n// ──────────────────────────────────────────────────────────────────────────\n\nconst evaluateStructural: RuleEvaluator<Extract<PolicyRule, { type: 'structural' }>> = ({\n  graph,\n  policy,\n  rule,\n  ctx,\n}) => {\n  const violations: PolicyViolation[] = []\n  graph.forEachNode((id, attrs) => {\n    const a = attrs as GraphNode\n    if (a.type !== rule.fromNodeType) return\n    let satisfied = false\n    for (const edgeId of graph.outboundEdges(id)) {\n      const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (e.type !== rule.edgeType) continue\n      const target = graph.getNodeAttributes(e.target) as GraphNode\n      // FrontierNodes are unresolved peers (ADR-068) — skip; the rule\n      // counts edges that resolve to a real typed node only.\n      if (target.type === NodeType.FrontierNode) continue\n      if (target.type === rule.toNodeType) {\n        satisfied = true\n        break\n      }\n    }\n    if (!satisfied) {\n      violations.push(\n        makeViolation(\n          policy,\n          rule,\n          id,\n          `${rule.fromNodeType} ${id} has no ${rule.edgeType} edge to a ${rule.toNodeType}`,\n          { nodeId: id },\n          ctx,\n        ),\n      )\n    }\n  })\n  return violations\n}\n\nconst evaluateOwnership: RuleEvaluator<Extract<PolicyRule, { type: 'ownership' }>> = ({\n  graph,\n  policy,\n  rule,\n  ctx,\n}) => {\n  const violations: PolicyViolation[] = []\n  graph.forEachNode((id, attrs) => {\n    const a = attrs as GraphNode & Record<string, unknown>\n    if (a.type !== rule.nodeType) return\n    const value = a[rule.field]\n    if (typeof value !== 'string' || value.length === 0) {\n      violations.push(\n        makeViolation(\n          policy,\n          rule,\n          id,\n          `${rule.nodeType} ${id} is missing required field \"${rule.field}\"`,\n          { nodeId: id },\n          ctx,\n        ),\n      )\n    }\n  })\n  return violations\n}\n\nconst evaluateProvenance: RuleEvaluator<Extract<PolicyRule, { type: 'provenance' }>> = ({\n  graph,\n  policy,\n  rule,\n  ctx,\n}) => {\n  const required = Array.isArray(rule.required) ? new Set(rule.required) : new Set([rule.required])\n  const violations: PolicyViolation[] = []\n  graph.forEachEdge((edgeId, attrs) => {\n    const e = attrs as GraphEdge\n    if (e.type !== rule.edgeType) return\n    if (rule.targetNodeId && e.target !== rule.targetNodeId) return\n    if (!required.has(e.provenance)) {\n      const requiredList = [...required].join(' | ')\n      violations.push(\n        makeViolation(\n          policy,\n          rule,\n          edgeId,\n          `${rule.edgeType} edge ${edgeId} has provenance ${e.provenance}; required ${requiredList}`,\n          { edgeId },\n          ctx,\n        ),\n      )\n    }\n  })\n  return violations\n}\n\nconst evaluateBlastRadius: RuleEvaluator<Extract<PolicyRule, { type: 'blast-radius' }>> = ({\n  graph,\n  policy,\n  rule,\n  ctx,\n}) => {\n  const violations: PolicyViolation[] = []\n  const depth = rule.depth\n  graph.forEachNode((id, attrs) => {\n    const a = attrs as GraphNode\n    if (a.type !== rule.nodeType) return\n    const result = depth !== undefined ? getBlastRadius(graph, id, depth) : getBlastRadius(graph, id)\n    if (result.totalAffected > rule.maxAffected) {\n      violations.push(\n        makeViolation(\n          policy,\n          rule,\n          id,\n          `${rule.nodeType} ${id} has blast radius ${result.totalAffected} > ${rule.maxAffected}`,\n          { nodeId: id, path: [id] },\n          ctx,\n        ),\n      )\n    }\n  })\n  return violations\n}\n\nconst evaluateCompatibility: RuleEvaluator<Extract<PolicyRule, { type: 'compatibility' }>> = ({\n  graph,\n  policy,\n  rule,\n  ctx,\n}) => {\n  const violations: PolicyViolation[] = []\n  // Iterate every ServiceNode and re-run the compat shapes the static\n  // extractor runs at extract time. Catches OBSERVED-vs-EXTRACTED divergence:\n  // a service whose dep manifest changed since the last extract gets re-flagged\n  // here on every evaluation cycle.\n  const wantsKind = (kind: NonNullable<typeof rule.kind>): boolean =>\n    rule.kind === undefined || rule.kind === kind\n\n  graph.forEachNode((svcId, attrs) => {\n    const a = attrs as GraphNode\n    if (a.type !== NodeType.ServiceNode) return\n    const svc = a as ServiceNode\n    const deps = svc.dependencies ?? {}\n\n    if (wantsKind('driver-engine')) {\n      // Walk every CONNECTS_TO edge from this service to a DatabaseNode,\n      // then run the driver-engine compat for each (driver, declared, engine,\n      // engineVersion) tuple.\n      for (const edgeId of graph.outboundEdges(svcId)) {\n        const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n        if (e.type !== EdgeType.CONNECTS_TO) continue\n        const dbAttrs = graph.getNodeAttributes(e.target) as GraphNode\n        // FrontierNodes are unresolved peers (ADR-068) — skip; compat\n        // checking needs a typed DatabaseNode.\n        if (dbAttrs.type === NodeType.FrontierNode) continue\n        if (dbAttrs.type !== NodeType.DatabaseNode) continue\n        const db = dbAttrs as { engine: string; engineVersion: string }\n        for (const pair of compatPairs()) {\n          if (pair.engine !== db.engine) continue\n          const declared = deps[pair.driver]\n          if (!declared) continue\n          const result = checkCompatibility(pair.driver, declared, db.engine, db.engineVersion)\n          if (!result.compatible && result.reason) {\n            violations.push(\n              makeViolation(\n                policy,\n                rule,\n                `${svcId}:driver-engine:${pair.driver}@${declared}:${db.engine}@${db.engineVersion}`,\n                result.reason,\n                { nodeId: svcId, edgeId },\n                ctx,\n              ),\n            )\n          }\n        }\n      }\n    }\n\n    if (wantsKind('node-engine')) {\n      const serviceNodeRange = svc.nodeEngine\n      for (const constraint of nodeEngineConstraints()) {\n        const declared = deps[constraint.package]\n        if (!declared) continue\n        const result = checkNodeEngineConstraint(constraint, declared, serviceNodeRange)\n        if (!result.compatible && result.reason) {\n          violations.push(\n            makeViolation(\n              policy,\n              rule,\n              `${svcId}:node-engine:${constraint.package}@${declared}`,\n              result.reason,\n              { nodeId: svcId },\n              ctx,\n            ),\n          )\n        }\n      }\n    }\n\n    if (wantsKind('package-conflict')) {\n      for (const conflict of packageConflicts()) {\n        const declared = deps[conflict.package]\n        if (!declared) continue\n        const requiredDeclared = deps[conflict.requires.name]\n        const result = checkPackageConflict(conflict, declared, requiredDeclared)\n        if (!result.compatible && result.reason) {\n          violations.push(\n            makeViolation(\n              policy,\n              rule,\n              `${svcId}:package-conflict:${conflict.package}@${declared}`,\n              result.reason,\n              { nodeId: svcId },\n              ctx,\n            ),\n          )\n        }\n      }\n    }\n\n    if (wantsKind('deprecated-api')) {\n      for (const dep of deprecatedApis()) {\n        const declared = deps[dep.package]\n        if (!declared) continue\n        const result = checkDeprecatedApi(dep, declared)\n        if (!result.compatible && result.reason) {\n          violations.push(\n            makeViolation(\n              policy,\n              rule,\n              `${svcId}:deprecated-api:${dep.package}@${declared}`,\n              result.reason,\n              { nodeId: svcId },\n              ctx,\n            ),\n          )\n        }\n      }\n    }\n  })\n\n  return violations\n}\n\nconst policyEvaluators: { [K in PolicyRule['type']]: RuleEvaluator<Extract<PolicyRule, { type: K }>> } = {\n  structural: evaluateStructural,\n  ownership: evaluateOwnership,\n  provenance: evaluateProvenance,\n  'blast-radius': evaluateBlastRadius,\n  compatibility: evaluateCompatibility,\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Public entry point\n// ──────────────────────────────────────────────────────────────────────────\n\n// Block-action gating for FrontierNode promotion (ADR-044 §block, MVP scope).\n// Runs the policy evaluator and returns the subset of block-action violations\n// that mention the candidate FrontierNode. Callers (ingest.ts\n// promoteFrontierNodes) check `allowed` before rewiring; when false, the\n// promotion is skipped and the violations surface through the standard\n// policy-violations.ndjson channel.\n//\n// Block scope is tightly bounded per the contract: FrontierNode promotion\n// only. Other gating points (deploy, codemod, OTel auto-create) need their\n// own ADRs before this function expands.\nexport function canPromoteFrontier(\n  graph: NeatGraph,\n  frontierId: string,\n  policies: Policy[],\n  ctx: EvaluationContext,\n): { allowed: boolean; violations: PolicyViolation[] } {\n  if (policies.length === 0) return { allowed: true, violations: [] }\n  const all = evaluateAllPolicies(graph, policies, ctx)\n  const blocking = all.filter((v) => {\n    if (v.onViolation !== 'block') return false\n    return (\n      v.subject.nodeId === frontierId ||\n      v.subject.path?.includes(frontierId) === true\n    )\n  })\n  return { allowed: blocking.length === 0, violations: blocking }\n}\n\nexport function evaluateAllPolicies(\n  graph: NeatGraph,\n  policies: Policy[],\n  ctx: EvaluationContext,\n): PolicyViolation[] {\n  const out: PolicyViolation[] = []\n  for (const policy of policies) {\n    const evaluator = policyEvaluators[policy.rule.type] as RuleEvaluator\n    const violations = evaluator({ graph, policy, rule: policy.rule, ctx })\n    for (const v of violations) out.push(v)\n  }\n  return out\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Soft guardrail — applicable-policy selection (ADR-108)\n// ──────────────────────────────────────────────────────────────────────────\n\n// The launch form of \"every agent stays inside the lines\": policies INFORM,\n// they never block. selectApplicablePolicies answers \"which policies govern the\n// node I'm about to edit?\" so the rules can ride into the agent's context.\n//\n// Matching is a subject/region match:\n//   - subject — the node's type is the rule's declared subject (the rule\n//     governs every node of that type).\n//   - region — for most rule types, the node sits one hop inside the rule's\n//     region: the target end of a structural edge, the database a compat rule\n//     reaches across a CONNECTS_TO, or a node sitting on an edge a provenance\n//     rule governs. For blast-radius rules the region is the subject's real\n//     blast radius — its dependents: we walk getBlastRadius (to the rule's\n//     declared depth) and surface the rule on every dependent it reaches, so a\n//     far-away node that breaks when the subject changes still sees the\n//     invariant that governs it.\n//\n// The general scope×distance injection over the policy overlay (ADR-105 §5),\n// which would carry this reachability behaviour to every rule type rather than\n// just blast-radius, is still unbuilt; the gap is written up in\n// docs/quirks/policies-soft-guardrail.md. selectApplicablePolicies never\n// evaluates a violation and never returns a verdict — surfacing a policy is not\n// gating it.\nexport function selectApplicablePolicies(\n  graph: NeatGraph,\n  policies: Policy[],\n  nodeId: string,\n): ApplicablePolicy[] {\n  // Without the node in the graph there's no type to match against, so we\n  // can't decide applicability. A not-yet-created node (a brand-new edit) is\n  // exactly the case the unbuilt overlay would handle; here we return empty\n  // rather than guess.\n  if (!graph.hasNode(nodeId)) return []\n  const node = graph.getNodeAttributes(nodeId) as GraphNode\n  const out: ApplicablePolicy[] = []\n  for (const policy of policies) {\n    const m = matchPolicyToNode(graph, policy, nodeId, node)\n    if (!m) continue\n    out.push({\n      policyId: policy.id,\n      policyName: policy.name,\n      ...(policy.description !== undefined ? { description: policy.description } : {}),\n      severity: policy.severity,\n      onViolation: resolveOnViolation(policy),\n      ruleType: policy.rule.type,\n      match: m.match,\n      reason: m.reason,\n    })\n  }\n  return out\n}\n\ninterface PolicyMatch {\n  match: ApplicablePolicy['match']\n  reason: string\n}\n\nfunction requiredProvenanceList(required: string | readonly string[]): string {\n  // required is ProvenanceRule['required'] — a single value or a non-empty\n  // array. Normalize to a readable \"A | B\" string.\n  if (Array.isArray(required)) return required.join(' | ')\n  return String(required)\n}\n\nfunction nodeTouchesEdgeType(\n  graph: NeatGraph,\n  nodeId: string,\n  edgeType: string,\n  requiredOtherEnd?: string,\n): boolean {\n  const incident = [...graph.outboundEdges(nodeId), ...graph.inboundEdges(nodeId)]\n  for (const edgeId of incident) {\n    const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n    if (e.type !== edgeType) continue\n    // A provenance rule with a targetNodeId only governs edges that touch that\n    // target. Without one, any edge of the type counts.\n    if (requiredOtherEnd === undefined) return true\n    if (e.source === requiredOtherEnd || e.target === requiredOtherEnd) return true\n  }\n  return false\n}\n\n// Reverse-reachability for blast-radius rules. Returns the id of a subject-type\n// node whose blast radius (its dependents, to the rule's declared depth)\n// contains nodeId, or null if none does. This is what lets a blast-radius rule\n// surface on the dependents it actually governs, not only on its subject.\nfunction blastRadiusSubjectReaching(\n  graph: NeatGraph,\n  rule: Extract<PolicyRule, { type: 'blast-radius' }>,\n  nodeId: string,\n): string | null {\n  let found: string | null = null\n  graph.forEachNode((subjId, attrs) => {\n    if (found !== null) return\n    if (subjId === nodeId) return\n    if ((attrs as GraphNode).type !== rule.nodeType) return\n    const radius =\n      rule.depth !== undefined\n        ? getBlastRadius(graph, subjId, rule.depth)\n        : getBlastRadius(graph, subjId)\n    if (radius.affectedNodes.some((n) => n.nodeId === nodeId)) found = subjId\n  })\n  return found\n}\n\nfunction matchPolicyToNode(\n  graph: NeatGraph,\n  policy: Policy,\n  nodeId: string,\n  node: GraphNode,\n): PolicyMatch | null {\n  const rule = policy.rule\n  switch (rule.type) {\n    case 'structural': {\n      if (node.type === rule.fromNodeType) {\n        return {\n          match: 'subject',\n          reason: `every ${rule.fromNodeType} must have a ${rule.edgeType} edge to a ${rule.toNodeType}`,\n        }\n      }\n      // The target end is one hop inside the rule's region — editing it can\n      // make a sibling fromNodeType pass or fail the rule.\n      if (node.type === rule.toNodeType) {\n        return {\n          match: 'region',\n          reason: `${rule.fromNodeType} nodes must reach a ${rule.toNodeType} like this one via a ${rule.edgeType} edge`,\n        }\n      }\n      return null\n    }\n    case 'ownership': {\n      if (node.type === rule.nodeType) {\n        return {\n          match: 'subject',\n          reason: `every ${rule.nodeType} must declare a non-empty \"${rule.field}\" field`,\n        }\n      }\n      return null\n    }\n    case 'blast-radius': {\n      const depthLabel = rule.depth !== undefined ? ` at depth ${rule.depth}` : ''\n      if (node.type === rule.nodeType) {\n        return {\n          match: 'subject',\n          reason: `no ${rule.nodeType} may exceed a blast radius of ${rule.maxAffected}${depthLabel}`,\n        }\n      }\n      // Dependent surfacing. The whole point of a blast-radius rule is \"this\n      // subject has too many things depending on it\" — so the agent who most\n      // needs the warning is the one editing one of those dependents, the nodes\n      // that break if the subject changes. We compute the subject's actual\n      // blast radius with getBlastRadius (to the rule's own declared depth) and\n      // surface the rule on any dependent inside it. This is the buildable slice\n      // of ADR-105 §5's scope×distance injection: a real reverse-reachability\n      // walk, not the general overlay, so a far-away node still surfaces the\n      // invariant.\n      const subject = blastRadiusSubjectReaching(graph, rule, nodeId)\n      if (subject) {\n        return {\n          match: 'region',\n          reason: `this node is in the blast radius of ${subject} (a ${rule.nodeType} held to a blast radius of ${rule.maxAffected}${depthLabel}) — it breaks if that ${rule.nodeType} changes`,\n        }\n      }\n      return null\n    }\n    case 'compatibility': {\n      const kindLabel = rule.kind ?? 'all compat shapes'\n      // The evaluator iterates every ServiceNode, so a ServiceNode is the\n      // direct subject.\n      if (node.type === NodeType.ServiceNode) {\n        return {\n          match: 'subject',\n          reason: `this service's dependencies are compatibility-checked (${kindLabel})`,\n        }\n      }\n      // A DatabaseNode one CONNECTS_TO hop from a service is in the region of\n      // the driver-engine shape — its engine version feeds that check.\n      const reachesDriverEngine = rule.kind === undefined || rule.kind === 'driver-engine'\n      if (\n        reachesDriverEngine &&\n        node.type === NodeType.DatabaseNode &&\n        nodeTouchesEdgeType(graph, nodeId, EdgeType.CONNECTS_TO)\n      ) {\n        return {\n          match: 'region',\n          reason: 'services connecting to this database have their driver/engine compatibility checked against it',\n        }\n      }\n      return null\n    }\n    case 'provenance': {\n      const requiredList = requiredProvenanceList(rule.required)\n      // The named target is the direct subject — every governed edge points at\n      // it.\n      if (rule.targetNodeId !== undefined && nodeId === rule.targetNodeId) {\n        return {\n          match: 'subject',\n          reason: `every ${rule.edgeType} edge into ${rule.targetNodeId} must carry ${requiredList} provenance`,\n        }\n      }\n      // Otherwise the node is in the region if it sits on a governed edge.\n      if (nodeTouchesEdgeType(graph, nodeId, rule.edgeType, rule.targetNodeId)) {\n        return {\n          match: 'region',\n          reason:\n            rule.targetNodeId !== undefined\n              ? `this node sits on a ${rule.edgeType} edge to ${rule.targetNodeId}, which must carry ${requiredList} provenance`\n              : `this node sits on a ${rule.edgeType} edge, which must carry ${requiredList} provenance`,\n        }\n      }\n      return null\n    }\n  }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Loader\n// ──────────────────────────────────────────────────────────────────────────\n\n// Reads <projectRoot>/policy.json. Returns [] when the file doesn't exist —\n// a project without policies is a perfectly fine state. Failures to parse\n// throw with the Zod error so the daemon surfaces malformed files loudly\n// instead of silently dropping rules.\nexport async function loadPolicyFile(policyPath: string): Promise<Policy[]> {\n  let raw: string\n  try {\n    raw = await fs.readFile(policyPath, 'utf8')\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n    throw err\n  }\n  const json = JSON.parse(raw) as unknown\n  const file: PolicyFile = PolicyFileSchema.parse(json)\n  return file.policies\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Append-only ndjson writer with id-based dedup\n// ──────────────────────────────────────────────────────────────────────────\n\n// Keeps an in-memory Set of seen violation ids so re-evaluation cycles don't\n// produce duplicate ndjson lines. The set hydrates from disk on first append\n// — startups that load an existing log don't lose dedup state.\nexport class PolicyViolationsLog {\n  private readonly path: string\n  private readonly project: string\n  private seen: Set<string> | null = null\n\n  constructor(logPath: string, project: string = DEFAULT_PROJECT) {\n    this.path = logPath\n    this.project = project\n  }\n\n  async append(v: PolicyViolation): Promise<boolean> {\n    if (!this.seen) await this.hydrate()\n    if (this.seen!.has(v.id)) return false\n    this.seen!.add(v.id)\n    await fs.mkdir(path.dirname(this.path), { recursive: true })\n    await fs.appendFile(this.path, JSON.stringify(v) + '\\n', 'utf8')\n    // Emit policy-violation only on first sighting (post-dedup) so SSE\n    // consumers don't see the same violation again on every evaluation\n    // cycle (ADR-051 #2).\n    emitNeatEvent({\n      type: 'policy-violation',\n      project: this.project,\n      payload: { violation: v },\n    })\n    return true\n  }\n\n  async readAll(): Promise<PolicyViolation[]> {\n    try {\n      const raw = await fs.readFile(this.path, 'utf8')\n      return raw\n        .split('\\n')\n        .filter(Boolean)\n        .map((line) => JSON.parse(line) as PolicyViolation)\n    } catch (err) {\n      if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n      throw err\n    }\n  }\n\n  private async hydrate(): Promise<void> {\n    this.seen = new Set()\n    const existing = await this.readAll()\n    for (const v of existing) this.seen.add(v.id)\n  }\n}\n","// Frontend-facing event bus (ADR-051). The single in-process EventEmitter\n// every producer (ingest / extract / watch / policy) emits through and the\n// only thing the SSE handler in streaming.ts subscribes to.\n//\n// Direct producer-to-handler coupling is a contract violation — every event\n// goes through `eventBus.emit('event', envelope)` so the SSE layer is the\n// only consumer that has to know the wire taxonomy.\n//\n// The taxonomy is locked at eight types (ADR-051 #2). Adding a ninth type\n// requires a successor ADR.\n\nimport { EventEmitter } from 'node:events'\nimport type { GraphEdge, GraphNode, PolicyViolation, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\n\n// Locked event taxonomy. Eight values. Tests assert the array shape\n// directly, so adding here without updating the contract is a regression.\nexport const NEAT_EVENT_TYPES = [\n  'node-added',\n  'node-updated',\n  'node-removed',\n  'edge-added',\n  'edge-removed',\n  'extraction-complete',\n  'policy-violation',\n  'stale-transition',\n] as const\n\nexport type NeatEventType = (typeof NEAT_EVENT_TYPES)[number]\n\n// Per-type payload shapes. The wire layer (SSE) writes the payload as the\n// `data` field; producers use `emit*` helpers below for type safety.\nexport interface NodeAddedPayload {\n  node: GraphNode\n}\nexport interface NodeUpdatedPayload {\n  id: string\n  changes: Partial<GraphNode>\n}\nexport interface NodeRemovedPayload {\n  id: string\n}\nexport interface EdgeAddedPayload {\n  edge: GraphEdge\n}\nexport interface EdgeRemovedPayload {\n  id: string\n}\nexport interface ExtractionCompletePayload {\n  project: string\n  fileCount: number\n  nodesAdded: number\n  edgesAdded: number\n}\nexport interface PolicyViolationPayload {\n  violation: PolicyViolation\n}\nexport interface StaleTransitionPayload {\n  edgeId: string\n  from: typeof Provenance.OBSERVED\n  to: typeof Provenance.STALE\n}\n\nexport type NeatEventPayload = {\n  'node-added': NodeAddedPayload\n  'node-updated': NodeUpdatedPayload\n  'node-removed': NodeRemovedPayload\n  'edge-added': EdgeAddedPayload\n  'edge-removed': EdgeRemovedPayload\n  'extraction-complete': ExtractionCompletePayload\n  'policy-violation': PolicyViolationPayload\n  'stale-transition': StaleTransitionPayload\n}\n\n// What the bus carries internally. Project is metadata used by the SSE\n// handler to route between /events and /projects/:project/events; it never\n// lands in the wire payload.\nexport interface NeatEventEnvelope<T extends NeatEventType = NeatEventType> {\n  type: T\n  project: string\n  payload: NeatEventPayload[T]\n}\n\n// Single event channel — listeners filter by `envelope.type` and\n// `envelope.project`. Using one channel keeps the contract surface narrow\n// and matches the SSE handler's needs (one subscription, fan out).\nexport const EVENT_BUS_CHANNEL = 'event'\n\nclass NeatEventBus extends EventEmitter {}\n\n// Singleton. Process-wide so producers in ingest / extract / watch / policy\n// can emit without threading a bus instance through every call site.\nexport const eventBus: NeatEventBus = new NeatEventBus()\n\n// EventEmitter defaults to 10 listeners; SSE clients add up quickly under a\n// browser refresh storm, so lift the cap.\neventBus.setMaxListeners(0)\n\nexport function emitNeatEvent<T extends NeatEventType>(envelope: NeatEventEnvelope<T>): void {\n  eventBus.emit(EVENT_BUS_CHANNEL, envelope)\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Graph subscription — one place to wire graphology mutation events into\n// the bus so producers don't have to instrument every addNode/addEdge.\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface AttachOptions {\n  project: string\n}\n\n// Subscribes to a NeatGraph and re-emits node/edge add/remove + node/edge\n// attribute updates as bus envelopes scoped to `project`. Returns a detach\n// fn that removes every listener it installed.\n//\n// Stale-transition is NOT routed through here — a provenance flip is just an\n// attribute update from graphology's view, and we'd lose the OBSERVED→STALE\n// semantic. ingest.ts emits stale-transition itself.\nexport function attachGraphToEventBus(graph: NeatGraph, opts: AttachOptions): () => void {\n  const { project } = opts\n\n  const onNodeAdded = (payload: { key: string; attributes: GraphNode }): void => {\n    emitNeatEvent({\n      type: 'node-added',\n      project,\n      payload: { node: payload.attributes },\n    })\n  }\n  const onNodeDropped = (payload: { key: string }): void => {\n    emitNeatEvent({\n      type: 'node-removed',\n      project,\n      payload: { id: payload.key },\n    })\n  }\n  const onEdgeAdded = (payload: { key: string; attributes: GraphEdge }): void => {\n    emitNeatEvent({\n      type: 'edge-added',\n      project,\n      payload: { edge: payload.attributes },\n    })\n  }\n  const onEdgeDropped = (payload: { key: string }): void => {\n    emitNeatEvent({\n      type: 'edge-removed',\n      project,\n      payload: { id: payload.key },\n    })\n  }\n  const onNodeAttrsUpdated = (payload: { key: string; attributes: GraphNode }): void => {\n    emitNeatEvent({\n      type: 'node-updated',\n      project,\n      payload: { id: payload.key, changes: payload.attributes },\n    })\n  }\n\n  graph.on('nodeAdded', onNodeAdded)\n  graph.on('nodeDropped', onNodeDropped)\n  graph.on('edgeAdded', onEdgeAdded)\n  graph.on('edgeDropped', onEdgeDropped)\n  graph.on('nodeAttributesUpdated', onNodeAttrsUpdated)\n\n  return () => {\n    graph.off('nodeAdded', onNodeAdded)\n    graph.off('nodeDropped', onNodeDropped)\n    graph.off('edgeAdded', onEdgeAdded)\n    graph.off('edgeDropped', onEdgeDropped)\n    graph.off('nodeAttributesUpdated', onNodeAttrsUpdated)\n  }\n}\n","export { extractFromDirectory, type ExtractResult } from './extract/index.js'\n","// Static-extraction pipeline. Phase order is load-bearing:\n//   services → aliases → databases (+ compat) → configs → routes → grpc → calls → infra → frontier promotion.\n//   Routes precede calls so the cross-service matcher (ADR-119) sees the full\n//   RouteNode table when it resolves client call sites to server routes. gRPC\n//   `.proto` extraction (ADR-123) mints the method nodes an OBSERVED span fuses onto.\n//\n// Contract anchors (see /docs/contracts.md):\n//   * Rule 1 — Every emitted edge carries Provenance.EXTRACTED from @neat.is/types.\n//   * Rule 2 — EXTRACTED edges use the plain `${type}:src->tgt` id pattern.\n//     Never write under the OBSERVED id pattern; that's ingest.ts's territory.\n//   * Rule 5 — Nodes/edges constructed against schemas in @neat.is/types; no\n//     local interface redefinitions in this tree.\n//   * Rule 8 — No demo-name hardcoding. Driver names come from package.json\n//     dependencies; engine names from compat.json via compatPairs().\n//   * Rule 14 — ConfigNodes record file existence only; never the contents.\nimport type { NeatGraph } from '../graph.js'\nimport { DEFAULT_PROJECT } from '../graph.js'\nimport { promoteFrontierNodes } from '../ingest.js'\nimport { ensureCompatLoaded } from '../compat.js'\nimport { emitNeatEvent } from '../events.js'\nimport { addServiceNodes, discoverServices } from './services.js'\nimport { addServiceAliases } from './aliases.js'\nimport { addFiles } from './files.js'\nimport { addImports } from './imports.js'\nimport { addDatabasesAndCompat } from './databases/index.js'\nimport { addConfigNodes } from './configs.js'\nimport { addRoutes } from './routes.js'\nimport { addGrpcMethods } from './proto.js'\nimport { addCallEdges } from './calls/index.js'\nimport { addInfra } from './infra/index.js'\nimport {\n  drainExtractionErrors,\n  writeExtractionErrors,\n  drainDroppedExtracted,\n  isRejectedLogEnabled,\n  writeRejectedExtracted,\n  type ExtractionError,\n  type DroppedExtractedEdge,\n} from './errors.js'\nimport path from 'node:path'\nimport { retireExtractedEdgesByMissingFile } from './retire.js'\n\nexport interface ExtractResult {\n  nodesAdded: number\n  edgesAdded: number\n  frontiersPromoted: number\n  // ADR-065 — per-file extraction failures collected during the pass.\n  // `extractionErrors` is the count; `errorEntries` is the drained list,\n  // available for callers that want to surface per-file context. Both are\n  // present on every pass (zero is observable as a positive signal).\n  extractionErrors: number\n  errorEntries: ExtractionError[]\n  // #140 — count of EXTRACTED edges retired this pass because their\n  // evidence.file no longer exists on disk. Zero on a clean pass; non-zero\n  // means the snapshot was carrying ghosts from deleted source.\n  ghostsRetired: number\n  // ADR-066 — count of EXTRACTED candidates dropped at emit time because\n  // their graded confidence fell below NEAT_EXTRACTED_PRECISION_FLOOR.\n  // Always reported (zero is observable). Detail entries surface in\n  // `droppedEntries` and route to rejected.ndjson when\n  // NEAT_EXTRACTED_REJECTED_LOG=1.\n  extractedDropped: number\n  droppedEntries: DroppedExtractedEdge[]\n}\n\nexport interface ExtractOptions {\n  // Post-extract policy trigger (ADR-043). Awaited after frontier promotion\n  // so policies see the final post-pass graph state. Daemons wire this to\n  // evaluateAllPolicies + PolicyViolationsLog.append.\n  onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n  // Project tag for the extraction-complete event (ADR-051). Defaults to\n  // DEFAULT_PROJECT when omitted.\n  project?: string\n  // ADR-065 — when set, drained extraction errors are appended to this\n  // path as ndjson with `source: 'extract'`. Daemons / `neat init` /\n  // `neat watch` wire this to `<projectDir>/neat-out/errors.ndjson`. When\n  // omitted, errors are still drained and returned in the result, just\n  // not persisted.\n  errorsPath?: string\n}\n\nexport async function extractFromDirectory(\n  graph: NeatGraph,\n  scanPath: string,\n  opts: ExtractOptions = {},\n): Promise<ExtractResult> {\n  await ensureCompatLoaded()\n  // Clear any stale entries from a prior pass (the producer-side sink is\n  // process-local). Per ADR-065, every pass collects its own errors; we drain\n  // again at the end to capture this pass's failures.\n  drainExtractionErrors()\n\n  const services = await discoverServices(scanPath)\n\n  const phase1Nodes = addServiceNodes(graph, services)\n  await addServiceAliases(graph, scanPath, services)\n  const fileEnum = await addFiles(graph, services)\n  const importGraph = await addImports(graph, services)\n  const phase2 = await addDatabasesAndCompat(graph, services, scanPath)\n  const phase3 = await addConfigNodes(graph, services, scanPath)\n  // Route extraction (ADR-119) runs before calls so the RouteNodes exist when\n  // addCallEdges' cross-service matcher (route-match.ts) looks them up.\n  const routePhase = await addRoutes(graph, services)\n  // gRPC `.proto` service/method extraction (ADR-123). Independent of the call\n  // phase — it reads the proto contract surface, not JS/TS call sites — and mints\n  // the same method nodes an OBSERVED gRPC span lands on, so declared and observed\n  // methods fuse.\n  const grpcPhase = await addGrpcMethods(graph, services)\n  const phase4 = await addCallEdges(graph, services)\n  const phase5 = await addInfra(graph, scanPath, services)\n  // #140 — drop EXTRACTED edges whose evidence.file no longer exists on disk.\n  // Catches the deleted-file ghost case for the full-pass entry point\n  // (init / daemon bootstrap). The edited-file case is handled per-mtime by\n  // watch.ts's `retireEdgesByFile`. Service dirs are passed alongside scanPath\n  // because CALLS-family producers store service-dir-relative paths while\n  // configs / databases / infra store scanPath-relative.\n  const ghostsRetired = retireExtractedEdgesByMissingFile(\n    graph,\n    scanPath,\n    services.map((s) => s.dir),\n  )\n  const frontiersPromoted = promoteFrontierNodes(graph)\n\n  // Post-extract policy trigger (ADR-043). Fires after frontier promotion so\n  // policies see the post-pass graph (including any FRONTIER → OBSERVED edge\n  // upgrades that just landed).\n  if (opts.onPolicyTrigger) await opts.onPolicyTrigger(graph)\n\n  // ADR-065 — drain the per-file extraction errors collected during the pass.\n  // If a sidecar path was supplied, append the entries; otherwise return them\n  // for the caller to surface.\n  const errorEntries = drainExtractionErrors()\n  if (opts.errorsPath && errorEntries.length > 0) {\n    try {\n      await writeExtractionErrors(errorEntries, opts.errorsPath)\n    } catch (err) {\n      console.warn(\n        `[neat] failed to write extraction errors to ${opts.errorsPath}: ${(err as Error).message}`,\n      )\n    }\n  }\n\n  // ADR-066 — drain the precision-floor drops. Always returned; only\n  // persisted when NEAT_EXTRACTED_REJECTED_LOG=1 (opt-in to keep the\n  // default sidecar surface quiet).\n  const droppedEntries = drainDroppedExtracted()\n  if (\n    isRejectedLogEnabled() &&\n    opts.errorsPath &&\n    droppedEntries.length > 0\n  ) {\n    // rejected.ndjson lives alongside errors.ndjson under neat-out/. Derive\n    // from errorsPath rather than re-plumbing a new option.\n    const rejectedPath = path.join(path.dirname(opts.errorsPath), 'rejected.ndjson')\n    try {\n      await writeRejectedExtracted(droppedEntries, rejectedPath)\n    } catch (err) {\n      console.warn(\n        `[neat] failed to write rejected extracted edges to ${rejectedPath}: ${(err as Error).message}`,\n      )\n    }\n  }\n\n  const result: ExtractResult = {\n    nodesAdded:\n      phase1Nodes +\n      fileEnum.nodesAdded +\n      importGraph.nodesAdded +\n      phase2.nodesAdded +\n      phase3.nodesAdded +\n      routePhase.nodesAdded +\n      grpcPhase.nodesAdded +\n      phase4.nodesAdded +\n      phase5.nodesAdded,\n    edgesAdded:\n      fileEnum.edgesAdded +\n      importGraph.edgesAdded +\n      phase2.edgesAdded +\n      phase3.edgesAdded +\n      routePhase.edgesAdded +\n      grpcPhase.edgesAdded +\n      phase4.edgesAdded +\n      phase5.edgesAdded,\n    frontiersPromoted,\n    extractionErrors: errorEntries.length,\n    errorEntries,\n    ghostsRetired,\n    extractedDropped: droppedEntries.length,\n    droppedEntries,\n  }\n\n  // extraction-complete (ADR-051). fileCount is the number of services\n  // discovered — the closest proxy we have for \"how much source did this\n  // pass touch\" without a per-phase file accountant.\n  emitNeatEvent({\n    type: 'extraction-complete',\n    project: opts.project ?? DEFAULT_PROJECT,\n    payload: {\n      project: opts.project ?? DEFAULT_PROJECT,\n      fileCount: services.length,\n      nodesAdded: result.nodesAdded,\n      edgesAdded: result.edgesAdded,\n    },\n  })\n\n  return result\n}\n","import { promises as fs, existsSync, readFileSync } from 'node:fs'\nimport path from 'node:path'\nimport * as sourceMapJs from 'source-map-js'\nimport type {\n  DatabaseNode,\n  ErrorEvent,\n  FileNode,\n  FrontierNode,\n  GraphEdge,\n  GraphNode,\n  GraphQLOperationNode,\n  GrpcMethodNode,\n  InfraNode,\n  Policy,\n  ServiceNode,\n  StaleEvent,\n  WebSocketChannelNode,\n} from '@neat.is/types'\nimport type { PersistedGraph } from './persist.js'\nimport type { EvaluationContext as PolicyEvaluationContext } from './policy.js'\nimport { canPromoteFrontier } from './policy.js'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForObservedSignal,\n  databaseId,\n  localDatabaseId,\n  extractedEdgeId,\n  fileId,\n  frontierId,\n  graphqlOperationId,\n  grpcMethodId,\n  inferredEdgeId,\n  infraId,\n  observedEdgeId,\n  serviceId,\n  websocketChannelId,\n  type EdgeTypeValue,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT } from './graph.js'\nimport type { ParsedSpan } from './otel.js'\nimport { emitNeatEvent } from './events.js'\n\n// Maps OTel spans to graph signal:\n//   * Cross-service span → upsert CALLS edge.\n//   * Database span (db.system attr present) → upsert CONNECTS_TO edge to a\n//     DatabaseNode resolved by host, or a service-scoped local DatabaseNode when\n//     the span carries no peer host (an in-process / embedded DB, ADR-118).\n//   * Span with status.code === 2 → ErrorEvent appended to errors.ndjson.\n//\n// Contract anchors (see /docs/contracts.md):\n//   * Rule 1 — Provenance: every edge here carries Provenance.X from @neat.is/types.\n//   * Rule 2 — Coexistence: OBSERVED edges live alongside EXTRACTED ones with a\n//     distinct id pattern (`${type}:OBSERVED:src->tgt`). Never write OBSERVED\n//     under the EXTRACTED id; that erases the gap NEAT exists to surface.\n//   * Rule 4 — Per-edge-type staleness (ADR-024): STALE_THRESHOLDS_BY_EDGE_TYPE\n//     governs decay; never hardcode a flat 24h threshold.\n//   * Rule 8 — No demo names: derive driver/engine identifiers from node\n//     properties, not literals.\n\nexport interface IngestContext {\n  graph: NeatGraph\n  errorsPath: string\n  // Absolute scan root the daemon is watching for this project. When set, a\n  // runtime `code.filepath` is made service-root-relative against it before the\n  // FileNode is keyed (file-awareness.md §4) — the service's absolute root is\n  // `scanPath/<repoPath>`, which recovers `dist/foo.js` even for a single-\n  // package service whose `repoPath` is empty (issue #430). Omitted by ad-hoc\n  // callers and most tests, which rely on the repoPath-segment anchor instead.\n  scanPath?: string\n  // Project name for event-bus routing (ADR-051). Defaults to DEFAULT_PROJECT\n  // when omitted — keeps single-project tests / scripts wire-compatible.\n  project?: string\n  now?: () => number\n  // Set to false when the receiver already wrote the ErrorEvent synchronously\n  // (production daemons via watch.ts wire this). When true or omitted, handleSpan\n  // appends the ErrorEvent itself — the path used by ad-hoc scripts and tests\n  // that don't go through buildOtelReceiver. ADR-033 §Error events.\n  writeErrorEventInline?: boolean\n  // Post-mutation policy trigger (ADR-043). Fires after handleSpan finishes\n  // and the queue is drained. Daemons wire this to evaluateAllPolicies +\n  // PolicyViolationsLog.append. Ad-hoc callers leave it undefined; their tests\n  // don't need policy side effects.\n  onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n  // 4xx-burst coalescing state, keyed by `${source}->${peer}` (issue #481).\n  // Lazily created the first time handleSpan sees a 4xx CLIENT/PRODUCER span.\n  // Carried on the context so each project/daemon keeps its own bursts and a\n  // long-lived handler accumulates across spans; ad-hoc callers reuse one ctx\n  // across a batch and get the same coalescing.\n  burstState?: Map<string, BurstState>\n}\n\nconst HOUR_MS = 60 * 60 * 1000\nconst DAY_MS = 24 * HOUR_MS\n\n// Per-edge-type stale thresholds. HTTP CALLS at 24h is meaningless because\n// healthy traffic recurs in seconds; infra DEPENDS_ON is the opposite — a\n// docker-compose service can sit idle overnight without anything being wrong.\n// Override via NEAT_STALE_THRESHOLDS (JSON, ms-per-edge-type).\nconst DEFAULT_STALE_THRESHOLDS: Record<string, number> = {\n  CALLS: HOUR_MS,\n  CONNECTS_TO: 4 * HOUR_MS,\n  PUBLISHES_TO: 4 * HOUR_MS,\n  CONSUMES_FROM: 4 * HOUR_MS,\n  DEPENDS_ON: DAY_MS,\n  CONFIGURED_BY: DAY_MS,\n  RUNS_ON: DAY_MS,\n}\n// Fallback for any edge type not in the map (forward compat — adding a new\n// EdgeType shouldn't break staleness sweeps).\nconst FALLBACK_STALE_THRESHOLD_MS = DAY_MS\n\nfunction loadStaleThresholdsFromEnv(): Record<string, number> {\n  const raw = process.env.NEAT_STALE_THRESHOLDS\n  if (!raw) return DEFAULT_STALE_THRESHOLDS\n  try {\n    const overrides = JSON.parse(raw) as Record<string, unknown>\n    const merged = { ...DEFAULT_STALE_THRESHOLDS }\n    for (const [k, v] of Object.entries(overrides)) {\n      if (typeof v === 'number' && Number.isFinite(v) && v >= 0) merged[k] = v\n    }\n    return merged\n  } catch (err) {\n    console.warn(\n      `[neat] NEAT_STALE_THRESHOLDS could not be parsed (${(err as Error).message}); using defaults`,\n    )\n    return DEFAULT_STALE_THRESHOLDS\n  }\n}\n\nexport function thresholdForEdgeType(\n  edgeType: string,\n  overrides?: Record<string, number>,\n): number {\n  const map = overrides ?? loadStaleThresholdsFromEnv()\n  return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS\n}\n\n// Failing-response incident tuning. A span that completes 5xx, carries an\n// ERROR status, or an exception event records an incident on its own — those\n// are unambiguous failures. A 4xx CLIENT/PRODUCER span doesn't: a single 404 is\n// often correct app behavior (auth probe, conditional fetch). 4xx becomes a\n// signal only when it repeats — N consecutive 4xx against the same (source,\n// peer) pair inside a window record ONE coalesced incident carrying the count\n// and the dominant status code, rather than N separate lines that would drown\n// the history. Mirrors the NEAT_STALE_THRESHOLDS override shape.\n//   threshold — how many consecutive 4xx against one peer trip the burst.\n//   windowMs  — the gap that ends a burst; a 4xx more than this after the\n//               previous one starts a fresh burst rather than extending it.\nconst DEFAULT_INCIDENT_THRESHOLDS = {\n  threshold: 5,\n  windowMs: 60_000,\n}\n\nfunction loadIncidentThresholdsFromEnv(): { threshold: number; windowMs: number } {\n  const raw = process.env.NEAT_INCIDENT_THRESHOLDS\n  if (!raw) return DEFAULT_INCIDENT_THRESHOLDS\n  try {\n    const overrides = JSON.parse(raw) as Record<string, unknown>\n    const merged = { ...DEFAULT_INCIDENT_THRESHOLDS }\n    if (\n      typeof overrides.threshold === 'number' &&\n      Number.isFinite(overrides.threshold) &&\n      overrides.threshold >= 1\n    ) {\n      merged.threshold = Math.floor(overrides.threshold)\n    }\n    if (\n      typeof overrides.windowMs === 'number' &&\n      Number.isFinite(overrides.windowMs) &&\n      overrides.windowMs >= 0\n    ) {\n      merged.windowMs = overrides.windowMs\n    }\n    return merged\n  } catch (err) {\n    console.warn(\n      `[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${(err as Error).message}); using defaults`,\n    )\n    return DEFAULT_INCIDENT_THRESHOLDS\n  }\n}\n\n// An attribute bag — either a live span's `attributes` or the passthrough set a\n// recorded ErrorEvent carries. The message helpers read from both, so the same\n// \"what failed here\" logic that names an incident at record time can re-derive\n// it at read time (dedupeIncidents).\ntype AttrBag = Record<string, unknown>\n\n// Read the HTTP response status off an attribute bag. OTel semconv renamed this\n// attribute — modern SDKs write `http.response.status_code`, older ones\n// `http.status_code`. Returns undefined when neither is present or parseable, so\n// a span with no response status is never misclassified as a failure.\nfunction httpResponseStatusFromAttrs(attrs: AttrBag): number | undefined {\n  for (const key of ['http.response.status_code', 'http.status_code']) {\n    const v = attrs[key]\n    if (typeof v === 'number' && Number.isFinite(v)) return v\n    if (typeof v === 'string') {\n      const n = Number(v)\n      if (Number.isFinite(n)) return n\n    }\n  }\n  return undefined\n}\n\nfunction httpResponseStatus(span: ParsedSpan): number | undefined {\n  return httpResponseStatusFromAttrs(span.attributes)\n}\n\n// A human incident line built from the HTTP context a server span carries even\n// when no exception event was recorded — an Express error handler that answers\n// 500 cleanly leaves `span.exception` empty but still carries the route and\n// status. \"500 on GET /users/:id\" reads better than the literal 'unknown\n// error'. Returns undefined when the bag has no usable HTTP context, so a\n// non-HTTP failure falls through to nonHttpFailureMessage / 'unknown error'.\n// Method/route follow the OTel semconv rename (modern `http.request.method` /\n// legacy `http.method`; `http.route` matched template, `http.target` / `url.path`\n// concrete-path fallback).\nfunction httpFailureMessageFromAttrs(attrs: AttrBag): string | undefined {\n  const status = httpResponseStatusFromAttrs(attrs)\n  const route = pickAttrFrom(attrs, 'http.route', 'http.target', 'url.path')\n  const method = pickAttrFrom(attrs, 'http.request.method', 'http.method')\n  const where = route ? `${method ? `${method} ` : ''}${route}` : undefined\n  if (status !== undefined && where) return `${status} on ${where}`\n  if (status !== undefined) return `HTTP ${status}`\n  if (where) return `error on ${where}`\n  return undefined\n}\n\n// Canonical gRPC status code → name (grpc/status.proto). Fixed protocol\n// constants shared by every gRPC implementation — not driver/engine data, so\n// they don't belong in compat.json (Rule 8 governs the latter, not a wire enum).\nconst GRPC_STATUS_NAMES: Record<number, string> = {\n  1: 'CANCELLED',\n  2: 'UNKNOWN',\n  3: 'INVALID_ARGUMENT',\n  4: 'DEADLINE_EXCEEDED',\n  5: 'NOT_FOUND',\n  6: 'ALREADY_EXISTS',\n  7: 'PERMISSION_DENIED',\n  8: 'RESOURCE_EXHAUSTED',\n  9: 'FAILED_PRECONDITION',\n  10: 'ABORTED',\n  11: 'OUT_OF_RANGE',\n  12: 'UNIMPLEMENTED',\n  13: 'INTERNAL',\n  14: 'UNAVAILABLE',\n  15: 'DATA_LOSS',\n  16: 'UNAUTHENTICATED',\n}\n\nfunction grpcStatusCodeFromAttrs(attrs: AttrBag): number | undefined {\n  const v = attrs['rpc.grpc.status_code']\n  if (typeof v === 'number' && Number.isFinite(v)) return v\n  if (typeof v === 'string') {\n    const n = Number(v)\n    if (Number.isFinite(n)) return n\n  }\n  return undefined\n}\n\n// A non-HTTP failure still carries its cause in span attributes — a non-OK gRPC\n// status, or a transport-level connection error (ECONNREFUSED reaching a peer).\n// Reading them keeps the incident from degrading to the literal 'unknown error'\n// when the span has no exception event and no HTTP response code. Returns\n// undefined for a span carrying neither, so the 'unknown error' floor still\n// applies to a genuinely opaque failure (issue #624).\nfunction nonHttpFailureMessageFromAttrs(attrs: AttrBag): string | undefined {\n  const grpc = grpcStatusCodeFromAttrs(attrs)\n  if (grpc !== undefined && grpc !== 0) {\n    const name = GRPC_STATUS_NAMES[grpc] ?? `status ${grpc}`\n    const detail = pickAttrFrom(attrs, 'rpc.grpc.status_message')\n    return detail ? `gRPC ${name}: ${detail}` : `gRPC ${name}`\n  }\n  // Transport/connection failure — OTel's `error.type` carries the errno\n  // (ECONNREFUSED, ETIMEDOUT, …) or the exception class for a call that never\n  // got a response. Skip the HTTP status-class forms (\"500\", \"_OTHER\") that\n  // http semconv also writes there; the HTTP path above owns those.\n  const errType = pickAttrFrom(attrs, 'error.type')\n  if (errType && errType !== '_OTHER' && !/^\\d+$/.test(errType)) {\n    const peer = pickAttrFrom(attrs, 'server.address', 'net.peer.name', 'net.host.name')\n    return peer ? `${errType} connecting to ${peer}` : errType\n  }\n  return undefined\n}\n\n// The incident's human message: the recorded exception first, then the HTTP\n// context a server span still carries, then a non-HTTP (gRPC / connection)\n// failure read from attributes, and only then the 'unknown error' floor. Shared\n// by every incident write path so the fallback chain can't drift between the\n// receiver's synchronous write and handleSpan's inline write.\nfunction incidentMessage(span: ParsedSpan): string {\n  return (\n    span.exception?.message ??\n    httpFailureMessageFromAttrs(span.attributes) ??\n    nonHttpFailureMessageFromAttrs(span.attributes) ??\n    'unknown error'\n  )\n}\n\n// In-flight 4xx burst against one (source, peer) pair. Lives on IngestContext so\n// it survives across spans without leaking into module state shared by every\n// project. firstTs/lastTs are the span timestamps (ADR-033 — span time, not\n// wall clock); codes counts each 4xx by status so the dominant one can be named\n// when the burst flushes.\ninterface BurstState {\n  count: number\n  firstTs: string\n  lastTs: string\n  lastMs: number\n  codes: Map<number, number>\n}\n\nfunction nowIso(ctx: IngestContext): string {\n  return new Date(ctx.now ? ctx.now() : Date.now()).toISOString()\n}\n\n// One-time-per-session-per-project warning for spans whose resource omits\n// `service.name`. The OTel spec requires SDKs to set it; customised exporters\n// occasionally don't. Routing the span to `service:unidentified` keeps\n// diagnostic visibility intact (silent drop hides a real SDK misconfiguration);\n// the warning gives an operator one line of stderr per project to act on.\n// See docs/contracts/otlp-routing.md §Fallback when `resource.service.name`\n// is missing.\nconst unidentifiedWarnedProjects = new Set<string>()\nfunction warnUnidentifiedSpan(project: string): void {\n  if (unidentifiedWarnedProjects.has(project)) return\n  unidentifiedWarnedProjects.add(project)\n  console.warn(\n    `[neatd] span lacked service.name; routed to 'unidentified' in project ${project}; check your OTel SDK config.`,\n  )\n}\n\n// Test seam — production code never calls this. Tests that exercise the\n// once-per-session contract reset between cases so each assertion sees a\n// fresh warned-set.\nexport function resetUnidentifiedSpanWarnings(): void {\n  unidentifiedWarnedProjects.clear()\n}\n\n// One-time-per-session-per-service audit for a compiled `dist/...js` call site\n// that carried no adjacent source map (file-awareness.md §4 + §6). Without a\n// map, ingest can't reconcile the observed dist file to the static `src/...ts`\n// the extractor parsed — the dist path is the honest answer, never a fabricated\n// src path. The leak this surfaces (issue #430) was hiding behind an absolute\n// path prefix; once the path is service-root-relative the mismatch is legible,\n// and this line tells the operator how to close it.\nconst noSourceMapWarnedServices = new Set<string>()\nfunction warnNoSourceMaps(serviceName: string): void {\n  if (noSourceMapWarnedServices.has(serviceName)) return\n  noSourceMapWarnedServices.add(serviceName)\n  console.warn(\n    `[neat] ${serviceName}: no .map files found under dist/; observed file edges will land on dist paths, not src. Set sourceMap: true in tsconfig to enable file-level reconciliation.`,\n  )\n}\n\n// Test seam — mirrors resetUnidentifiedSpanWarnings for the once-per-service\n// audit above.\nexport function resetNoSourceMapWarnings(): void {\n  noSourceMapWarnedServices.clear()\n}\n\nfunction pickAttrFrom(attrs: AttrBag, ...keys: string[]): string | undefined {\n  for (const k of keys) {\n    const v = attrs[k]\n    if (typeof v === 'string' && v.length > 0) return v\n  }\n  return undefined\n}\n\nfunction pickAttr(span: ParsedSpan, ...keys: string[]): string | undefined {\n  return pickAttrFrom(span.attributes, ...keys)\n}\n\nfunction hostFromUrl(u: string | undefined): string | undefined {\n  if (!u) return undefined\n  try {\n    return new URL(u).hostname\n  } catch {\n    return undefined\n  }\n}\n\n// OTel HTTP/db semconv has gone through several names for \"the host on the\n// other end of this call.\" Try the modern ones first, fall back to the legacy\n// ones, then last resort parse out of a full URL.\nfunction pickAddress(span: ParsedSpan): string | undefined {\n  return (\n    pickAttr(span, 'server.address', 'net.peer.name', 'net.host.name') ??\n    hostFromUrl(pickAttr(span, 'url.full', 'http.url'))\n  )\n}\n\n// A loopback peer address is this host talking to itself, never a distinct\n// upstream service. Cross-service correlation on the callee's SERVER span (the\n// parent-span fallback, ADR-033) recovers the real peer, so a loopback address\n// on a CLIENT span must not mint a standalone frontier:localhost /\n// frontier:127.0.0.1 that duplicates that resolved edge (issues #590, #577).\n// Scoped to the cross-service CALLS path — a loopback database is a real local\n// dependency and keeps its CONNECTS_TO edge.\nfunction isLoopbackHost(host: string): boolean {\n  const h = host.toLowerCase()\n  return (\n    h === 'localhost' ||\n    h === 'ip6-localhost' ||\n    h === '::1' ||\n    h === '[::1]' ||\n    /^127(?:\\.\\d{1,3}){3}$/.test(h)\n  )\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Call-site capture (file-awareness.md §4)\n//\n// The injected SpanProcessor sets `code.filepath` / `code.lineno` /\n// `code.function` on CLIENT/PRODUCER spans — the exact OTel attribute names,\n// written by the emit template (installers/templates.ts) and read here. The\n// two sites are cross-referenced so the names can't drift. When present, an\n// OBSERVED relationship originates from the file rather than the service.\n// SERVER spans and the callee side carry no call site and stay service-level;\n// evidence is never fabricated (§6).\nconst CODE_FILEPATH_ATTR = 'code.filepath'\nconst CODE_LINENO_ATTR = 'code.lineno'\nconst CODE_FUNCTION_ATTR = 'code.function'\n\nfunction toPosix(p: string): string {\n  return p.split('\\\\').join('/')\n}\n\nfunction languageForExt(relPath: string): string | undefined {\n  const dot = relPath.lastIndexOf('.')\n  if (dot === -1) return undefined\n  switch (relPath.slice(dot).toLowerCase()) {\n    case '.py':\n      return 'python'\n    case '.ts':\n    case '.tsx':\n      return 'typescript'\n    case '.js':\n    case '.jsx':\n    case '.mjs':\n    case '.cjs':\n      return 'javascript'\n    default:\n      return undefined\n  }\n}\n\n// Join the runtime `code.filepath` against the service root so the OBSERVED\n// relPath lines up with the EXTRACTED service-relative path (file-awareness.md\n// §4 + §7). ServiceNode.repoPath is the scanPath-relative package dir; its\n// segments appear inside the absolute runtime path, so anchoring on it recovers\n// the package-relative tail. With no usable anchor, the real runtime path is\n// returned in a relative-looking form — honest, even if it doesn't align with a\n// static src path. Never fabricated.\nfunction relPathForRuntimeFile(\n  filepath: string,\n  serviceNode?: ServiceNode,\n  scanPath?: string,\n): string | null {\n  let p = toPosix(filepath).replace(/^file:\\/\\//, '')\n  // When ingest knows the absolute scan root, the service's absolute root is\n  // `scanPath/<repoPath>`. Stripping it directly recovers the service-relative\n  // tail (`dist/foo.js`) even for a single-package service whose `repoPath` is\n  // empty — the segment anchor below has nothing to grab in that case, so the\n  // absolute path used to leak into the FileNode key (issue #430).\n  if (scanPath && scanPath.length > 0) {\n    const absRoot = toPosix(path.resolve(scanPath, serviceNode?.repoPath ?? ''))\n    const anchor = absRoot.endsWith('/') ? absRoot : `${absRoot}/`\n    if (p.startsWith(anchor)) return p.slice(anchor.length)\n  }\n  const root = serviceNode?.repoPath\n  if (root && root !== '.' && root.length > 0) {\n    const rootPosix = toPosix(root)\n    const anchor = `/${rootPosix}/`\n    const idx = p.lastIndexOf(anchor)\n    if (idx !== -1) return p.slice(idx + anchor.length)\n    const base = rootPosix.split('/').filter(Boolean).pop()\n    if (base) {\n      const baseAnchor = `/${base}/`\n      const bidx = p.lastIndexOf(baseAnchor)\n      if (bidx !== -1) return p.slice(bidx + baseAnchor.length)\n    }\n  }\n  p = p.replace(/^[A-Za-z]:/, '').replace(/^\\/+/, '')\n  return p.length > 0 ? p : null\n}\n\n// Exported so the connectors plane (packages/core/src/connectors/index.ts,\n// docs/contracts/connectors.md) can build one from a signal's own callSite —\n// a connector's file:line comes from the provider's telemetry rather than an\n// OTel span's code.* attributes, but reconciles onto the same EXTRACTED\n// FileNode through the same primitives below.\nexport interface CallSite {\n  relPath: string\n  line?: number\n  fn?: string\n  // The service-relative dist path the call site was captured on, when ingest\n  // resolved it through a source map to a different (source) `relPath`\n  // (file-awareness.md §4). Surfaces as FileNode.originalPath. Absent when the\n  // captured frame was already source-grained.\n  originalRelPath?: string\n}\n\n// dist→src source-map resolution (file-awareness.md §4). A runtime call site in\n// a compiled `dist/...js` is resolved through a disk-adjacent `.map` to the\n// original `src/...ts`, so an OBSERVED edge lands on the source file an agent\n// can open. Same-host only — when the daemon's filesystem doesn't carry the map\n// (a service that ran on a different machine) the dist frame is kept, honestly,\n// never fabricated (§6). Each dist file is read from disk once: a present map\n// caches its consumer, an absent one caches `null`. Synchronous reads keep\n// callSiteFromSpan synchronous; the cost is amortised across a file's spans.\nconst sourceMapCache = new Map<\n  string,\n  { consumer: sourceMapJs.SourceMapConsumer; dir: string } | null\n>()\n\ninterface ResolvedSrc {\n  filepath: string\n  line?: number\n}\n\nfunction resolveDistToSrc(absFilepath: string, line?: number): ResolvedSrc | null {\n  if (!absFilepath.endsWith('.js')) return null\n  let entry = sourceMapCache.get(absFilepath)\n  if (entry === undefined) {\n    entry = null\n    const mapPath = `${absFilepath}.map`\n    try {\n      if (existsSync(mapPath)) {\n        const raw = JSON.parse(readFileSync(mapPath, 'utf8')) as unknown\n        const consumer = new sourceMapJs.SourceMapConsumer(raw as never)\n        entry = { consumer, dir: path.dirname(mapPath) }\n      }\n    } catch {\n      entry = null\n    }\n    sourceMapCache.set(absFilepath, entry)\n  }\n  if (!entry) return null\n  try {\n    const pos = entry.consumer.originalPositionFor({\n      line: line !== undefined && Number.isFinite(line) ? line : 1,\n      column: 0,\n    })\n    if (!pos || !pos.source) return null\n    const root = entry.consumer.sourceRoot ?? ''\n    const resolved = path.resolve(entry.dir, root, pos.source)\n    return { filepath: resolved, ...(pos.line ? { line: pos.line } : {}) }\n  } catch {\n    return null\n  }\n}\n\n// Read the call-site attributes off a span. Returns null when the span carries\n// no `code.filepath` (SERVER spans, un-instrumented peers, callee side) so the\n// caller falls back to a service-level edge.\nfunction callSiteFromSpan(\n  span: ParsedSpan,\n  serviceNode?: ServiceNode,\n  scanPath?: string,\n): CallSite | null {\n  const filepath = span.attributes[CODE_FILEPATH_ATTR]\n  if (typeof filepath !== 'string' || filepath.length === 0) return null\n  const linenoRaw = span.attributes[CODE_LINENO_ATTR]\n  let line =\n    typeof linenoRaw === 'number' && Number.isFinite(linenoRaw) ? linenoRaw : undefined\n  // Resolve a compiled dist frame to its source before computing the service-\n  // relative path, so the FileNode lands on the original `src/...ts`.\n  const abs = toPosix(filepath).replace(/^file:\\/\\//, '')\n  const resolved = resolveDistToSrc(abs, line)\n  let effectivePath = filepath\n  let originalRelPath: string | undefined\n  if (resolved) {\n    originalRelPath = relPathForRuntimeFile(filepath, serviceNode, scanPath) ?? undefined\n    effectivePath = resolved.filepath\n    if (resolved.line !== undefined) line = resolved.line\n  }\n  const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath)\n  if (!relPath) return null\n  // A compiled `dist/...js` call site that didn't resolve through a map keeps\n  // the (honest) dist path. Surface the absence once per service so the\n  // operator can enable source maps and recover src-level reconciliation\n  // (file-awareness.md §4 + §6, issue #430).\n  if (!resolved && abs.endsWith('.js') && relPath.startsWith('dist/') && serviceNode?.name) {\n    warnNoSourceMaps(serviceNode.name)\n  }\n  const fnRaw = span.attributes[CODE_FUNCTION_ATTR]\n  const fn = typeof fnRaw === 'string' && fnRaw.length > 0 ? fnRaw : undefined\n  return {\n    relPath,\n    ...(line !== undefined ? { line } : {}),\n    ...(fn ? { fn } : {}),\n    ...(originalRelPath && originalRelPath !== relPath ? { originalRelPath } : {}),\n  }\n}\n\n// Reconcile a runtime-derived relPath onto the service-relative path the\n// extractor already minted, so OBSERVED and EXTRACTED FileNodes for the same\n// source file fuse into ONE node instead of two disjoint subgraphs\n// (file-awareness.md §4 — ingest joins the runtime path against the service\n// root to land the edge on a FileNode).\n//\n// relPathForRuntimeFile anchors the absolute `code.filepath` against scanPath /\n// repoPath. When that anchor can't be found — no scanPath wired, or the span\n// was emitted from a service whose absolute root differs from the daemon's\n// checkout (a container image rooted at `/app`, a relocated clone) — the\n// leftover relPath still carries the unanchored leading segments\n// (`app/src/foo.ts`, `Users/me/repo/src/foo.ts`) and forks a parallel FileNode\n// keyed off the absolute path. That splits the graph: the OBSERVED layer never\n// lands on the EXTRACTED `src/foo.ts` node, and divergence/traversal see two\n// half-graphs for one file.\n//\n// The extractor's FileNode paths are ground truth for which service-relative\n// paths exist. Recover the right one by matching the longest EXTRACTED (non-\n// OTel) FileNode path that is a trailing segment-suffix of the runtime relPath.\n// A match means the runtime path is the same file the extractor parsed, just\n// carrying extra leading directories the anchor couldn't strip — reuse the\n// extractor's path so both layers key the same node. No match means the file is\n// genuinely OTel-only; the honest runtime path stands (never fabricated, §6).\nexport function reconcileObservedRelPath(\n  graph: NeatGraph,\n  serviceName: string,\n  relPath: string,\n): string {\n  // Already lands on a known node (the anchor resolved cleanly, or a prior span\n  // created this node) — fused, nothing to recover.\n  if (graph.hasNode(fileId(serviceName, relPath))) return relPath\n  let best: string | null = null\n  graph.forEachNode((_id, attrs) => {\n    const a = attrs as FileNode & { type?: string }\n    if (a.type !== NodeType.FileNode || a.service !== serviceName) return\n    // Only fuse onto a statically-known file. An existing OTel-only node would\n    // already have matched the hasNode short-circuit above.\n    if (a.discoveredVia === 'otel') return\n    const p = a.path\n    if (!p) return\n    if ((relPath === p || relPath.endsWith(`/${p}`)) && (!best || p.length > best.length)) {\n      best = p\n    }\n  })\n  return best ?? relPath\n}\n\n// Ensure the FileNode for an observed call site and the owning service's\n// OBSERVED `CONTAINS` edge both exist, returning the FileNode id so the caller\n// can originate the relationship from it (file-awareness.md §1–2 + §4). The\n// CONTAINS edge carries no `lastObserved` — structural ownership doesn't go\n// STALE when traffic quiets (markStaleEdges skips edges without lastObserved),\n// and divergence detection skips CONTAINS so an OTel-only file node doesn't\n// surface as a missing-extracted finding.\nexport function ensureObservedFileNode(\n  graph: NeatGraph,\n  serviceName: string,\n  serviceNodeId: string,\n  callSite: CallSite,\n): string {\n  const relPath = reconcileObservedRelPath(graph, serviceName, callSite.relPath)\n  const fileNodeId = fileId(serviceName, relPath)\n  if (!graph.hasNode(fileNodeId)) {\n    const language = languageForExt(relPath)\n    const node: FileNode = {\n      id: fileNodeId,\n      type: NodeType.FileNode,\n      service: serviceName,\n      path: relPath,\n      ...(language ? { language } : {}),\n      ...(callSite.originalRelPath ? { originalPath: callSite.originalRelPath } : {}),\n      discoveredVia: 'otel',\n    }\n    graph.addNode(fileNodeId, node)\n  }\n  const containsId = makeObservedEdgeId(EdgeType.CONTAINS, serviceNodeId, fileNodeId)\n  if (!graph.hasEdge(containsId)) {\n    const edge: GraphEdge = {\n      id: containsId,\n      source: serviceNodeId,\n      target: fileNodeId,\n      type: EdgeType.CONTAINS,\n      provenance: Provenance.OBSERVED,\n    }\n    graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge)\n  }\n  return fileNodeId\n}\n\n// Edge id helpers live in @neat.is/types/identity.ts (ADR-029). The local\n// signatures below preserve the (type, source, target) argument order ingest.ts\n// has used historically while delegating to the canonical wire-format helpers.\nfunction makeObservedEdgeId(type: EdgeTypeValue, source: string, target: string): string {\n  return observedEdgeId(source, target, type)\n}\n\nfunction makeInferredEdgeId(type: EdgeTypeValue, source: string, target: string): string {\n  return inferredEdgeId(source, target, type)\n}\n\nconst INFERRED_CONFIDENCE = 0.6\nconst STITCH_MAX_DEPTH = 2\n\n// The trace stitcher only reasons about runtime *dependency* edges — the ones an\n// error actually propagates along (a service calling a service, connecting to a\n// datastore, a declared runtime dependency). Structural edges (CONTAINS a file,\n// IMPORTS a module, CONFIGURED_BY a ConfigNode, RUNS_ON a host) are static facts\n// learned by extraction; a 500 says nothing new about them. Minting an INFERRED\n// twin of a structural EXTRACTED edge would corrupt the trust signal — the twin\n// (conf 0.6) outranks the ground-truth EXTRACTED edge (0.85) under PROV_RANK, so\n// consumer queries would surface the inference in place of the hard fact\n// (docs/contracts/trace-stitcher.md — dependency-edge-type allowlist).\nconst STITCH_EDGE_TYPES = new Set<EdgeTypeValue>([\n  EdgeType.CALLS,\n  EdgeType.CONNECTS_TO,\n  EdgeType.DEPENDS_ON,\n])\n\n// OTLP-wire SpanKind values. The receiver decodes the raw wire integer onto\n// `ParsedSpan.kind` (otel.ts), and the wire enum is offset by one from the\n// `@opentelemetry/api` SpanKind the SDK uses in-process — UNSPECIFIED 0,\n// INTERNAL 1, SERVER 2, CLIENT 3, PRODUCER 4, CONSUMER 5. So we must NOT import\n// `@opentelemetry/api` here: its CLIENT is 2 (= wire SERVER) and PRODUCER is 3\n// (= wire CLIENT), which would gate the wrong kinds. Cross-referenced with the\n// wire fixtures in otel.test.ts (kind 2 = SERVER, kind 3 = CLIENT) and the\n// CLIENT call-site spans in ingest.test.ts (kind 3).\nconst WIRE_SPAN_KIND_CLIENT = 3\nconst WIRE_SPAN_KIND_PRODUCER = 4\nconst WIRE_SPAN_KIND_CONSUMER = 5\n\n// The caller-side gate for the CALLS / CONNECTS_TO paths. A CLIENT or PRODUCER\n// span is the caller/producer side of a service-to-service call or a datastore\n// read; INTERNAL / SERVER spans are not — a SERVER span is the callee, and its\n// edge is minted from its parent CLIENT via the parent-span fallback. Without\n// this gate every INTERNAL span that happens to carry a peer address — e.g. a\n// `tcp.connect` / `tls.connect` to an AWS endpoint — mints a spurious\n// service-level edge (issue #429), because no §4 capture layer stamps `code.*`\n// on INTERNAL spans.\n//\n// CONSUMER spans are handled by the messaging branch in handleSpan, not here:\n// a queue consumer isn't calling a service, it's reading a topic, so it mints a\n// CONSUMES_FROM edge to the destination node (spanMintsMessagingEdge below) —\n// the observed mirror of the static consumer→topic edge, the queue-side pair of\n// the PRODUCER→topic PUBLISHES_TO edge.\n//\n// A span that reports no kind (undefined) or UNSPECIFIED (0) carries no\n// caller/callee signal, so it falls back to the historical unconditional\n// behavior — hand-built and legacy producers keep minting. The leak this gates\n// is always an explicitly-kinded INTERNAL span.\nfunction spanMintsObservedEdge(kind: number | undefined): boolean {\n  if (kind === undefined || kind === 0) return true\n  return kind === WIRE_SPAN_KIND_CLIENT || kind === WIRE_SPAN_KIND_PRODUCER\n}\n\n// The messaging counterpart to the caller-side gate. A PRODUCER span publishes\n// to a destination; a CONSUMER span reads from one. Both sides mint an OBSERVED\n// edge to the topic/queue node — the PRODUCER a PUBLISHES_TO, the CONSUMER a\n// CONSUMES_FROM — so declared and observed queue topology fuse. Only spans that\n// actually carry a messaging destination reach this (handleSpan checks the\n// semconv attrs first), so a stray CONSUMER span with no destination stays inert.\nfunction spanMintsMessagingEdge(kind: number | undefined): boolean {\n  return kind === WIRE_SPAN_KIND_PRODUCER || kind === WIRE_SPAN_KIND_CONSUMER\n}\n\n// The GraphQL execution span is emitted by the service that RESOLVES the\n// operation — a SERVER or INTERNAL span (the instrumentation's `graphql.execute`),\n// never the CLIENT side that only ever sees an opaque `POST /graphql`. Gating out\n// the caller/producer/consumer kinds keeps a client-side operation span from\n// attributing the operation to the caller (client-side operation attribution is\n// deferred, ADR-122); an unkinded / UNSET span still mints, mirroring the\n// caller-side gate's legacy-producer fallback.\nfunction spanServesGraphqlOperation(kind: number | undefined): boolean {\n  return (\n    kind !== WIRE_SPAN_KIND_CLIENT &&\n    kind !== WIRE_SPAN_KIND_PRODUCER &&\n    kind !== WIRE_SPAN_KIND_CONSUMER\n  )\n}\n\n// A GraphQL operation node keyed on (service, operationType, operationName) via\n// graphqlOperationId (ADR-122). Minted observed-first from the execution span so\n// operation-level topology is legible even before any static GraphQL extractor\n// exists; a later static extractor fuses onto the same id. `operationType` is\n// stored lower-cased to match the id's normalisation, so a `query` observed and a\n// `Query` static resolver land on one node. Idempotent — a high-volume operation\n// upserts, never grows the node set.\nfunction ensureGraphqlOperationNode(\n  graph: NeatGraph,\n  serviceName: string,\n  operationType: string,\n  operationName: string,\n): string {\n  const id = graphqlOperationId(serviceName, operationType, operationName)\n  if (graph.hasNode(id)) return id\n  const node: GraphQLOperationNode = {\n    id,\n    type: NodeType.GraphQLOperationNode,\n    name: operationName,\n    service: serviceName,\n    operationType: operationType.toLowerCase(),\n    operationName,\n    discoveredVia: 'otel',\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// The gRPC execution span carrying `rpc.service` / `rpc.method` is emitted on\n// both sides of a call — the SERVER that resolves the method and the CLIENT that\n// invokes it. Only the serving side owns the method (ADR-123): gating out the\n// caller/producer/consumer kinds keeps a CLIENT gRPC span from attributing\n// ownership to the caller (that client span still falls through to the\n// cross-service resolver below, so the caller→callee edge is unaffected).\n// Client→method attribution is deferred. An unkinded / UNSET span still mints,\n// mirroring the graphql and caller-side gates' legacy fallbacks.\nfunction spanServesGrpcMethod(kind: number | undefined): boolean {\n  return (\n    kind !== WIRE_SPAN_KIND_CLIENT &&\n    kind !== WIRE_SPAN_KIND_PRODUCER &&\n    kind !== WIRE_SPAN_KIND_CONSUMER\n  )\n}\n\n// A gRPC method node keyed on (rpcService, rpcMethod) via grpcMethodId (ADR-123).\n// `rpcService` is the fully-qualified `rpc.service` the wire carries verbatim\n// (`orders.OrderService`), so an OBSERVED span and a static `.proto` definition\n// fuse onto one node rather than twinning. Idempotent — a high-volume method\n// upserts, never grows the node set.\nfunction ensureGrpcMethodNode(\n  graph: NeatGraph,\n  rpcService: string,\n  rpcMethod: string,\n): string {\n  const id = grpcMethodId(rpcService, rpcMethod)\n  if (graph.hasNode(id)) return id\n  const node: GrpcMethodNode = {\n    id,\n    type: NodeType.GrpcMethodNode,\n    name: `${rpcService}/${rpcMethod}`,\n    rpcService,\n    rpcMethod,\n    discoveredVia: 'otel',\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// The HTTP upgrade span that opens a WebSocket is emitted by the service that\n// SERVES the channel — a SERVER span (the framework's inbound `GET`), or an\n// unkinded / INTERNAL span from a hand-built handshake. The CLIENT that dials the\n// socket carries the same upgrade header, but the channel belongs to the server\n// (ADR-125), so gating out the caller/producer/consumer kinds keeps a client-side\n// upgrade span from attributing the channel to the caller. An unkinded / UNSET\n// span still mints, mirroring the graphql and gRPC serving-side gates.\nfunction spanServesWebsocketChannel(kind: number | undefined): boolean {\n  return (\n    kind !== WIRE_SPAN_KIND_CLIENT &&\n    kind !== WIRE_SPAN_KIND_PRODUCER &&\n    kind !== WIRE_SPAN_KIND_CONSUMER\n  )\n}\n\n// A WebSocket channel node keyed on (service, channel) via websocketChannelId\n// (ADR-125). Minted OBSERVED-only from the upgrade span — a WebSocket channel is\n// known from observation, never from static extraction, so there is no declared\n// twin to fuse with and no static producer to fill in `path` / `line` (those stay\n// absent, never fabricated — file-awareness.md §6). Idempotent — every reconnect\n// on the same channel upserts, never grows the node set.\nfunction ensureWebsocketChannelNode(\n  graph: NeatGraph,\n  serviceName: string,\n  channel: string,\n): string {\n  const id = websocketChannelId(serviceName, channel)\n  if (graph.hasNode(id)) return id\n  const node: WebSocketChannelNode = {\n    id,\n    type: NodeType.WebSocketChannelNode,\n    name: channel,\n    service: serviceName,\n    channel,\n    discoveredVia: 'otel',\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// A messaging destination node (Kafka topic, queue, stream) keyed exactly the\n// way the static extractor keys it, so the OBSERVED and EXTRACTED edges fuse\n// onto one node instead of twinning. The Kafka static side names its topic\n// `infra:kafka-topic:<topic>` (extract/calls/kafka.ts), so the kind is\n// `<messaging.system>-topic` — `kafka` → `kafka-topic` — and the same shape\n// generalises to every messaging system the semconv names. `provider: 'self'`\n// mirrors the static extractor's non-AWS provider so an observed-first node\n// merges cleanly when static analysis later reaches the same destination.\nfunction messagingDestinationKind(system: string): string {\n  return `${system}-topic`\n}\n\nfunction ensureMessagingDestinationNode(\n  graph: NeatGraph,\n  system: string,\n  destination: string,\n): string {\n  const id = infraId(messagingDestinationKind(system), destination)\n  if (graph.hasNode(id)) return id\n  const node: InfraNode = {\n    id,\n    type: NodeType.InfraNode,\n    name: destination,\n    provider: 'self',\n    kind: messagingDestinationKind(system),\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// Parent-span TTL cache (ADR-033). Address-based peer resolution (server.address /\n// net.peer.name / url.full) misses non-HTTP RPCs and any span with an opaque\n// peer. The cache stores each span's service keyed by `${traceId}:${spanId}` so\n// a child span whose address resolution fails can fall back to its parent's\n// service, identifying a cross-service CALLS edge from parent → current.\n//\n// Bounded size + TTL — out-of-order arrival (child before parent) drops the\n// child rather than buffering. We accept that loss because the cache is best-\n// effort: for every cross-service call, the CLIENT span on the caller side\n// covers the same edge via address-based resolution, so missing one direction\n// is recoverable.\nconst PARENT_SPAN_CACHE_SIZE = 10_000\nconst PARENT_SPAN_CACHE_TTL_MS = 5 * 60 * 1000\n\ninterface ParentSpanCacheEntry {\n  service: string\n  // Env discriminator from the parent span (ADR-074 §2). The parent-span\n  // fallback in handleSpan uses this so the auto-created parent ServiceNode\n  // lands on the same env-tagged id the OTel emitter advertised.\n  env: string\n  // The parent span's own `code.*` call site, when its SpanProcessor captured\n  // one (file-awareness.md §4). The parent-span fallback below originates its\n  // edge from the parent's FileNode instead of the bare parent ServiceNode when\n  // this is present, so the fallback edge anchors to file:line rather than\n  // pinning to a service node (issue #536). Undefined when the parent carried no\n  // call site — never fabricated (§6), so the service-level fallback stands.\n  callSite?: CallSite\n  expiresAt: number\n}\n\nconst parentSpanCache = new Map<string, ParentSpanCacheEntry>()\n\nfunction parentSpanKey(traceId: string, spanId: string): string {\n  return `${traceId}:${spanId}`\n}\n\nfunction cacheSpanService(span: ParsedSpan, now: number, callSite: CallSite | null): void {\n  if (!span.traceId || !span.spanId) return\n  const key = parentSpanKey(span.traceId, span.spanId)\n  // Map preserves insertion order, so deleting + re-inserting bumps an entry to\n  // the back. Eviction is \"drop oldest\" once size exceeds the cap.\n  parentSpanCache.delete(key)\n  parentSpanCache.set(key, {\n    service: span.service,\n    env: span.env ?? 'unknown',\n    ...(callSite ? { callSite } : {}),\n    expiresAt: now + PARENT_SPAN_CACHE_TTL_MS,\n  })\n  while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {\n    const oldest = parentSpanCache.keys().next().value\n    if (!oldest) break\n    parentSpanCache.delete(oldest)\n  }\n}\n\nfunction lookupParentSpan(\n  traceId: string,\n  parentSpanId: string,\n  now: number,\n): { service: string; env: string; callSite?: CallSite } | null {\n  const entry = parentSpanCache.get(parentSpanKey(traceId, parentSpanId))\n  if (!entry) return null\n  if (entry.expiresAt <= now) {\n    parentSpanCache.delete(parentSpanKey(traceId, parentSpanId))\n    return null\n  }\n  return {\n    service: entry.service,\n    env: entry.env,\n    ...(entry.callSite ? { callSite: entry.callSite } : {}),\n  }\n}\n\n// Test seam: lets unit tests start from a clean slate.\nexport function resetParentSpanCache(): void {\n  parentSpanCache.clear()\n}\n\n// Peer host → ServiceNode id resolution. With env-dimension (ADR-074 §2),\n// the same `name` may live across multiple ServiceNodes — one per env, plus\n// the env-less form from static extraction. When `env` is known (the source\n// span's env), prefer a same-env match; fall back to the env-less node so\n// EXTRACTED edges from static analysis remain reachable until OBSERVED\n// traffic from the same env promotes them.\n//\n// Match passes:\n//   1. Exact id lookup for `(host, env)` — `serviceId(host, env)`.\n//   2. Exact id lookup for env-less `serviceId(host)`.\n//   3. Name/alias scan across every ServiceNode, preferring same-env then\n//      env-less then any other env.\nfunction resolveServiceId(\n  graph: NeatGraph,\n  host: string,\n  env: string,\n): string | null {\n  const envTagged = serviceId(host, env)\n  if (graph.hasNode(envTagged)) return envTagged\n  const envLess = serviceId(host)\n  if (envLess !== envTagged && graph.hasNode(envLess)) return envLess\n\n  let sameEnv: string | null = null\n  let envLessMatch: string | null = null\n  let anyMatch: string | null = null\n  graph.forEachNode((id, attrs) => {\n    if (sameEnv) return\n    const a = attrs as ServiceNode & { type?: string }\n    if (a.type !== NodeType.ServiceNode) return\n    const matchesByName = a.name === host\n    const matchesByAlias = a.aliases ? a.aliases.includes(host) : false\n    if (!matchesByName && !matchesByAlias) return\n    const nodeEnv = a.env ?? 'unknown'\n    if (nodeEnv === env) {\n      sameEnv = id\n      return\n    }\n    if (nodeEnv === 'unknown' && !envLessMatch) envLessMatch = id\n    else if (!anyMatch) anyMatch = id\n  })\n  return sameEnv ?? envLessMatch ?? anyMatch\n}\n\nexport function frontierIdFor(host: string): string {\n  return frontierId(host)\n}\n\n// Auto-create a minimal ServiceNode for span.service when no such node exists.\n// Used at the top of handleSpan so subsequent edge upserts always have endpoints\n// — without it, OBSERVED edges silently drop for any service the static\n// extractor hasn't reached yet (and never reaches at all in OTel-only setups).\n// `language: 'unknown'` is the contract's specified placeholder (ADR-033). When\n// static extraction later produces a ServiceNode at the same id, addServiceNodes\n// merges and flips discoveredVia to 'merged' rather than overwriting.\n// Exported for the connectors plane (connectors/index.ts) — a connector\n// signal needs the same auto-vivified ServiceNode any OTel span gets before\n// an edge upsert, since a connector-sourced service may never have been\n// statically extracted or seen a span yet.\nexport function ensureServiceNode(\n  graph: NeatGraph,\n  serviceName: string,\n  env: string,\n): string {\n  const id = serviceId(serviceName, env)\n  if (graph.hasNode(id)) return id\n  const node: ServiceNode = {\n    id,\n    type: NodeType.ServiceNode,\n    name: serviceName,\n    language: 'unknown',\n    discoveredVia: 'otel',\n    ...(env !== 'unknown' ? { env } : {}),\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// Same shape for unseen db.system + host pairs. Engine comes off the OTel\n// attribute as a string per Rule 8 — no hardcoded engine list. compatibleDrivers\n// is empty until static extraction merges in the matrix-derived drivers.\nfunction ensureDatabaseNode(graph: NeatGraph, host: string, engine: string): string {\n  const id = databaseId(host)\n  if (graph.hasNode(id)) return id\n  const node: DatabaseNode = {\n    id,\n    type: NodeType.DatabaseNode,\n    name: host,\n    engine,\n    engineVersion: 'unknown',\n    compatibleDrivers: [],\n    host,\n    discoveredVia: 'otel',\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// An in-process / embedded database (SQLite, better-sqlite3, an in-memory\n// store) crosses no network boundary, so its span carries no peer host to key a\n// DatabaseNode on. Key it on a service-scoped local identity instead so two\n// services each reading their own `app.db` stay distinct nodes rather than\n// collapsing onto one (ADR-118). `name` is the logical database from the span\n// (db.name) when present, the engine string otherwise. `host` is intentionally\n// omitted — an embedded database has no network host, and evidence is never\n// fabricated (file-awareness.md §6); host-mismatch divergence skips a hostless\n// DatabaseNode. Returns the node id so the caller can point the CONNECTS_TO edge\n// at it. Idempotent — high-volume DB spans upsert, never grow the node set.\nfunction ensureLocalDatabaseNode(\n  graph: NeatGraph,\n  serviceName: string,\n  name: string,\n  engine: string,\n): string {\n  const id = localDatabaseId(serviceName, name)\n  if (graph.hasNode(id)) return id\n  const node: DatabaseNode = {\n    id,\n    type: NodeType.DatabaseNode,\n    name,\n    engine,\n    engineVersion: 'unknown',\n    compatibleDrivers: [],\n    discoveredVia: 'otel',\n  }\n  graph.addNode(id, node)\n  return id\n}\n\nfunction ensureFrontierNode(graph: NeatGraph, host: string, ts: string): string {\n  const id = frontierIdFor(host)\n  if (graph.hasNode(id)) {\n    const existing = graph.getNodeAttributes(id) as FrontierNode\n    graph.replaceNodeAttributes(id, { ...existing, lastObserved: ts })\n    return id\n  }\n  const node: FrontierNode = {\n    id,\n    type: NodeType.FrontierNode,\n    name: host,\n    host,\n    firstObserved: ts,\n    lastObserved: ts,\n  }\n  graph.addNode(id, node)\n  return id\n}\n\n// Exported so the connectors plane (connectors/index.ts) mints edges through\n// the identical primitive OTel ingest uses — a connector-sourced edge and a\n// span-sourced edge are meant to be indistinguishable to traversal,\n// divergence, and staleness (docs/connectors/README.md).\nexport interface UpsertResult {\n  edge: GraphEdge\n  created: boolean\n}\n\nexport function upsertObservedEdge(\n  graph: NeatGraph,\n  type: EdgeTypeValue,\n  source: string,\n  target: string,\n  ts: string,\n  isError = false,\n  evidence?: { file: string; line?: number },\n): UpsertResult | null {\n  if (!graph.hasNode(source) || !graph.hasNode(target)) return null\n\n  const id = makeObservedEdgeId(type, source, target)\n  if (graph.hasEdge(id)) {\n    const existing = graph.getEdgeAttributes(id) as GraphEdge\n    const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1\n    const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0)\n    const newSignal = {\n      spanCount: newSpanCount,\n      errorCount: newErrorCount,\n      lastObservedAgeMs: 0,\n    }\n    // ADR-066 §2 — confidence grades from the signal block. PROV_RANK stays;\n    // the grade reflects volume + recency + error ratio within the OBSERVED\n    // tier.\n    const updated: GraphEdge = {\n      ...existing,\n      provenance: Provenance.OBSERVED,\n      lastObserved: ts,\n      callCount: newSpanCount,\n      signal: newSignal,\n      confidence: confidenceForObservedSignal(newSignal),\n    }\n    graph.replaceEdgeAttributes(id, updated)\n    return { edge: updated, created: false }\n  }\n\n  const signal = {\n    spanCount: 1,\n    errorCount: isError ? 1 : 0,\n    lastObservedAgeMs: 0,\n  }\n  const edge: GraphEdge = {\n    id,\n    source,\n    target,\n    type,\n    provenance: Provenance.OBSERVED,\n    confidence: confidenceForObservedSignal(signal),\n    lastObserved: ts,\n    callCount: 1,\n    signal,\n    // Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).\n    // Only set when code.filepath was present on the span — never fabricated.\n    ...(evidence ? { evidence } : {}),\n  }\n  graph.addEdgeWithKey(id, source, target, edge)\n  return { edge, created: true }\n}\n\n// When a span errors, the system is exercising its dependencies right now even\n// if some of them aren't auto-instrumented (pg 7.4.0 in the demo, see ADR-014).\n// Walk EXTRACTED edges out from the erroring service for a couple of hops and\n// promote them to INFERRED twins so traversal can prefer them over the bare\n// static edges without claiming OBSERVED-grade certainty.\nfunction stitchTrace(graph: NeatGraph, sourceServiceId: string, ts: string): void {\n  if (!graph.hasNode(sourceServiceId)) return\n\n  const visited = new Set<string>([sourceServiceId])\n  const queue: { nodeId: string; depth: number }[] = [{ nodeId: sourceServiceId, depth: 0 }]\n\n  while (queue.length > 0) {\n    const { nodeId, depth } = queue.shift()!\n    if (depth >= STITCH_MAX_DEPTH) continue\n\n    const outbound = graph.outboundEdges(nodeId)\n    for (const edgeId of outbound) {\n      const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n      if (edge.provenance !== Provenance.EXTRACTED) continue\n\n      // Only runtime dependency edges get stitched. Structural edges (CONTAINS /\n      // IMPORTS / CONFIGURED_BY / RUNS_ON) are never mirrored into INFERRED twins\n      // and the BFS does not recurse through them — an error propagates along\n      // dependencies, not static containment (trace-stitcher.md allowlist).\n      if (!STITCH_EDGE_TYPES.has(edge.type)) continue\n\n      // OBSERVED twin already covers this hop with ground truth — no inference\n      // needed (ADR-034). Stomping it with INFERRED erases the gap NEAT exists\n      // to surface; skipping it keeps the OBSERVED edge as the authoritative\n      // record and avoids cluttering the graph with a redundant INFERRED twin.\n      if (graph.hasEdge(observedEdgeId(edge.source, edge.target, edge.type))) continue\n\n      upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts)\n\n      if (!visited.has(edge.target)) {\n        visited.add(edge.target)\n        queue.push({ nodeId: edge.target, depth: depth + 1 })\n      }\n    }\n  }\n}\n\nfunction upsertInferredEdge(\n  graph: NeatGraph,\n  type: EdgeTypeValue,\n  source: string,\n  target: string,\n  ts: string,\n): void {\n  const id = makeInferredEdgeId(type, source, target)\n  if (graph.hasEdge(id)) {\n    const existing = graph.getEdgeAttributes(id) as GraphEdge\n    const updated: GraphEdge = { ...existing, lastObserved: ts }\n    graph.replaceEdgeAttributes(id, updated)\n    return\n  }\n\n  const edge: GraphEdge = {\n    id,\n    source,\n    target,\n    type,\n    provenance: Provenance.INFERRED,\n    confidence: INFERRED_CONFIDENCE,\n    lastObserved: ts,\n  }\n  graph.addEdgeWithKey(id, source, target, edge)\n}\n\nasync function appendErrorEvent(ctx: IngestContext, ev: ErrorEvent): Promise<void> {\n  await fs.mkdir(path.dirname(ctx.errorsPath), { recursive: true })\n  await fs.appendFile(ctx.errorsPath, JSON.stringify(ev) + '\\n', 'utf8')\n}\n\n// Resolve the incident's affectedNode. When the span carries a `code.filepath`\n// call site, the incident attributes to the FileNode the failure surfaced in —\n// the same file grain OBSERVED CALLS edges land on (file-awareness.md §4) —\n// resolving a compiled `dist/...js` frame through its disk-adjacent source map\n// when one is present. Without a call site it stays at the originating service,\n// the honest fallback (§2).\n//\n// The runtime `code.filepath` is a deploy-absolute path (`/var/task/...` on\n// Lambda, `/app/...` in a container image) that need not match the daemon's\n// checkout. When the graph is available it's reconciled onto the service-\n// relative path the extractor already minted (reconcileObservedRelPath, the\n// same trailing-suffix match the OBSERVED edge origin uses), so the incident\n// lands on the ONE fused FileNode instead of a phantom keyed off the absolute\n// path — the node root-cause actually walks. Without a graph the honest runtime\n// path stands: the file node may not be materialised yet, but querying the\n// service still surfaces the incident, and the file:line is real (§6 — never\n// fabricated).\nfunction incidentAffectedNode(\n  span: ParsedSpan,\n  graph?: NeatGraph,\n  scanPath?: string,\n): string {\n  const sid = serviceId(span.service, span.env)\n  const serviceNode =\n    graph && graph.hasNode(sid)\n      ? (graph.getNodeAttributes(sid) as ServiceNode)\n      : undefined\n  const callSite = callSiteFromSpan(span, serviceNode, scanPath)\n  if (callSite) {\n    const relPath = graph\n      ? reconcileObservedRelPath(graph, span.service, callSite.relPath)\n      : callSite.relPath\n    return fileId(span.service, relPath)\n  }\n  return sid\n}\n\n// Build the minimal ErrorEvent the receiver writes synchronously before\n// replying (ADR-033 §Error events, amended). affectedNode attributes to the\n// FileNode when the span carries a `code.filepath` call site, else to the\n// originating service (incidentAffectedNode above).\n//\n// errorMessage reads from the exception event's `exception.message` (OTel\n// semconv) so the incident surface shows the actual thrown error string.\n// When the span carries no exception event the field falls back to the HTTP\n// context the span still holds — \"500 on GET /users/:id\" (httpFailureMessage)\n// — and only then to the literal 'unknown error'. `span.name` is never in the\n// chain: OTel HTTP server instrumentation routinely populates it with the HTTP\n// method, which produces incidents that read 'GET' or 'POST' instead of the\n// underlying failure. `span.status.message` is intentionally out for the same\n// reason.\n// Span attributes pass through verbatim so consumers can read source\n// attribution (`code.filepath`, `code.lineno`, `code.function`) and other\n// SDK-emitted context without ingest enumerating every key it cares about.\n// Coerce span attributes to a JSON-safe shape — bigint values from the\n// parsed span (long ids, high-cardinality counters) become strings so the\n// passthrough record can be serialised to the ErrorEvent shape and round-\n// tripped through ErrorEventSchema. All other types pass through verbatim.\nfunction sanitizeAttributes(\n  attrs: ParsedSpan['attributes'],\n): Record<string, string | number | boolean | null | string[] | number[] | boolean[]> {\n  const out: Record<string, string | number | boolean | null | string[] | number[] | boolean[]> = {}\n  for (const [k, v] of Object.entries(attrs)) {\n    if (typeof v === 'bigint') out[k] = v.toString()\n    else out[k] = v as string | number | boolean | null | string[] | number[] | boolean[]\n  }\n  return out\n}\n\nexport function buildErrorEventForReceiver(\n  span: ParsedSpan,\n  graph?: NeatGraph,\n  scanPath?: string,\n): ErrorEvent | null {\n  if (span.statusCode !== 2) return null\n  const ts = span.startTimeIso ?? new Date().toISOString()\n  const attrs = sanitizeAttributes(span.attributes)\n  return {\n    id: `${span.traceId}:${span.spanId}`,\n    timestamp: ts,\n    service: span.service,\n    traceId: span.traceId,\n    spanId: span.spanId,\n    errorMessage: incidentMessage(span),\n    ...(span.exception?.type ? { exceptionType: span.exception.type } : {}),\n    ...(span.exception?.stacktrace\n      ? { exceptionStacktrace: span.exception.stacktrace }\n      : {}),\n    ...(Object.keys(attrs).length > 0 ? { attributes: attrs } : {}),\n    affectedNode: incidentAffectedNode(span, graph, scanPath),\n  }\n}\n\n// Synchronous file-write helper bound to a receiver. The receiver awaits this\n// before replying, so a write failure surfaces as 500 → OTel SDK retries.\nexport function makeErrorSpanWriter(\n  errorsPath: string,\n  graph?: NeatGraph,\n  scanPath?: string,\n): (span: ParsedSpan) => Promise<void> {\n  return async (span) => {\n    const ev = buildErrorEventForReceiver(span, graph, scanPath)\n    if (!ev) return\n    await fs.mkdir(path.dirname(errorsPath), { recursive: true })\n    await fs.appendFile(errorsPath, JSON.stringify(ev) + '\\n', 'utf8')\n  }\n}\n\n// Write one failing-response incident (issue #481) to errors.ndjson. Used for\n// an unambiguous 5xx (count 1) and for a flushed 4xx burst (count N). The\n// dominant status code names the failure; `incidentCount` carries N so the\n// incident surface shows \"5× 404\" without the per-span flood. Span attributes\n// pass through verbatim, same as the statusCode === 2 path, so source\n// attribution (`code.*`) and the response code survive to the consumer.\nasync function recordFailingResponseIncident(\n  ctx: IngestContext,\n  span: ParsedSpan,\n  affectedNode: string,\n  timestamp: string,\n  statusCode: number,\n  count: number,\n  firstTimestamp?: string,\n): Promise<void> {\n  const attrs = sanitizeAttributes(span.attributes)\n  const first = firstTimestamp ?? timestamp\n  const peer = pickAddress(span)\n  const message =\n    count > 1\n      ? `${count} consecutive HTTP ${statusCode} responses` +\n        (peer ? ` to ${peer}` : '')\n      : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : '')\n  const ev: ErrorEvent = {\n    id: `${span.traceId}:${span.spanId}`,\n    timestamp,\n    service: span.service,\n    traceId: span.traceId,\n    spanId: span.spanId,\n    errorType: 'http-failure',\n    errorMessage: message,\n    ...(Object.keys(attrs).length > 0 ? { attributes: attrs } : {}),\n    affectedNode,\n    httpStatusCode: statusCode,\n    incidentCount: count,\n    firstTimestamp: first,\n    lastTimestamp: timestamp,\n  }\n  await appendErrorEvent(ctx, ev)\n}\n\n// Record one incident for a span that carries an exception event but no ERROR\n// status and no HTTP failure code — an async / queue / background worker\n// (bullmq, Redis Streams, a scheduled task) whose job threw (ADR-117). Incident\n// recording keys on the failure signal, not on HTTP context: the message\n// follows the shared incidentMessage chain and the record attributes to the\n// handler file:line when the span carries `code.filepath`, else to the\n// originating service (incidentAffectedNode). handleSpan owns this write — the\n// receiver's synchronous error-writer fires only for statusCode === 2, so an\n// exception-only worker span reaches durability here, the same way the\n// response-code incidents below do.\nasync function recordExceptionIncident(\n  ctx: IngestContext,\n  span: ParsedSpan,\n  ts: string,\n): Promise<void> {\n  const attrs = sanitizeAttributes(span.attributes)\n  const ev: ErrorEvent = {\n    id: `${span.traceId}:${span.spanId}`,\n    timestamp: ts,\n    service: span.service,\n    traceId: span.traceId,\n    spanId: span.spanId,\n    errorMessage: incidentMessage(span),\n    ...(span.exception?.type ? { exceptionType: span.exception.type } : {}),\n    ...(span.exception?.stacktrace\n      ? { exceptionStacktrace: span.exception.stacktrace }\n      : {}),\n    ...(Object.keys(attrs).length > 0 ? { attributes: attrs } : {}),\n    affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath),\n  }\n  await appendErrorEvent(ctx, ev)\n}\n\n// Advance the 4xx burst for this (source, peer) pair (issue #481). A burst\n// accumulates silently; only when it crosses the threshold inside the window\n// does it flush ONE coalesced incident. A 4xx that arrives more than windowMs\n// after the previous one resets the burst — a slow trickle of probes never\n// coalesces. The dominant code is the most frequent 4xx seen across the burst.\nasync function advance4xxBurst(\n  ctx: IngestContext,\n  span: ParsedSpan,\n  affectedNode: string,\n  ts: string,\n  nowMs: number,\n  status: number,\n): Promise<void> {\n  const { threshold, windowMs } = loadIncidentThresholdsFromEnv()\n  if (!ctx.burstState) ctx.burstState = new Map()\n  const peer = pickAddress(span) ?? span.spanId\n  const key = `${span.service}->${peer}`\n  const existing = ctx.burstState.get(key)\n  let state: BurstState\n  if (existing && nowMs - existing.lastMs <= windowMs) {\n    existing.count += 1\n    existing.lastTs = ts\n    existing.lastMs = nowMs\n    existing.codes.set(status, (existing.codes.get(status) ?? 0) + 1)\n    state = existing\n  } else {\n    state = {\n      count: 1,\n      firstTs: ts,\n      lastTs: ts,\n      lastMs: nowMs,\n      codes: new Map([[status, 1]]),\n    }\n    ctx.burstState.set(key, state)\n  }\n\n  if (state.count < threshold) return\n\n  // Threshold met — flush one incident carrying the count, the dominant code,\n  // and the burst's first/last timestamps, then clear the burst so the next\n  // run of failures records its own incident rather than re-flushing every span.\n  let dominant = status\n  let max = 0\n  for (const [code, n] of state.codes) {\n    if (n > max) {\n      max = n\n      dominant = code\n    }\n  }\n  await recordFailingResponseIncident(\n    ctx,\n    span,\n    affectedNode,\n    state.lastTs,\n    dominant,\n    state.count,\n    state.firstTs,\n  )\n  ctx.burstState.delete(key)\n}\n\nexport async function handleSpan(ctx: IngestContext, span: ParsedSpan): Promise<void> {\n  // lastObserved derives from the span's own startTime per ADR-033 — replayed\n  // traces and out-of-order spans get a timestamp that reflects when the call\n  // actually fired, not when the receiver received it. Wall-clock is only the\n  // fallback for spans whose startTimeUnixNano is missing or unparseable.\n  const ts = span.startTimeIso ?? nowIso(ctx)\n  const nowMs = ctx.now ? ctx.now() : Date.now()\n  // Env discriminator from `deployment.environment(.name)` (ADR-074 §2).\n  // Older ParsedSpan producers may omit it — fall back to the literal\n  // `'unknown'` so the env-less wire format is preserved on auto-creation.\n  const env = span.env ?? 'unknown'\n  // Issue #374 — spans whose resource omits `service.name` route to\n  // `service:unidentified` in the URL-resolved project (the parser already\n  // substitutes the fallback). One warning per project per session names\n  // the project so an operator can fix the SDK config without grepping.\n  if (span.resourceServiceNamePresent === false) {\n    warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT)\n  }\n  // Auto-create a minimal ServiceNode for unseen span.service so OBSERVED\n  // edges land instead of silently dropping. Static extraction merges richer\n  // fields when it later finds the same id (ADR-033). The node is env-tagged\n  // when the span carries an env signal.\n  const sourceId = ensureServiceNode(ctx.graph, span.service, env)\n  const isError = span.statusCode === 2\n\n  // File-first OBSERVED origin (file-awareness.md §4). When the injected\n  // SpanProcessor captured a call site on this outbound (CLIENT/PRODUCER) span,\n  // the relationship originates from the file; without one it stays\n  // service-level. `observedSource()` creates the FileNode + CONTAINS lazily so\n  // they only land when an edge actually does — and never for the inbound\n  // (SERVER) parent-fallback side, which carries no call site.\n  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId) as ServiceNode\n  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath)\n\n  // Stash this span in the parent-span cache so any later child whose address\n  // resolution misses can still resolve the cross-service edge via parentSpanId.\n  // The call site rides along so the fallback edge anchors to this span's\n  // file:line when this span turns out to be a parent (issue #536).\n  cacheSpanService(span, nowMs, callSite)\n  const observedSource = (): string =>\n    callSite ? ensureObservedFileNode(ctx.graph, span.service, sourceId, callSite) : sourceId\n  // Evidence for the OBSERVED edge — populated from the span's code.* semconv\n  // when the call site resolved (file-awareness.md §4 + §6). Never fabricated:\n  // absent call site → undefined evidence. The path is reconciled the same way\n  // the edge's origin node is (reconcileObservedRelPath), so evidence.file names\n  // the fused EXTRACTED path the edge lands on rather than the raw deployed\n  // absolute path — otherwise the edge node and its own evidence disagree.\n  const callSiteEvidence: { file: string; line?: number } | undefined = callSite\n    ? {\n        file: reconcileObservedRelPath(ctx.graph, span.service, callSite.relPath),\n        ...(callSite.line !== undefined ? { line: callSite.line } : {}),\n      }\n    : undefined\n\n  let affectedNode = sourceId\n\n  // Only the caller/producer side of a call mints an OBSERVED edge directly\n  // (issue #429). INTERNAL / SERVER / CONSUMER spans don't: a SERVER/CONSUMER\n  // span is the callee, and its edge is minted from its parent via the\n  // parent-span fallback below (left ungated). Gating here keeps INTERNAL\n  // connection spans (`tcp.connect` / `tls.connect` with a peer address) from\n  // minting spurious service-level edges.\n  const mintsFromCallerSide = spanMintsObservedEdge(span.kind)\n\n  if (span.dbSystem) {\n    // Database span. A networked database resolves its DatabaseNode by peer\n    // host; an in-process / embedded one (SQLite, better-sqlite3, an in-memory\n    // store) crosses no network boundary and carries no peer address, so it\n    // keys on a service-scoped local identity instead (ADR-118). Both mint the\n    // same file-grained service→database CONNECTS_TO OBSERVED edge — the edge\n    // that makes a leaf service's datastore reads legible (#576, #546).\n    if (mintsFromCallerSide) {\n      const host = pickAddress(span)\n      // Engine comes off the OTel attribute as a string per Rule 8 — no\n      // hardcoded engine list on either branch.\n      let targetId: string\n      if (host) {\n        ensureDatabaseNode(ctx.graph, host, span.dbSystem)\n        targetId = databaseId(host)\n      } else {\n        // No peer host — an in-process DB. Name the node by its logical\n        // database (db.name) when the span carries one, the engine otherwise.\n        const localName = span.dbName ?? span.dbSystem\n        targetId = ensureLocalDatabaseNode(\n          ctx.graph,\n          span.service,\n          localName,\n          span.dbSystem,\n        )\n      }\n      const result = upsertObservedEdge(\n        ctx.graph,\n        EdgeType.CONNECTS_TO,\n        observedSource(),\n        targetId,\n        ts,\n        isError,\n        callSiteEvidence,\n      )\n      if (result) affectedNode = targetId\n    }\n  } else if (\n    span.messagingSystem &&\n    span.messagingDestination &&\n    spanMintsMessagingEdge(span.kind)\n  ) {\n    // Messaging span. The semantic destination is the topic / queue / stream the\n    // code talks to — not the broker host, which is transport. A PRODUCER span\n    // publishes; a CONSUMER span consumes. Both mint an OBSERVED edge to the same\n    // destination node the static extractor names (extract/calls/kafka.ts), so\n    // the declared and observed queue edges fuse into one (→ divergence) instead\n    // of twinning. File-grained through the same call-site path as any other\n    // OBSERVED edge (file-awareness.md §4): when the span carries `code.*`, the\n    // edge originates from the caller's FileNode at the exact call site,\n    // reconciled onto the EXTRACTED service-relative path (`reconcileObservedRelPath`,\n    // ADR-118); without a call site it stays service-level, honestly. This is the\n    // consumer-side pair of the producer edge — the queue topology the OBSERVED\n    // layer used to leave dark on the consumer side (issue #614).\n    const targetId = ensureMessagingDestinationNode(\n      ctx.graph,\n      span.messagingSystem,\n      span.messagingDestination,\n    )\n    const edgeType =\n      span.kind === WIRE_SPAN_KIND_CONSUMER\n        ? EdgeType.CONSUMES_FROM\n        : EdgeType.PUBLISHES_TO\n    const result = upsertObservedEdge(\n      ctx.graph,\n      edgeType,\n      observedSource(),\n      targetId,\n      ts,\n      isError,\n      callSiteEvidence,\n    )\n    if (result) affectedNode = targetId\n  } else if (\n    span.graphqlOperationName &&\n    span.graphqlOperationType &&\n    spanServesGraphqlOperation(span.kind)\n  ) {\n    // GraphQL execution span (issue #615). Every GraphQL request rides one HTTP\n    // endpoint (`POST /graphql`), so at HTTP grain the whole API collapses to a\n    // single edge and the operation-level topology is invisible. The execution\n    // span carries the operation the client actually named — `graphql.operation.name`\n    // with `graphql.operation.type` — so mint an OBSERVED `CONTAINS` edge from the\n    // serving service to a per-operation node, the same ownership shape a service\n    // has over a route (ADR-119) and a file (file-awareness.md §2). OBSERVED-only:\n    // the SDL / resolver map is not parsed statically in this cut; the node is\n    // minted observed-first and a future static GraphQL extractor fuses onto the\n    // same id (ADR-122). File-grained through the same call-site path as any other\n    // OBSERVED edge (file-awareness.md §4): when the span carries `code.*` (the\n    // resolver call site) the edge originates from that `FileNode` at the exact\n    // `file:line`, reconciled onto the EXTRACTED service-relative path; without a\n    // call site it stays service-level, honestly. Both operation name and type\n    // must be present to key a stable id — a nameless/typeless execution span\n    // falls through rather than minting a fabricated operation.\n    const targetId = ensureGraphqlOperationNode(\n      ctx.graph,\n      span.service,\n      span.graphqlOperationType,\n      span.graphqlOperationName,\n    )\n    const result = upsertObservedEdge(\n      ctx.graph,\n      EdgeType.CONTAINS,\n      observedSource(),\n      targetId,\n      ts,\n      isError,\n      callSiteEvidence,\n    )\n    if (result) affectedNode = targetId\n  } else if (\n    span.rpcSystem === 'grpc' &&\n    span.rpcService &&\n    span.rpcMethod &&\n    spanServesGrpcMethod(span.kind)\n  ) {\n    // gRPC execution span (issue #616). gRPC used to engage only at service\n    // grain: every method collapsed onto one service→service edge, so the\n    // per-method topology was invisible and one-sided. The serving span carries\n    // the method the caller actually invoked — `rpc.service` (the fully-qualified\n    // `orders.OrderService`) with `rpc.method` (`GetOrder`) — so mint an OBSERVED\n    // `CONTAINS` edge from the serving service to a per-method node, the same\n    // ownership shape a service has over a route (ADR-119), a GraphQL operation\n    // (ADR-122), and a file (file-awareness.md §2). The node is keyed on the\n    // fully-qualified `rpc.service` — the wire contract both the span and the\n    // `.proto` carry verbatim — so the static `.proto` extractor's declared\n    // method fuses onto this same id into a two-sided divergence (ADR-123).\n    // File-grained through the same call-site path as any other OBSERVED edge\n    // (file-awareness.md §4): when the span carries `code.*` (the handler call\n    // site) the edge originates from that `FileNode` at the exact `file:line`,\n    // reconciled onto the EXTRACTED service-relative path; without a call site it\n    // stays service-level, honestly. The gate admits only the serving side —\n    // SERVER / INTERNAL / unkinded — so a CLIENT span mints no ownership\n    // (client→method attribution is deferred) and instead falls through to the\n    // cross-service resolver, leaving the caller→callee edge intact. Both service\n    // and method must be present to key a stable id.\n    const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod)\n    const result = upsertObservedEdge(\n      ctx.graph,\n      EdgeType.CONTAINS,\n      observedSource(),\n      targetId,\n      ts,\n      isError,\n      callSiteEvidence,\n    )\n    if (result) affectedNode = targetId\n  } else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {\n    // WebSocket upgrade span (issue #617). A WebSocket app used to produce no\n    // OBSERVED topology at all: only message-handler errors surfaced, as\n    // incidents, and the channels themselves stayed invisible — the frames after\n    // the handshake ride the socket, not more spans. The one span that reliably\n    // marks a channel is the HTTP upgrade that opens it: a SERVER `GET` carrying\n    // `Upgrade: websocket` and the connection path. So mint an OBSERVED\n    // `CONNECTS_TO` edge from the serving service to a per-channel node —\n    // reusing the same connection edge a service has to a datastore (#576), not a\n    // new edge type. Unlike the structural `CONTAINS` a service has over a route /\n    // operation / method — durably declared artifacts whose edge never goes\n    // stale — a channel's whole meaning is liveness, so `CONNECTS_TO` is the right\n    // shape: it carries `lastObserved` and decays OBSERVED → STALE on\n    // CONNECTS_TO's own threshold when the channel goes quiet (the daemon\n    // staleness loop, #532). OBSERVED-only: a WebSocket channel is known from\n    // observation, never from static extraction, so the node has no declared twin\n    // and is excluded from `missing-extracted` (divergences.ts). File-grained\n    // through the same call-site path as any other OBSERVED edge\n    // (file-awareness.md §4): when the span carries `code.*` the edge originates\n    // from that `FileNode` at the exact `file:line`, reconciled onto the EXTRACTED\n    // service-relative path; without a call site it stays service-level, honestly.\n    // The gate admits only the serving side — SERVER / INTERNAL / unkinded — so a\n    // CLIENT upgrade span mints no channel (client→channel attribution is\n    // deferred, ADR-125).\n    const targetId = ensureWebsocketChannelNode(\n      ctx.graph,\n      span.service,\n      span.websocketChannel,\n    )\n    const result = upsertObservedEdge(\n      ctx.graph,\n      EdgeType.CONNECTS_TO,\n      observedSource(),\n      targetId,\n      ts,\n      isError,\n      callSiteEvidence,\n    )\n    if (result) affectedNode = targetId\n  } else {\n    // Possibly a cross-service call. Resolve the peer; if it matches a known\n    // ServiceNode, record an OBSERVED CALLS edge to the typed target. If it\n    // matches nothing — pod IP, ingress hostname, AWS PrivateLink endpoint —\n    // create a FrontierNode placeholder and record an OBSERVED edge to that\n    // FrontierNode so the call carries the same provenance + signal-block +\n    // graded confidence as any other OBSERVED edge (ADR-068). The target ref\n    // identifies the node-type; provenance describes how the edge was learned.\n    // promoteFrontierNodes (run by the extract orchestrator) rewrites the\n    // target ref once a later round resolves the host; the edge's provenance\n    // stays OBSERVED across promotion.\n    // A loopback host (localhost / 127.0.0.0/8 / ::1) is skipped here: it never\n    // resolves to a distinct peer, and minting frontier:localhost would double\n    // the edge that the callee's parent-span fallback already records for this\n    // same call (issues #590, #577). Leaving resolvedViaAddress false hands the\n    // call to that fallback instead.\n    const host = pickAddress(span)\n    let resolvedViaAddress = false\n    if (mintsFromCallerSide && host && host !== span.service && !isLoopbackHost(host)) {\n      const targetId = resolveServiceId(ctx.graph, host, env)\n      if (targetId && targetId !== sourceId) {\n        upsertObservedEdge(\n          ctx.graph,\n          EdgeType.CALLS,\n          observedSource(),\n          targetId,\n          ts,\n          isError,\n          callSiteEvidence,\n        )\n        affectedNode = targetId\n        resolvedViaAddress = true\n      } else if (!targetId) {\n        const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts)\n        upsertObservedEdge(\n          ctx.graph,\n          EdgeType.CALLS,\n          observedSource(),\n          frontierNodeId,\n          ts,\n          isError,\n          callSiteEvidence,\n        )\n        affectedNode = frontierNodeId\n        resolvedViaAddress = true\n      }\n    }\n\n    // Parent-span fallback (ADR-033): when address-based resolution didn't\n    // produce an edge and the span has a parentSpanId we've cached, the\n    // parent's service identifies the caller. The current span is the server\n    // side of the call, so the edge direction is parent.service → current.\n    // The cached entry carries the parent span's env, so the auto-created\n    // parent ServiceNode lands on the env-tagged id the parent advertised.\n    if (!resolvedViaAddress && span.parentSpanId) {\n      const parent = lookupParentSpan(span.traceId, span.parentSpanId, nowMs)\n      if (parent && parent.service !== span.service) {\n        const parentId = ensureServiceNode(ctx.graph, parent.service, parent.env)\n        // When the parent span carried a `code.*` call site, originate the edge\n        // from the parent's FileNode so it anchors to file:line instead of the\n        // bare parent ServiceNode (issue #536). Without a cached call site the\n        // edge stays service-coarse — never fabricated (file-awareness.md §6).\n        const fallbackSource = parent.callSite\n          ? ensureObservedFileNode(ctx.graph, parent.service, parentId, parent.callSite)\n          : parentId\n        const fallbackEvidence: { file: string; line?: number } | undefined =\n          parent.callSite\n            ? {\n                file: reconcileObservedRelPath(\n                  ctx.graph,\n                  parent.service,\n                  parent.callSite.relPath,\n                ),\n                ...(parent.callSite.line !== undefined\n                  ? { line: parent.callSite.line }\n                  : {}),\n              }\n            : undefined\n        upsertObservedEdge(\n          ctx.graph,\n          EdgeType.CALLS,\n          fallbackSource,\n          sourceId,\n          ts,\n          isError,\n          fallbackEvidence,\n        )\n      }\n    }\n  }\n\n  if (span.statusCode === 2) {\n    stitchTrace(ctx.graph, sourceId, ts)\n    // The durable ErrorEvent write moved to the receiver so the file write\n    // happens synchronously before the 200 reply (ADR-033 §Error events,\n    // amended). watch.ts wires makeErrorSpanWriter into onErrorSpanSync.\n    // handleSpan still runs the in-graph error effects (stitchTrace above);\n    // it just doesn't append to errors.ndjson anymore. ctx.errorsPath stays\n    // for the optional opt-in path below — daemon-less callers (CLI tests,\n    // ad-hoc scripts) that skip the receiver hook still get a write here.\n    if (ctx.writeErrorEventInline !== false) {\n      const attrs = sanitizeAttributes(span.attributes)\n      const ev: ErrorEvent = {\n        id: `${span.traceId}:${span.spanId}`,\n        timestamp: ts,\n        service: span.service,\n        traceId: span.traceId,\n        spanId: span.spanId,\n        errorMessage: incidentMessage(span),\n        ...(span.exception?.type ? { exceptionType: span.exception.type } : {}),\n        ...(span.exception?.stacktrace\n          ? { exceptionStacktrace: span.exception.stacktrace }\n          : {}),\n        ...(Object.keys(attrs).length > 0 ? { attributes: attrs } : {}),\n        affectedNode,\n      }\n      await appendErrorEvent(ctx, ev)\n    }\n  }\n\n  // Failing-response incidents (issue #481). OTel semconv leaves a CLIENT span's\n  // status UNSET on a 4xx/5xx response, so the status-only path above is blind\n  // to a service whose outbound calls are failing en masse — the exact gap a\n  // debugging session hits (80× HTTP 404 against one peer surfacing nothing).\n  // A response status is read from the span here regardless of statusCode:\n  //   * 5xx → record an incident immediately (unambiguous failure, even with\n  //     UNSET status). Skipped when statusCode === 2 already recorded above so\n  //     a 5xx that also carries ERROR status isn't double-counted.\n  //   * 4xx on a CLIENT/PRODUCER span → coalesce. The burst against this\n  //     (source, peer) pair advances; when it reaches the threshold inside the\n  //     window it records ONE incident carrying the count and dominant code.\n  //   * a lone 4xx, or any 2xx/3xx → no incident.\n  // Always written here (not gated on writeErrorEventInline): the daemon's\n  // receiver only fires its synchronous error-writer for statusCode === 2, so\n  // these spans never reach that durability handoff — handleSpan owns them.\n  if (span.statusCode !== 2) {\n    const status = httpResponseStatus(span)\n    // ADR-117 — an exception event on a span that left its status UNSET is\n    // still an unambiguous failure: a bullmq / Redis-Streams / background\n    // worker whose job threw carries the exception and no HTTP response\n    // context, so the status-only and response-code paths both miss it. Record\n    // it independent of HTTP, attributed to the handler file:line (or the\n    // service) the same way the statusCode === 2 path is. Ordered first because\n    // the exception is the unambiguous signal; the response-code branches below\n    // carry the exception-less HTTP failures.\n    if (span.exception) {\n      await recordExceptionIncident(ctx, span, ts)\n    } else if (status !== undefined && status >= 500) {\n      // A failing-response incident is attributed to the SOURCE service — the\n      // caller whose outbound calls are failing is the node a debugger asks\n      // about (\"why is my service erroring\"). The peer it failed against is\n      // carried in the message and in attributes. This is deliberately not the\n      // edge target (frontier/peer) the OBSERVED edge above resolved to: the\n      // signal is \"this service's calls to X are failing\", not \"X failed\".\n      await recordFailingResponseIncident(ctx, span, sourceId, ts, status, 1)\n    } else if (\n      status !== undefined &&\n      status >= 400 &&\n      spanMintsObservedEdge(span.kind)\n    ) {\n      await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status)\n    }\n  }\n  void affectedNode\n\n  // Post-ingest policy trigger (ADR-043). The hook is awaited so failures\n  // surface; daemons wrap it in a try/catch that logs without throwing.\n  if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph)\n}\n\nexport { stitchTrace }\n\n// Promote any frontier:<host> placeholder whose host matches an alias on a\n// real ServiceNode: re-link inbound/outbound edges to the service, then drop\n// the placeholder. Returns the count of nodes promoted, for tests + logs.\n//\n// Called at the end of every extraction round. Static rounds are when new\n// aliases land (compose names, k8s metadata.name, Dockerfile labels), so\n// running it there picks up the case the issue describes: ingest fills in a\n// frontier when traffic arrives for an unknown host, and the next extraction\n// round resolves it.\n// Optional gate for block-action policies (ADR-044). When `policies` is\n// non-empty, each candidate FrontierNode runs through `canPromoteFrontier`\n// before its incident edges are rewired. Block-action policies that fire on\n// the frontier veto the promotion — the FrontierNode persists; the next\n// extract pass tries again.\nexport interface PromoteFrontierOptions {\n  policies?: Policy[]\n  policyCtx?: PolicyEvaluationContext\n}\n\nexport function promoteFrontierNodes(\n  graph: NeatGraph,\n  opts: PromoteFrontierOptions = {},\n): number {\n  const aliasIndex = new Map<string, string>()\n  graph.forEachNode((id, attrs) => {\n    const a = attrs as ServiceNode & { type?: string }\n    if (a.type !== NodeType.ServiceNode) return\n    aliasIndex.set(a.name, id)\n    if (a.aliases) {\n      for (const alias of a.aliases) aliasIndex.set(alias, id)\n    }\n  })\n\n  const toPromote: { frontierId: string; serviceId: string }[] = []\n  graph.forEachNode((id, attrs) => {\n    const a = attrs as FrontierNode & { type?: string }\n    if (a.type !== NodeType.FrontierNode) return\n    const target = aliasIndex.get(a.host)\n    if (!target) return\n    if (target === id) return\n    toPromote.push({ frontierId: id, serviceId: target })\n  })\n\n  let promoted = 0\n  for (const { frontierId, serviceId } of toPromote) {\n    if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {\n      const gate = canPromoteFrontier(graph, frontierId, opts.policies, opts.policyCtx)\n      if (!gate.allowed) {\n        // Block-action policy fired on this frontier — skip the rewire and\n        // leave the FrontierNode in place. Violations already surfaced via\n        // the policy log on the same evaluation pass.\n        continue\n      }\n    }\n    rewireFrontierEdges(graph, frontierId, serviceId)\n    graph.dropNode(frontierId)\n    promoted++\n  }\n  return promoted\n}\n\nfunction rewireFrontierEdges(graph: NeatGraph, frontierId: string, serviceId: string): void {\n  const inbound = [...graph.inboundEdges(frontierId)]\n  const outbound = [...graph.outboundEdges(frontierId)]\n\n  for (const edgeId of inbound) {\n    const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n    rebuildEdge(graph, edge, edge.source, serviceId, edgeId)\n  }\n  for (const edgeId of outbound) {\n    const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n    rebuildEdge(graph, edge, serviceId, edge.target, edgeId)\n  }\n}\n\nfunction rebuildEdge(\n  graph: NeatGraph,\n  edge: GraphEdge,\n  newSource: string,\n  newTarget: string,\n  oldEdgeId: string,\n): void {\n  graph.dropEdge(oldEdgeId)\n  // ADR-068 — promotion rewrites the target ref; provenance carries forward.\n  // An OBSERVED edge to a FrontierNode promotes to an OBSERVED edge to the\n  // matched typed node; an INFERRED edge stays INFERRED; etc.\n  const newId =\n    edge.provenance === Provenance.OBSERVED\n      ? observedEdgeId(newSource, newTarget, edge.type)\n      : edge.provenance === Provenance.INFERRED\n        ? inferredEdgeId(newSource, newTarget, edge.type)\n        : extractedEdgeId(newSource, newTarget, edge.type)\n\n  if (graph.hasEdge(newId)) {\n    const existing = graph.getEdgeAttributes(newId) as GraphEdge\n    const merged: GraphEdge = {\n      ...existing,\n      callCount: (existing.callCount ?? 0) + (edge.callCount ?? 0),\n      lastObserved: pickLater(existing.lastObserved, edge.lastObserved),\n    }\n    graph.replaceEdgeAttributes(newId, merged)\n    return\n  }\n\n  const rebuilt: GraphEdge = {\n    ...edge,\n    id: newId,\n    source: newSource,\n    target: newTarget,\n  }\n  graph.addEdgeWithKey(newId, newSource, newTarget, rebuilt)\n}\n\nfunction pickLater(a: string | undefined, b: string | undefined): string | undefined {\n  if (!a) return b\n  if (!b) return a\n  return new Date(a).getTime() >= new Date(b).getTime() ? a : b\n}\n\nexport function makeSpanHandler(ctx: IngestContext): (span: ParsedSpan) => Promise<void> {\n  return (span) => handleSpan(ctx, span)\n}\n\nexport type { StaleEvent }\n\nexport interface MarkStaleOptions {\n  // Per-edge-type override map. Defaults to DEFAULT_STALE_THRESHOLDS, merged\n  // with NEAT_STALE_THRESHOLDS if the env var is set.\n  thresholds?: Record<string, number>\n  now?: number\n  // ndjson path. When set, every OBSERVED → STALE transition appends one\n  // line. Skipped if undefined — tests and embedded use cases don't need a\n  // log.\n  staleEventsPath?: string\n  // Project tag for event-bus routing (ADR-051). Defaults to DEFAULT_PROJECT.\n  project?: string\n}\n\n// Demote OBSERVED edges that haven't been seen in a while. Per-edge-type\n// thresholds: HTTP CALLS go stale fast; infra DEPENDS_ON is patient. Returns\n// the count of demotions and the events appended to the log.\nexport async function markStaleEdges(\n  graph: NeatGraph,\n  options: MarkStaleOptions = {},\n): Promise<{ count: number; events: StaleEvent[] }> {\n  const thresholds = options.thresholds ?? loadStaleThresholdsFromEnv()\n  const now = options.now ?? Date.now()\n  const events: StaleEvent[] = []\n\n  const project = options.project ?? DEFAULT_PROJECT\n  graph.forEachEdge((id, attrs) => {\n    const e = attrs as GraphEdge\n    if (e.provenance !== Provenance.OBSERVED) return\n    if (!e.lastObserved) return\n    const threshold = thresholdForEdgeType(e.type, thresholds)\n    const age = now - new Date(e.lastObserved).getTime()\n    if (age > threshold) {\n      const updated: GraphEdge = { ...e, provenance: Provenance.STALE, confidence: 0.3 }\n      graph.replaceEdgeAttributes(id, updated)\n      events.push({\n        edgeId: id,\n        source: e.source,\n        target: e.target,\n        edgeType: e.type,\n        thresholdMs: threshold,\n        ageMs: age,\n        lastObserved: e.lastObserved,\n        transitionedAt: new Date(now).toISOString(),\n      })\n      // Stale-transition fires through the bus (ADR-051). The graph\n      // subscription in events.ts can't see the OBSERVED→STALE semantic on\n      // its own — a provenance flip is just an attribute update from\n      // graphology's view.\n      emitNeatEvent({\n        type: 'stale-transition',\n        project,\n        payload: {\n          edgeId: id,\n          from: Provenance.OBSERVED,\n          to: Provenance.STALE,\n        },\n      })\n    }\n  })\n\n  if (options.staleEventsPath && events.length > 0) {\n    await appendStaleEvents(options.staleEventsPath, events)\n  }\n\n  return { count: events.length, events }\n}\n\nasync function appendStaleEvents(staleEventsPath: string, events: StaleEvent[]): Promise<void> {\n  await fs.mkdir(path.dirname(staleEventsPath), { recursive: true })\n  const lines = events.map((e) => JSON.stringify(e)).join('\\n') + '\\n'\n  await fs.appendFile(staleEventsPath, lines, 'utf8')\n}\n\nexport async function readStaleEvents(staleEventsPath: string): Promise<StaleEvent[]> {\n  try {\n    const raw = await fs.readFile(staleEventsPath, 'utf8')\n    return raw\n      .split('\\n')\n      .filter((line) => line.length > 0)\n      .map((line) => JSON.parse(line) as StaleEvent)\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n    throw err\n  }\n}\n\nexport interface StalenessLoopOptions {\n  thresholds?: Record<string, number>\n  intervalMs?: number\n  staleEventsPath?: string\n  // Project tag for event-bus routing (ADR-051).\n  project?: string\n  // Post-stale-transition policy trigger (ADR-043). Fires after each tick of\n  // markStaleEdges so policies see the new STALE state. Daemons wire this to\n  // evaluateAllPolicies + PolicyViolationsLog.append.\n  onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n}\n\nexport function startStalenessLoop(\n  graph: NeatGraph,\n  options: StalenessLoopOptions = {},\n): () => void {\n  let stopped = false\n  const intervalMs = options.intervalMs ?? 60_000\n  const tick = (): void => {\n    if (stopped) return\n    void (async () => {\n      try {\n        await markStaleEdges(graph, {\n          thresholds: options.thresholds,\n          staleEventsPath: options.staleEventsPath,\n          project: options.project,\n        })\n        if (options.onPolicyTrigger) await options.onPolicyTrigger(graph)\n      } catch (err) {\n        console.error('staleness tick failed', err)\n      }\n    })()\n  }\n  const interval = setInterval(tick, intervalMs)\n  if (typeof interval.unref === 'function') interval.unref()\n  return () => {\n    stopped = true\n    clearInterval(interval)\n  }\n}\n\nexport async function readErrorEvents(errorsPath: string): Promise<ErrorEvent[]> {\n  try {\n    const raw = await fs.readFile(errorsPath, 'utf8')\n    const events = raw\n      .split('\\n')\n      .filter((line) => line.length > 0)\n      .map((line) => JSON.parse(line) as ErrorEvent)\n    return dedupeIncidents(events)\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n    throw err\n  }\n}\n\n// A synthesized HTTP-status incident carries no failure of its own — it's the\n// \"500 on GET /users/:id\" line handleSpan mints for a server span that answered\n// 5xx with no exception event of its own (httpFailureMessage). When the real\n// failure surfaced deeper in the same trace (a DB driver threw, a downstream\n// gRPC returned UNAVAILABLE), that exception is recorded as its own incident on\n// the same node, and the server's HTTP echo is a duplicate of it. A record is\n// \"synthesized HTTP\" when it carries no exception data, no explicit errorType\n// (the coalesced http-failure incidents set one and carry their own count), and\n// its message is exactly the HTTP line re-derived from its own attributes.\nfunction isSynthesizedHttpIncident(ev: ErrorEvent): boolean {\n  if (ev.exceptionType || ev.exceptionStacktrace) return false\n  if (ev.errorType) return false\n  if (!ev.attributes) return false\n  const synth = httpFailureMessageFromAttrs(ev.attributes)\n  return synth !== undefined && synth === ev.errorMessage\n}\n\n// Make the incident surface idempotent per failure. Two passes:\n//\n// Pass 1 — collapse exact `(traceId, spanId)` re-deliveries. The ndjson sidecar\n// is append-only (persistence contract), so a re-delivered span — OTel\n// BatchSpanProcessor retries, or a receiver + handler both writing one POST —\n// leaves duplicate lines on disk. The deterministic incident `id` already\n// encodes the pair (`${traceId}:${spanId}`); we dedupe on it directly, falling\n// back to the raw pair for any record that predates the id. Records that carry\n// neither (extract parse-failure rows, `source: 'extract'`) pass through\n// untouched — they aren't span incidents. First write wins so the original\n// timestamp is preserved.\n//\n// Pass 2 — collapse one failure recorded from two spans of the same trace. A\n// failing request lands one incident from the span that actually threw (the DB\n// child's exception, a downstream gRPC error) and a second, synthesized one from\n// the HTTP server span that echoed it as a 5xx. Both key to the same\n// `(traceId, affectedNode)`; the exact-id pass can't see it because the spanIds\n// differ. When a real failure shares a trace and node with a synthesized HTTP\n// echo, drop the echo so the request counts once (issue #624). A cross-service\n// failure keeps both sides: the caller's failing-response incident and the\n// callee's exception land on different `affectedNode`s (separate ledgers per the\n// otel-ingest contract), so they never share a group.\nfunction dedupeIncidents(events: ErrorEvent[]): ErrorEvent[] {\n  const seen = new Set<string>()\n  const once: ErrorEvent[] = []\n  for (const ev of events) {\n    const key =\n      ev.id ??\n      (ev.traceId && ev.spanId ? `${ev.traceId}:${ev.spanId}` : undefined)\n    if (key === undefined) {\n      once.push(ev)\n      continue\n    }\n    if (seen.has(key)) continue\n    seen.add(key)\n    once.push(ev)\n  }\n\n  const groupKey = (ev: ErrorEvent): string => `${ev.traceId}\\u0000${ev.affectedNode}`\n  const hasRealFailure = new Set<string>()\n  for (const ev of once) {\n    if (ev.traceId && !isSynthesizedHttpIncident(ev)) hasRealFailure.add(groupKey(ev))\n  }\n  return once.filter((ev) => {\n    if (!ev.traceId || !isSynthesizedHttpIncident(ev)) return true\n    return !hasRealFailure.has(groupKey(ev))\n  })\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Snapshot merge (ADR-074 §1)\n//\n// `neat sync` (local) and `neat sync --to <url>` (remote) feed snapshots into\n// a live graph through this helper. It lives in ingest.ts because mutation\n// authority sits with ingest + extract per the lifecycle contract (ADR-030);\n// the merge is ingestion of an external snapshot, no different in shape from\n// the way handleSpan ingests an OTel span.\n//\n// The merge preserves EXTRACTED + OBSERVED coexistence per Rule 2 — each\n// provenance variant has its own edge id, so the incoming EXTRACTED edges\n// can't stomp the daemon's accumulated OBSERVED edges and vice versa. Rule of\n// thumb: incoming wins for nodes/edges the live graph hasn't seen yet;\n// everything already present keeps its current attributes.\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface MergeSnapshotResult {\n  nodesAdded: number\n  edgesAdded: number\n}\n\nexport function mergeSnapshot(\n  graph: NeatGraph,\n  snapshot: PersistedGraph,\n): MergeSnapshotResult {\n  const exported = snapshot.graph as {\n    nodes?: Array<{ key: string; attributes?: GraphNode }>\n    edges?: Array<{ key?: string; source: string; target: string; attributes?: GraphEdge }>\n  }\n\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const node of exported.nodes ?? []) {\n    if (graph.hasNode(node.key)) continue\n    if (!node.attributes) continue\n    graph.addNode(node.key, node.attributes)\n    nodesAdded++\n  }\n\n  for (const edge of exported.edges ?? []) {\n    const attrs = edge.attributes\n    if (!attrs) continue\n    const id = edge.key ?? attrs.id\n    if (!id) continue\n    if (graph.hasEdge(id)) continue\n    // Skip when either endpoint is missing — can happen if the snapshot\n    // names a node the live graph already evicted and the incoming nodes\n    // array didn't include.\n    if (!graph.hasNode(edge.source) || !graph.hasNode(edge.target)) continue\n    graph.addEdgeWithKey(id, edge.source, edge.target, attrs)\n    edgesAdded++\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport ignore, { type Ignore } from 'ignore'\nimport { minimatch } from 'minimatch'\nimport type { ServiceNode } from '@neat.is/types'\nimport { NodeType, serviceId } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport {\n  IGNORED_DIRS,\n  exists,\n  isPythonVenvDir,\n  readJson,\n  type DiscoveredService,\n  type PackageJson,\n} from './shared.js'\nimport { discoverPythonService, pythonToPackage } from './python.js'\nimport { computeServiceOwner, loadCodeowners } from './owners.js'\nimport { recordExtractionError } from './errors.js'\n\nconst DEFAULT_SCAN_DEPTH = 5\n\ninterface RootPackageJson extends PackageJson {\n  workspaces?: string[] | { packages?: string[] }\n}\n\nfunction parseScanDepth(): number {\n  const raw = process.env.NEAT_SCAN_DEPTH\n  if (!raw) return DEFAULT_SCAN_DEPTH\n  const n = Number.parseInt(raw, 10)\n  return Number.isFinite(n) && n >= 0 ? n : DEFAULT_SCAN_DEPTH\n}\n\nfunction workspaceGlobs(pkg: RootPackageJson): string[] | null {\n  const ws = pkg.workspaces\n  if (!ws) return null\n  if (Array.isArray(ws)) return ws.length > 0 ? ws : null\n  if (Array.isArray(ws.packages)) return ws.packages.length > 0 ? ws.packages : null\n  return null\n}\n\nasync function hasPythonManifest(dir: string): Promise<boolean> {\n  return (\n    (await exists(path.join(dir, 'pyproject.toml'))) ||\n    (await exists(path.join(dir, 'requirements.txt'))) ||\n    (await exists(path.join(dir, 'setup.py')))\n  )\n}\n\nasync function loadGitignore(scanPath: string): Promise<Ignore | null> {\n  const gitignorePath = path.join(scanPath, '.gitignore')\n  if (!(await exists(gitignorePath))) return null\n  const raw = await fs.readFile(gitignorePath, 'utf8')\n  return ignore().add(raw)\n}\n\ninterface WalkOptions {\n  maxDepth: number\n  ig: Ignore | null\n}\n\nasync function walkDirs(\n  start: string,\n  scanPath: string,\n  options: WalkOptions,\n  visit: (dir: string) => Promise<void> | void,\n): Promise<void> {\n  async function recurse(current: string, depth: number): Promise<void> {\n    if (depth > options.maxDepth) return\n    const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => [])\n    for (const entry of entries) {\n      if (!entry.isDirectory()) continue\n      if (IGNORED_DIRS.has(entry.name)) continue\n      const child = path.join(current, entry.name)\n      if (options.ig) {\n        const rel = path.relative(scanPath, child).split(path.sep).join('/')\n        // Trailing slash so `ignore` evaluates the entry as a directory; without\n        // it, gitignore patterns like `dist/` won't match because the lib\n        // distinguishes file vs. directory tests.\n        if (rel && options.ig.ignores(rel + '/')) continue\n      }\n      // Issue #344 — `pyvenv.cfg` marks every directory that `python -m venv`\n      // or `virtualenv` creates, regardless of what the wrapper dir is called.\n      // The `IGNORED_DIRS` name list catches the common shapes (`.venv`,\n      // `venv`, `.tox`); this catches the long-tail ones (`env-3.11`,\n      // `.direnv`, ad-hoc names).\n      if (await isPythonVenvDir(child)) continue\n      await visit(child)\n      await recurse(child, depth + 1)\n    }\n  }\n  await recurse(start, 0)\n}\n\nasync function expandWorkspaceGlobs(\n  scanPath: string,\n  globs: string[],\n): Promise<string[]> {\n  const found = new Set<string>()\n  const scanDepth = parseScanDepth()\n\n  for (const raw of globs) {\n    const pattern = raw.replace(/^\\.\\//, '')\n\n    if (!pattern.includes('*')) {\n      const candidate = path.join(scanPath, pattern)\n      if (await exists(path.join(candidate, 'package.json'))) found.add(candidate)\n      continue\n    }\n\n    const segments = pattern.split('/')\n    const staticSegments: string[] = []\n    for (const seg of segments) {\n      if (seg.includes('*')) break\n      staticSegments.push(seg)\n    }\n    const start = path.join(scanPath, ...staticSegments)\n    if (!(await exists(start))) continue\n\n    const hasDoubleStar = pattern.includes('**')\n    const walkDepth = hasDoubleStar\n      ? scanDepth\n      : Math.max(0, segments.length - staticSegments.length - 1)\n\n    await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {\n      const rel = path.relative(scanPath, dir).split(path.sep).join('/')\n      if (minimatch(rel, pattern) && (await exists(path.join(dir, 'package.json')))) {\n        found.add(dir)\n      }\n    })\n  }\n\n  return [...found]\n}\n\n// Framework detection from package.json deps (ADR-074 §3). Mirrors the\n// installer dispatch precedence so a project the installer recognises as\n// Remix records `framework: 'remix'` on its ServiceNode. The static\n// extractor sees only manifest data, so detection is dep-presence based —\n// it doesn't crack open config files. Detection precedence: Next → Remix\n// → SvelteKit → Nuxt → Astro → vanilla Node.\nfunction detectJsFramework(pkg: PackageJson): string | undefined {\n  const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }\n  if (deps['next'] !== undefined) return 'next'\n  if (deps['remix'] !== undefined) return 'remix'\n  for (const k of Object.keys(deps)) {\n    if (k.startsWith('@remix-run/')) return 'remix'\n  }\n  if (deps['@sveltejs/kit'] !== undefined) return 'sveltekit'\n  if (deps['nuxt'] !== undefined) return 'nuxt'\n  if (deps['astro'] !== undefined) return 'astro'\n  return undefined\n}\n\n// A JS-manifest service is TypeScript when its package depends on `typescript`\n// or carries a tsconfig — otherwise we default to javascript. The per-file\n// extractor already routes by extension (`languageForPath`); this only sets the\n// ServiceNode's own `language` field, which the static walker can't read off a\n// single file. One readdir, conservative on the javascript side.\nasync function detectJsServiceLanguage(\n  dir: string,\n  pkg: PackageJson,\n): Promise<'typescript' | 'javascript'> {\n  const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }\n  if (deps['typescript'] !== undefined) return 'typescript'\n  const entries = await fs.readdir(dir).catch(() => [] as string[])\n  if (entries.some((name) => /^tsconfig(\\..+)?\\.json$/.test(name))) return 'typescript'\n  return 'javascript'\n}\n\nasync function discoverNodeService(\n  scanPath: string,\n  dir: string,\n): Promise<DiscoveredService | null> {\n  const pkgPath = path.join(dir, 'package.json')\n  if (!(await exists(pkgPath))) return null\n  let pkg: PackageJson\n  try {\n    pkg = await readJson<PackageJson>(pkgPath)\n  } catch (err) {\n    recordExtractionError('services', path.relative(scanPath, pkgPath), err)\n    return null\n  }\n  if (!pkg.name) return null\n  const framework = detectJsFramework(pkg)\n  const language = await detectJsServiceLanguage(dir, pkg)\n  const node: ServiceNode = {\n    id: serviceId(pkg.name),\n    type: NodeType.ServiceNode,\n    name: pkg.name,\n    language,\n    version: pkg.version,\n    dependencies: pkg.dependencies ?? {},\n    repoPath: path.relative(scanPath, dir),\n    ...(pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {}),\n    ...(framework ? { framework } : {}),\n  }\n  return { pkg, dir, node }\n}\n\nasync function discoverPyService(\n  scanPath: string,\n  dir: string,\n): Promise<DiscoveredService | null> {\n  const py = await discoverPythonService(dir)\n  if (!py) return null\n  const pkg = pythonToPackage(py)\n  const node: ServiceNode = {\n    id: serviceId(py.name),\n    type: NodeType.ServiceNode,\n    name: py.name,\n    language: 'python',\n    version: py.version,\n    dependencies: py.dependencies,\n    repoPath: path.relative(scanPath, dir),\n  }\n  return { pkg, dir, node }\n}\n\n// Phase 1 — discover service directories under scanPath. A service is any\n// directory containing a JS/TS manifest (`package.json`) or a Python manifest\n// (`pyproject.toml` / `requirements.txt` / `setup.py`). JS wins on tie.\n//\n// If the root `package.json` declares `workspaces`, those globs are\n// authoritative — we don't fall back to a free recursive walk. Otherwise we\n// walk recursively, depth-bounded by `NEAT_SCAN_DEPTH` (default 5), skipping\n// `IGNORED_DIRS` and anything matched by the root `.gitignore`.\n//\n// Two manifests sharing a `name` collapse to one node per ADR-010; the\n// duplicate logs a warning naming both paths.\nexport async function discoverServices(scanPath: string): Promise<DiscoveredService[]> {\n  const rootPkgPath = path.join(scanPath, 'package.json')\n  let rootPkg: RootPackageJson | null = null\n  if (await exists(rootPkgPath)) {\n    try {\n      rootPkg = await readJson<RootPackageJson>(rootPkgPath)\n    } catch (err) {\n      recordExtractionError(\n        'services workspaces',\n        path.relative(scanPath, rootPkgPath),\n        err,\n      )\n    }\n  }\n  const wsGlobs = rootPkg ? workspaceGlobs(rootPkg) : null\n\n  const candidateDirs: string[] = []\n  if (wsGlobs) {\n    candidateDirs.push(...(await expandWorkspaceGlobs(scanPath, wsGlobs)))\n  } else {\n    if (rootPkg && rootPkg.name) {\n      candidateDirs.push(scanPath)\n    } else if (await hasPythonManifest(scanPath)) {\n      // A Python project commonly keeps its manifest at the repo root with the\n      // code in a subpackage and no package.json anywhere. The walk only visits\n      // descendants, so without this the root manifest is never seen and the\n      // whole project discovers zero services.\n      candidateDirs.push(scanPath)\n    }\n    const ig = await loadGitignore(scanPath)\n    await walkDirs(\n      scanPath,\n      scanPath,\n      { maxDepth: parseScanDepth(), ig },\n      async (dir) => {\n        if (await exists(path.join(dir, 'package.json'))) {\n          candidateDirs.push(dir)\n        } else if (await hasPythonManifest(dir)) {\n          candidateDirs.push(dir)\n        }\n      },\n    )\n  }\n\n  candidateDirs.sort()\n\n  const seen = new Map<string, string>()\n  const out: DiscoveredService[] = []\n  for (const dir of candidateDirs) {\n    const service =\n      (await discoverNodeService(scanPath, dir)) ??\n      (await discoverPyService(scanPath, dir))\n    if (!service) continue\n\n    const existingDir = seen.get(service.node.name)\n    if (existingDir !== undefined) {\n      const a = path.relative(scanPath, existingDir) || '.'\n      const b = path.relative(scanPath, dir) || '.'\n      console.warn(\n        `[neat] duplicate package name \"${service.node.name}\" — keeping ${a}, ignoring ${b}`,\n      )\n      continue\n    }\n    seen.set(service.node.name, dir)\n    out.push(service)\n  }\n\n  // Owner extraction (ADR-054). CODEOWNERS first, package.json `author`\n  // fallback, undefined otherwise. Read once per discovery pass; the file\n  // is small and parsing it per-service would be wasteful.\n  const codeowners = await loadCodeowners(scanPath)\n  for (const service of out) {\n    const owner = await computeServiceOwner(codeowners, service.node.repoPath, service.dir)\n    if (owner !== undefined) service.node.owner = owner\n  }\n\n  return out\n}\n\nexport function addServiceNodes(graph: NeatGraph, services: DiscoveredService[]): number {\n  let nodesAdded = 0\n  for (const service of services) {\n    if (!graph.hasNode(service.node.id)) {\n      graph.addNode(service.node.id, { ...service.node, discoveredVia: 'static' })\n      nodesAdded++\n      continue\n    }\n    // OTel ingest may have auto-created a minimal node at this id. Merge per\n    // ADR-033 / identity contract: static fields override OTel-derived fields,\n    // and discoveredVia flips to 'merged' when both layers contributed.\n    const existing = graph.getNodeAttributes(service.node.id) as ServiceNode\n    const mergedDiscoveredVia: 'static' | 'otel' | 'merged' =\n      existing.discoveredVia === 'otel' ? 'merged' : 'static'\n    graph.replaceNodeAttributes(service.node.id, {\n      ...existing,\n      ...service.node,\n      discoveredVia: mergedDiscoveredVia,\n    })\n  }\n  return nodesAdded\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parse as parseYaml } from 'yaml'\nimport type { ServiceNode } from '@neat.is/types'\n\nexport interface PackageJson {\n  name: string\n  version?: string\n  dependencies?: Record<string, string>\n  devDependencies?: Record<string, string>\n  engines?: { node?: string }\n}\n\nexport interface DiscoveredService {\n  pkg: PackageJson\n  dir: string\n  node: ServiceNode\n}\n\nexport const SERVICE_FILE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.py'])\nexport const CONFIG_FILE_EXTENSIONS = new Set(['.yaml', '.yml'])\nexport const IGNORED_DIRS = new Set([\n  'node_modules',\n  '.git',\n  '.turbo',\n  'dist',\n  'build',\n  '.next',\n  // Python virtualenv shapes (issue #344). Walking into a venv pulls in the\n  // entire CPython stdlib + every installed package as if it were first-party\n  // service code — 20k+ files, none of which the user wrote. The shape names\n  // cover the three common venv tools (`venv` / `python -m venv`,\n  // `virtualenv`) and the tox + PEP 582 conventions.\n  '.venv',\n  'venv',\n  '__pypackages__',\n  '.tox',\n  // `site-packages` shows up nested inside venvs that don't carry one of the\n  // outer names (system Python on macOS, in-place pyenv layouts). Listing it\n  // here means we stop the walk at the boundary even when the wrapper dir\n  // wasn't recognisable.\n  'site-packages',\n])\n\n// Marker file `python -m venv` and `virtualenv` both drop at the venv root.\n// Used by the walkers to recognise venvs whose top-level directory doesn't\n// happen to match one of the canonical `IGNORED_DIRS` names (issue #344).\n// Cached per process to keep the recursion hot path off the fs after the\n// first walk; venv contents don't change inside one extract pass.\nconst PYVENV_MARKER_CACHE = new Map<string, boolean>()\n\nexport async function isPythonVenvDir(dir: string): Promise<boolean> {\n  const cached = PYVENV_MARKER_CACHE.get(dir)\n  if (cached !== undefined) return cached\n  try {\n    const stat = await fs.stat(path.join(dir, 'pyvenv.cfg'))\n    const ok = stat.isFile()\n    PYVENV_MARKER_CACHE.set(dir, ok)\n    return ok\n  } catch {\n    PYVENV_MARKER_CACHE.set(dir, false)\n    return false\n  }\n}\n\nexport function isConfigFile(name: string): { match: boolean; fileType: string } {\n  const ext = path.extname(name)\n  if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) }\n  // .env, .env.local, .env.production. Bare filename or any dotted-suffix\n  // variant; folder names get filtered upstream by walking files only.\n  // ADR-065 #4 filters .env.template / .env.example / .env.sample (and the\n  // dotted-suffix variants) at the producer level — those are documentation,\n  // not runtime config.\n  if (name === '.env' || name.startsWith('.env.')) {\n    if (isEnvTemplateFile(name)) return { match: false, fileType: '' }\n    // NEAT writes `.env.neat` itself during `--apply` (ADR-069 §4). Ingesting\n    // it would record our own instrumentation env as if it were the user's\n    // declared config — self-pollution. Skip it the same way the template\n    // filter skips onboarding docs.\n    if (isNeatAuthoredEnvFile(name)) return { match: false, fileType: '' }\n    return { match: true, fileType: 'env' }\n  }\n  return { match: false, fileType: '' }\n}\n\n// NEAT-authored artifacts the installer drops into the user's tree during\n// `--apply` (ADR-069). Extraction skips them: they're our own runtime hooks,\n// not first-party config or source. Ingesting them pollutes the graph with\n// NEAT's instrumentation as though the user had written it.\n//\n//   `.env.neat`         — per-package env carrying `OTEL_SERVICE_NAME`.\n//   `otel-init.{ext}`   — generated SDK bootstrap (js/cjs/mjs/ts), including\n//                         the framework variants (`src/otel-init.*`,\n//                         `server/plugins/otel-init.*`) — basename is stable.\nexport function isNeatAuthoredEnvFile(name: string): boolean {\n  return name === '.env.neat'\n}\n\nexport function isNeatAuthoredSourceFile(name: string): boolean {\n  return /^otel-init\\.(?:js|cjs|mjs|ts|tsx)$/i.test(name)\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// ADR-065 precision-filter helpers. Pre-emit gates inside the producer pass.\n// Filtered candidates are never written to the graph (idempotency intact).\n// ─────────────────────────────────────────────────────────────────────────\n\n// ADR-065 #1 — test-scope exclusion. Returns true when the file path matches\n// any test-scope pattern. Path is normalised to forward slashes before\n// matching so callers can pass either form.\n//\n// Patterns:\n//   - any segment named __tests__, __fixtures__, or integration-tests\n//   - basename matches *.spec.{ts,tsx,js,jsx,mjs,cjs,py}\n//   - basename matches *.test.{ts,tsx,js,jsx,mjs,cjs,py}\nexport function isTestPath(filePath: string): boolean {\n  const normalised = filePath.replace(/\\\\/g, '/')\n  const segments = normalised.split('/')\n  for (const seg of segments) {\n    if (seg === '__tests__' || seg === '__fixtures__' || seg === 'integration-tests') {\n      return true\n    }\n  }\n  const base = segments[segments.length - 1] ?? ''\n  return /\\.(spec|test)\\.(?:tsx?|jsx?|mjs|cjs|py)$/i.test(base)\n}\n\n// ADR-065 #4 — `.env.template` exclusion. Matches:\n//   .env.template / .env.example / .env.sample\n//   .env.*.template / .env.*.example / .env.*.sample\n// These are docs/onboarding artifacts, not runtime config. ConfigNodes are\n// bound to runtime existence (ADR-016); templates fail that test.\nexport function isEnvTemplateFile(name: string): boolean {\n  if (\n    name === '.env.template' ||\n    name === '.env.example' ||\n    name === '.env.sample'\n  ) {\n    return true\n  }\n  // `.env.*.template` / `.env.*.example` / `.env.*.sample`\n  return /^\\.env\\.[^.]+\\.(?:template|example|sample)$/i.test(name)\n}\n\n// ADR-065 #2 — comment-body exclusion. Replaces every JS/TS comment span in\n// the source with an equal-length run of spaces, preserving line/column for\n// downstream line-mapping. Strings that contain `//` sequences (URLs) are\n// preserved by tracking the string context as we scan.\n//\n// Not a full parser — good enough for the medusa-shape failures. The HTTP\n// extractor's AST walk already gets comment-awareness for free; this helper\n// is for the regex-based extractors (redis, kafka, aws, grpc).\nexport function maskCommentsInSource(src: string): string {\n  const len = src.length\n  const out: string[] = new Array(len)\n  let i = 0\n  // String context: ' \" ` (template) — open-quote char, 0 when not in a string.\n  let inString: string | 0 = 0\n  let escaped = false\n  while (i < len) {\n    const c = src[i]!\n    if (inString !== 0) {\n      out[i] = c\n      if (escaped) {\n        escaped = false\n      } else if (c === '\\\\') {\n        escaped = true\n      } else if (c === inString) {\n        inString = 0\n      }\n      i++\n      continue\n    }\n    if (c === '/' && i + 1 < len) {\n      const next = src[i + 1]!\n      if (next === '/') {\n        out[i] = ' '\n        out[i + 1] = ' '\n        let j = i + 2\n        while (j < len && src[j] !== '\\n') {\n          out[j] = ' '\n          j++\n        }\n        i = j\n        continue\n      }\n      if (next === '*') {\n        out[i] = ' '\n        out[i + 1] = ' '\n        let j = i + 2\n        while (j < len) {\n          if (src[j] === '\\n') {\n            out[j] = '\\n'\n            j++\n            continue\n          }\n          if (src[j] === '*' && j + 1 < len && src[j + 1] === '/') {\n            out[j] = ' '\n            out[j + 1] = ' '\n            j += 2\n            break\n          }\n          out[j] = ' '\n          j++\n        }\n        i = j\n        continue\n      }\n    }\n    out[i] = c\n    if (c === \"'\" || c === '\"' || c === '`') inString = c\n    i++\n  }\n  return out.join('')\n}\n\n// ADR-065 #5 — exact hostname match for cross-service URL inference. Returns\n// true if `urlString` looks like an actual URL — has an explicit `scheme://`\n// or starts with a scheme-relative `//` — and its hostname matches `host`\n// exactly (case-insensitive). No `.includes()` containment, and no bare-string\n// matching: a literal like `'admin-bundler'` would otherwise parse as\n// `http://admin-bundler` and match the basename of every service directory,\n// which is how the v0.3.3 medusa pre-check produced 279 false positives.\n//\n// Accepts a `host` that may include a port (`api.example.com:8080`); in that\n// case the URL's hostname AND port must both match.\nconst URL_LIKE = /^(?:[a-z][a-z0-9+.-]*:)?\\/\\//i\n\nexport function urlMatchesHost(urlString: string, host: string): boolean {\n  if (typeof urlString !== 'string' || urlString.length === 0) return false\n  // Require the literal to look like a URL — scheme + `://` or scheme-relative\n  // `//host`. Bare hostnames are rejected; they're the load-bearing source of\n  // false positives.\n  if (!URL_LIKE.test(urlString)) return false\n  const [wantedHost, wantedPort] = host.split(':')\n  let parsed: URL\n  try {\n    // For scheme-relative `//host/path`, prepend `http:` so URL accepts it.\n    const candidate = urlString.startsWith('//') ? `http:${urlString}` : urlString\n    parsed = new URL(candidate)\n  } catch {\n    return false\n  }\n  if (parsed.hostname.toLowerCase() !== (wantedHost ?? '').toLowerCase()) return false\n  if (wantedPort && parsed.port !== wantedPort) return false\n  return true\n}\n\n// Strip semver range prefixes (^, ~, >=, etc.) and bare \"v\" so the extracted\n// version is usable for compat checks. We don't try to resolve ranges to actual\n// installed versions — that's a published-lockfile concern, not extraction's job.\nexport function cleanVersion(raw: string | undefined): string | undefined {\n  if (!raw) return undefined\n  return raw.replace(/^[\\^~><=v\\s]+/, '').trim() || undefined\n}\n\nexport async function readJson<T>(filePath: string): Promise<T> {\n  const raw = await fs.readFile(filePath, 'utf8')\n  return JSON.parse(raw) as T\n}\n\nexport async function readYaml<T>(filePath: string): Promise<T> {\n  const raw = await fs.readFile(filePath, 'utf8')\n  return parseYaml(raw) as T\n}\n\nexport async function exists(p: string): Promise<boolean> {\n  try {\n    await fs.access(p)\n    return true\n  } catch {\n    return false\n  }\n}\n\n// Thin re-export so existing callers (calls/, configs.ts, databases/, infra/)\n// keep their import path. Wire format lives in @neat.is/types/identity.ts per\n// ADR-029.\nexport { extractedEdgeId as makeEdgeId } from '@neat.is/types'\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parse as parseToml } from 'smol-toml'\nimport { exists, type PackageJson } from './shared.js'\n\n// Lines like `psycopg2==2.7.0`, `psycopg2 == 2.7.0`, `psycopg2[extras]==2.7`,\n// or `psycopg2~=2.7,<3`. We capture the package name and the first version\n// that follows an `==` operator. Anything else (range, no pin, no version) is\n// recorded with an empty version — the compat matrix's semver coercer treats\n// those as \"can't reason\" and under-flags rather than over-flags.\nconst REQUIREMENT_LINE = /^\\s*([A-Za-z0-9_.-]+)(?:\\[[^\\]]*\\])?\\s*(?:(==)\\s*([A-Za-z0-9_.+-]+))?/\n\nfunction parseRequirementsTxt(content: string): Record<string, string> {\n  const out: Record<string, string> = {}\n  for (const rawLine of content.split('\\n')) {\n    const line = rawLine.split('#')[0]?.trim()\n    if (!line) continue\n    if (line.startsWith('-')) continue // -r requirements-dev.txt etc.\n    const match = REQUIREMENT_LINE.exec(line)\n    if (!match) continue\n    const name = match[1]!.toLowerCase()\n    const version = match[3] ?? ''\n    out[name] = version\n  }\n  return out\n}\n\ninterface PyProjectFile {\n  project?: {\n    name?: string\n    version?: string\n    dependencies?: string[]\n  }\n  tool?: {\n    poetry?: {\n      name?: string\n      version?: string\n      dependencies?: Record<string, string | { version?: string }>\n    }\n  }\n}\n\nfunction depsFromPyProject(pyproject: PyProjectFile): Record<string, string> {\n  const out: Record<string, string> = {}\n\n  // PEP 621 — [project] dependencies = [\"psycopg2==2.7.0\", \"requests\"]\n  for (const entry of pyproject.project?.dependencies ?? []) {\n    const match = REQUIREMENT_LINE.exec(entry)\n    if (!match) continue\n    out[match[1]!.toLowerCase()] = match[3] ?? ''\n  }\n\n  // Poetry — [tool.poetry.dependencies] psycopg2 = \"2.7.0\"\n  const poetryDeps = pyproject.tool?.poetry?.dependencies ?? {}\n  for (const [name, value] of Object.entries(poetryDeps)) {\n    if (name.toLowerCase() === 'python') continue\n    const raw = typeof value === 'string' ? value : (value?.version ?? '')\n    out[name.toLowerCase()] = raw.replace(/^[\\^~><=v\\s]+/, '')\n  }\n  return out\n}\n\nexport interface PythonService {\n  name: string\n  version?: string\n  dependencies: Record<string, string>\n}\n\n// Detect a Python service by the conventional manifest files. We try\n// pyproject.toml first because it can name the package; fallback to the\n// directory name when only requirements.txt or setup.py is present.\nexport async function discoverPythonService(serviceDir: string): Promise<PythonService | null> {\n  const pyprojectPath = path.join(serviceDir, 'pyproject.toml')\n  const requirementsPath = path.join(serviceDir, 'requirements.txt')\n  const setupPath = path.join(serviceDir, 'setup.py')\n\n  const hasPyproject = await exists(pyprojectPath)\n  const hasRequirements = await exists(requirementsPath)\n  const hasSetup = await exists(setupPath)\n  if (!hasPyproject && !hasRequirements && !hasSetup) return null\n\n  let name = path.basename(serviceDir)\n  let version: string | undefined\n  const dependencies: Record<string, string> = {}\n\n  if (hasPyproject) {\n    const raw = await fs.readFile(pyprojectPath, 'utf8')\n    const pyproject = parseToml(raw) as PyProjectFile\n    name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name\n    version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? undefined\n    Object.assign(dependencies, depsFromPyProject(pyproject))\n  }\n\n  if (hasRequirements) {\n    const raw = await fs.readFile(requirementsPath, 'utf8')\n    Object.assign(dependencies, parseRequirementsTxt(raw))\n  }\n\n  return { name, version, dependencies }\n}\n\n// Build the same `pkg`-shaped shim the JS path uses so downstream phases\n// (databases, calls, etc.) can keep reading service.pkg.dependencies and\n// service.pkg.name without caring which language produced the service.\nexport function pythonToPackage(service: PythonService): PackageJson {\n  return {\n    name: service.name,\n    version: service.version,\n    dependencies: service.dependencies,\n  }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { minimatch } from 'minimatch'\nimport { exists, readJson } from './shared.js'\n\nexport interface CodeownersRule {\n  pattern: string\n  owners: string\n}\n\nexport interface CodeownersFile {\n  rules: CodeownersRule[]\n}\n\ninterface PackageJsonAuthor {\n  author?: string | { name?: string }\n}\n\n// Read CODEOWNERS at <scanPath>/CODEOWNERS first, then <scanPath>/.github/CODEOWNERS.\n// Returns null when neither exists. ADR-054 #2.1.\nexport async function loadCodeowners(scanPath: string): Promise<CodeownersFile | null> {\n  const candidates = [\n    path.join(scanPath, 'CODEOWNERS'),\n    path.join(scanPath, '.github', 'CODEOWNERS'),\n  ]\n  for (const file of candidates) {\n    if (await exists(file)) {\n      const raw = await fs.readFile(file, 'utf8')\n      return parseCodeowners(raw)\n    }\n  }\n  return null\n}\n\nfunction parseCodeowners(raw: string): CodeownersFile {\n  const rules: CodeownersRule[] = []\n  for (const line of raw.split('\\n')) {\n    const trimmed = line.trim()\n    if (!trimmed || trimmed.startsWith('#')) continue\n    const match = /^(\\S+)\\s+(.+)$/.exec(trimmed)\n    if (!match) continue\n    rules.push({ pattern: match[1]!, owners: match[2]!.trim() })\n  }\n  return { rules }\n}\n\n// First matching pattern wins; returns the literal RHS or null. ADR-054 #2.1.\n// Pattern matcher is minimal per ADR-054 #6 — handles `*`, `**`, and exact\n// paths; not a full gitignore-style parser.\nexport function matchOwner(file: CodeownersFile, repoPath: string): string | null {\n  const normalized = repoPath.split(path.sep).join('/')\n  for (const rule of file.rules) {\n    if (matchesPattern(rule.pattern, normalized)) return rule.owners\n  }\n  return null\n}\n\nfunction matchesPattern(rawPattern: string, repoPath: string): boolean {\n  let pattern = rawPattern.startsWith('/') ? rawPattern.slice(1) : rawPattern\n  if (pattern === '*') return !repoPath.includes('/')\n  if (pattern === '**' || pattern === '') return true\n  // Trailing slash means \"everything in this directory\".\n  if (pattern.endsWith('/')) pattern = pattern + '**'\n  if (minimatch(repoPath, pattern, { dot: true })) return true\n  // A pattern that names a directory should match files beneath it too.\n  if (!pattern.includes('*') && minimatch(repoPath, pattern + '/**', { dot: true })) return true\n  return false\n}\n\n// Read <serviceDir>/package.json and return the `author` field as a literal\n// string. Accepts either string form (\"Cem D <cem@example.com>\") or object\n// form ({ name: 'Cem D' }). Returns null when missing or unparseable.\n// ADR-054 #2.2.\nexport async function readPackageJsonAuthor(serviceDir: string): Promise<string | null> {\n  const pkgPath = path.join(serviceDir, 'package.json')\n  if (!(await exists(pkgPath))) return null\n  try {\n    const pkg = await readJson<PackageJsonAuthor>(pkgPath)\n    if (!pkg.author) return null\n    if (typeof pkg.author === 'string') return pkg.author\n    if (typeof pkg.author === 'object' && typeof pkg.author.name === 'string') return pkg.author.name\n    return null\n  } catch {\n    return null\n  }\n}\n\n// Compute owner per ADR-054 priority: CODEOWNERS first, package.json `author`\n// fallback, undefined otherwise. Literal source value, no normalization (#3).\nexport async function computeServiceOwner(\n  codeowners: CodeownersFile | null,\n  repoPath: string | undefined,\n  serviceDir: string,\n): Promise<string | undefined> {\n  if (codeowners && repoPath !== undefined) {\n    const owner = matchOwner(codeowners, repoPath)\n    if (owner) return owner\n  }\n  const author = await readPackageJsonAuthor(serviceDir)\n  return author ?? undefined\n}\n","// ADR-065 — loud failure mode for static extraction.\n//\n// Per-file extraction failures used to land as a single `console.warn` line\n// with no aggregate count and no on-disk record. The 2026-05-12 medusa\n// experiment ran `neat init` against ~90 files that all silently failed\n// extraction with \"Invalid argument\"; the snapshot shipped with those files\n// missing, the user had no signal it had happened, and the divergence query\n// couldn't surface gaps it didn't know it had.\n//\n// This module is the failure-mode plumbing: a process-local sink that every\n// producer appends to when a per-file parse fails, with helpers to drain the\n// sink to `<projectDir>/neat-out/errors.ndjson` and to surface the aggregate\n// count in init / watch banners.\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface ExtractionError {\n  // Producer name (e.g. \"http\", \"services\", \"infra docker-compose\"). Stable\n  // human-readable identifier for logs and contract assertions.\n  producer: string\n  // Absolute or relative path of the file that failed. Stored verbatim from\n  // the call site; no normalisation here.\n  file: string\n  // The error's `.message`. Stringified if the throw value wasn't an Error.\n  error: string\n  // Optional stack trace (captured at the call site). Useful for diagnosing\n  // tree-sitter \"Invalid argument\" cases where the message alone is generic.\n  stack?: string\n  // ISO timestamp of when the error was recorded.\n  ts: string\n  // Discriminator for `errors.ndjson` consumers — separates extract failures\n  // from OTel error events that share the same file (ADR-033).\n  source: 'extract'\n}\n\nconst sink: ExtractionError[] = []\n\n// Record a per-file extraction failure. Preserves the visible warn line so\n// existing scripts/CI consumers still see a per-file message; appends to the\n// process-local sink so the aggregate count + sidecar write can fire later.\nexport function recordExtractionError(\n  producer: string,\n  file: string,\n  err: unknown,\n): void {\n  const e = err instanceof Error ? err : new Error(String(err))\n  sink.push({\n    producer,\n    file,\n    error: e.message,\n    stack: e.stack,\n    ts: new Date().toISOString(),\n    source: 'extract',\n  })\n  // Visible warn preserved (pre-ADR-065 callers logged this). Banners\n  // aggregate, but per-file context still useful for tail -f sessions.\n  console.warn(`[neat] ${producer} skipped ${file}: ${e.message}`)\n}\n\n// Drain all queued errors. Idempotent — subsequent calls return an empty\n// array until new errors land. Callers (extractFromDirectory) drain at start\n// to clear stale state from prior runs, then drain again at end to collect\n// the pass's failures.\nexport function drainExtractionErrors(): ExtractionError[] {\n  return sink.splice(0, sink.length)\n}\n\n// Read-only count of currently-queued errors. Used by callers that want the\n// count without consuming the sink (the banner case — we want the count for\n// display and the entries for `errors.ndjson`).\nexport function pendingExtractionErrors(): number {\n  return sink.length\n}\n\n// Append the drained entries to `<projectDir>/neat-out/errors.ndjson`. The\n// file is shared with OTel error events (per ADR-033); the `source: 'extract'`\n// discriminator separates them for consumers. Creates the directory if\n// missing. Append-only — never rewritten.\nexport async function writeExtractionErrors(\n  errors: ExtractionError[],\n  errorsPath: string,\n): Promise<void> {\n  if (errors.length === 0) return\n  await fs.mkdir(path.dirname(errorsPath), { recursive: true })\n  const lines = errors.map((e) => JSON.stringify(e)).join('\\n') + '\\n'\n  await fs.appendFile(errorsPath, lines, 'utf8')\n}\n\n// ADR-065 — `NEAT_STRICT_EXTRACTION=1` makes any per-file extraction failure\n// cause the calling command to exit non-zero. Default is forgiving (banner\n// only). The check is at the caller (cli.ts / server.ts / watch.ts) so the\n// extraction phase itself stays library-shaped.\nexport function isStrictExtractionEnabled(): boolean {\n  const raw = process.env.NEAT_STRICT_EXTRACTION\n  return raw === '1' || raw === 'true'\n}\n\n// Format the unconditional summary banner. Zero errors is a positive signal\n// (\"0 files skipped\") so callers print this even on clean runs.\nexport function formatExtractionBanner(count: number): string {\n  if (count === 1) return `[neat] 1 file skipped due to parse errors`\n  return `[neat] ${count} files skipped due to parse errors`\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// ADR-066 — precision-floor drop accounting.\n//\n// EXTRACTED candidates whose graded confidence falls below\n// NEAT_EXTRACTED_PRECISION_FLOOR (default 0.7) are computed but never added\n// to the graph. The drop count surfaces on the extraction banner;\n// NEAT_EXTRACTED_REJECTED_LOG=1 routes the per-candidate detail to\n// `<projectDir>/neat-out/rejected.ndjson`. The default keeps the sidecar\n// surface quiet.\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface DroppedExtractedEdge {\n  source: string\n  target: string\n  type: string\n  confidence: number\n  confidenceKind: string\n  evidence: { file: string; line?: number; snippet?: string }\n}\n\nconst droppedSink: DroppedExtractedEdge[] = []\n\nexport function noteExtractedDropped(edge: DroppedExtractedEdge): void {\n  droppedSink.push(edge)\n}\n\nexport function drainDroppedExtracted(): DroppedExtractedEdge[] {\n  return droppedSink.splice(0, droppedSink.length)\n}\n\nexport function pendingDroppedExtracted(): number {\n  return droppedSink.length\n}\n\nexport function isRejectedLogEnabled(): boolean {\n  const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG\n  return raw === '1' || raw === 'true'\n}\n\nexport async function writeRejectedExtracted(\n  drops: DroppedExtractedEdge[],\n  rejectedPath: string,\n): Promise<void> {\n  if (drops.length === 0) return\n  await fs.mkdir(path.dirname(rejectedPath), { recursive: true })\n  const lines = drops.map((d) => JSON.stringify({ ...d, ts: new Date().toISOString() })).join('\\n') + '\\n'\n  await fs.appendFile(rejectedPath, lines, 'utf8')\n}\n\nexport function formatPrecisionFloorBanner(count: number): string {\n  if (count === 1) return `[neat] 1 extracted edge dropped below precision floor`\n  return `[neat] ${count} extracted edges dropped below precision floor`\n}\n","import path from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport { parseAllDocuments } from 'yaml'\nimport type { ServiceNode } from '@neat.is/types'\nimport { NodeType } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport { recordExtractionError } from './errors.js'\nimport {\n  CONFIG_FILE_EXTENSIONS,\n  IGNORED_DIRS,\n  exists,\n  isPythonVenvDir,\n  readYaml,\n  type DiscoveredService,\n} from './shared.js'\n\n// Populate ServiceNode.aliases from sources that the OTel layer is likely to\n// see in span attributes:\n//\n//   - docker-compose service names (compose-DNS).\n//   - Dockerfile LABEL values that name the service.\n//   - k8s metadata.name (and the cluster-DNS variants) for Service /\n//     Deployment / StatefulSet whose name matches a known service.\n//\n// resolveServiceId in ingest.ts checks aliases before falling back to a\n// FRONTIER placeholder; promoteFrontierNodes uses them to retire stale\n// placeholders once they map to a real service.\n\ninterface ComposeService {\n  container_name?: string\n  hostname?: string\n  networks?: string[] | Record<string, unknown>\n}\n\ninterface ComposeFile {\n  services?: Record<string, ComposeService>\n}\n\ninterface K8sDoc {\n  kind?: string\n  metadata?: {\n    name?: string\n    namespace?: string\n    labels?: Record<string, string>\n  }\n  spec?: {\n    selector?: {\n      app?: string\n      matchLabels?: Record<string, string>\n    }\n  }\n}\n\nconst K8S_KINDS_WITH_HOSTNAMES = new Set([\n  'Service',\n  'Deployment',\n  'StatefulSet',\n  'DaemonSet',\n])\n\nfunction addAliases(graph: NeatGraph, serviceId: string, candidates: Iterable<string>): void {\n  if (!graph.hasNode(serviceId)) return\n  const node = graph.getNodeAttributes(serviceId) as ServiceNode & { type?: string }\n  if (node.type !== NodeType.ServiceNode) return\n  const set = new Set(node.aliases ?? [])\n  for (const c of candidates) {\n    if (!c) continue\n    if (c === node.name) continue\n    set.add(c)\n  }\n  if (set.size === 0) return\n  const updated: ServiceNode = { ...node, aliases: [...set].sort() }\n  graph.replaceNodeAttributes(serviceId, updated)\n}\n\nfunction indexServicesByName(services: DiscoveredService[]): Map<string, string> {\n  const map = new Map<string, string>()\n  for (const s of services) {\n    map.set(s.node.name, s.node.id)\n    map.set(path.basename(s.dir), s.node.id)\n  }\n  return map\n}\n\nasync function collectComposeAliases(\n  graph: NeatGraph,\n  scanPath: string,\n  serviceIndex: Map<string, string>,\n): Promise<void> {\n  let composePath: string | null = null\n  for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n    const abs = path.join(scanPath, name)\n    if (await exists(abs)) {\n      composePath = abs\n      break\n    }\n  }\n  if (!composePath) return\n\n  let compose: ComposeFile\n  try {\n    compose = await readYaml<ComposeFile>(composePath)\n  } catch (err) {\n    recordExtractionError(\n      'aliases compose',\n      path.relative(scanPath, composePath),\n      err,\n    )\n    return\n  }\n  if (!compose?.services) return\n\n  for (const [composeName, svc] of Object.entries(compose.services)) {\n    const serviceId = serviceIndex.get(composeName)\n    if (!serviceId) continue\n    const aliases = new Set<string>([composeName])\n    if (svc.container_name) aliases.add(svc.container_name)\n    if (svc.hostname) aliases.add(svc.hostname)\n    addAliases(graph, serviceId, aliases)\n  }\n}\n\nconst LABEL_KEYS = new Set([\n  'service',\n  'service.name',\n  'app',\n  'app.name',\n  'com.docker.compose.service',\n  'org.opencontainers.image.title',\n])\n\nfunction parseDockerfileLabels(content: string): string[] {\n  const out: string[] = []\n  // Support `LABEL key=value`, `LABEL key=\"value with spaces\"`, and the\n  // multi-pair form `LABEL k1=v1 k2=v2`. We don't try to honour line\n  // continuations — the common single-line form is enough.\n  const lineRegex = /^\\s*label\\s+(.+)$/i\n  for (const raw of content.split('\\n')) {\n    const m = lineRegex.exec(raw)\n    if (!m) continue\n    const rest = m[1]!\n    const pairRegex = /([\\w.-]+)\\s*=\\s*(\"([^\"]*)\"|'([^']*)'|([^\\s]+))/g\n    let pair: RegExpExecArray | null\n    while ((pair = pairRegex.exec(rest)) !== null) {\n      const key = pair[1]!.toLowerCase()\n      if (!LABEL_KEYS.has(key)) continue\n      const value = pair[3] ?? pair[4] ?? pair[5] ?? ''\n      if (value) out.push(value)\n    }\n  }\n  return out\n}\n\nasync function collectDockerfileAliases(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<void> {\n  for (const service of services) {\n    const dockerfilePath = path.join(service.dir, 'Dockerfile')\n    if (!(await exists(dockerfilePath))) continue\n    let content: string\n    try {\n      content = await fs.readFile(dockerfilePath, 'utf8')\n    } catch (err) {\n      recordExtractionError('aliases dockerfile', dockerfilePath, err)\n      continue\n    }\n    const aliases = parseDockerfileLabels(content)\n    if (aliases.length > 0) addAliases(graph, service.node.id, aliases)\n  }\n}\n\nasync function walkYamlFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n  if (depth > max) return []\n  const out: string[] = []\n  const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n  for (const entry of entries) {\n    if (entry.isDirectory()) {\n      if (IGNORED_DIRS.has(entry.name)) continue\n      const child = path.join(start, entry.name)\n      if (await isPythonVenvDir(child)) continue\n      out.push(...(await walkYamlFiles(child, depth + 1, max)))\n    } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path.extname(entry.name))) {\n      out.push(path.join(start, entry.name))\n    }\n  }\n  return out\n}\n\nfunction k8sHostnames(name: string, namespace: string | undefined): string[] {\n  const ns = namespace ?? 'default'\n  return [\n    name,\n    `${name}.${ns}`,\n    `${name}.${ns}.svc`,\n    `${name}.${ns}.svc.cluster.local`,\n  ]\n}\n\nfunction k8sServiceTarget(\n  doc: K8sDoc,\n  byName: Map<string, string>,\n): string | null {\n  // For `Service` resources, the target is whatever app the spec selects, not\n  // necessarily the Service's own metadata.name. We prefer that mapping; if no\n  // selector, fall back to the metadata.name match.\n  const selector = doc.spec?.selector\n  const selectorApp = selector?.app ?? selector?.matchLabels?.app\n  if (selectorApp && byName.has(selectorApp)) return byName.get(selectorApp)!\n\n  const labelApp = doc.metadata?.labels?.app\n  if (labelApp && byName.has(labelApp)) return byName.get(labelApp)!\n\n  const metaName = doc.metadata?.name\n  if (metaName && byName.has(metaName)) return byName.get(metaName)!\n\n  return null\n}\n\nasync function collectK8sAliases(\n  graph: NeatGraph,\n  scanPath: string,\n  serviceIndex: Map<string, string>,\n): Promise<void> {\n  const files = await walkYamlFiles(scanPath)\n  for (const file of files) {\n    const content = await fs.readFile(file, 'utf8')\n    let docs: K8sDoc[]\n    try {\n      docs = parseAllDocuments(content).map((d) => d.toJSON() as K8sDoc)\n    } catch {\n      continue\n    }\n    for (const doc of docs) {\n      if (!doc?.kind || !doc.metadata?.name) continue\n      if (!K8S_KINDS_WITH_HOSTNAMES.has(doc.kind)) continue\n      const target = k8sServiceTarget(doc, serviceIndex)\n      if (!target) continue\n      addAliases(graph, target, k8sHostnames(doc.metadata.name, doc.metadata.namespace))\n    }\n  }\n}\n\nexport async function addServiceAliases(\n  graph: NeatGraph,\n  scanPath: string,\n  services: DiscoveredService[],\n): Promise<void> {\n  const byName = indexServicesByName(services)\n  await collectComposeAliases(graph, scanPath, byName)\n  await collectDockerfileAliases(graph, services)\n  await collectK8sAliases(graph, scanPath, byName)\n}\n","import type { NeatGraph } from '../graph.js'\nimport type { DiscoveredService } from './shared.js'\nimport { ensureFileNode, walkSourceFiles, toPosix } from './calls/shared.js'\nimport path from 'node:path'\n\n// Phase 1 — unconditional file enumeration (ADR-092, file-awareness.md §1).\n// Walks every source file matching SERVICE_FILE_EXTENSIONS within each service\n// and emits a FileNode + service ──CONTAINS──▶ file edge, regardless of whether\n// any call pattern fires from the file later.\nexport async function addFiles(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const filePaths = await walkSourceFiles(service.dir)\n    for (const filePath of filePaths) {\n      const relPath = toPosix(path.relative(service.dir, filePath))\n      const { nodesAdded: n, edgesAdded: e } = ensureFileNode(\n        graph,\n        service.pkg.name,\n        service.node.id,\n        relPath,\n      )\n      nodesAdded += n\n      edgesAdded += e\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { EdgeEvidence, ExtractedConfidenceKind, FileNode, GraphEdge } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForExtracted,\n  extractedEdgeId,\n  fileId,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport {\n  IGNORED_DIRS,\n  SERVICE_FILE_EXTENSIONS,\n  isNeatAuthoredSourceFile,\n  isPythonVenvDir,\n  type DiscoveredService,\n} from '../shared.js'\n\n// Host → owning ServiceNode id, the cross-service resolution the HTTP call-site\n// producers share (ADR-065 #5, ADR-119). A service is reachable by either its\n// directory basename or its manifest name; both map to the same node id. Reused\n// by http.ts (host-level CALLS edges) and route-match.ts (client↔route\n// matching) so the two producers resolve a URL's host to a service identically.\nexport interface ServiceHostIndex {\n  knownHosts: Set<string>\n  hostToNodeId: Map<string, string>\n}\n\nexport function buildServiceHostIndex(services: DiscoveredService[]): ServiceHostIndex {\n  const knownHosts = new Set<string>()\n  const hostToNodeId = new Map<string, string>()\n  for (const service of services) {\n    const base = path.basename(service.dir)\n    knownHosts.add(base)\n    knownHosts.add(service.pkg.name)\n    hostToNodeId.set(base, service.node.id)\n    hostToNodeId.set(service.pkg.name, service.node.id)\n  }\n  return { knownHosts, hostToNodeId }\n}\n\nexport interface SourceFile {\n  path: string\n  content: string\n}\n\nexport interface ExternalEndpoint {\n  // Stable id of the InfraNode this evidence implies. Format\n  // `infra:<kind>:<name>` so the orchestrator can dedupe across services.\n  infraId: string\n  // Display name on the InfraNode (e.g., \"orders\" for kafka-topic:orders).\n  name: string\n  kind: string\n  edgeType: 'CALLS' | 'PUBLISHES_TO' | 'CONSUMES_FROM'\n  evidence: EdgeEvidence\n  // Confidence grade per ADR-066 — set by the per-shape detector. The\n  // orchestrator (calls/index.ts) writes this onto the EXTRACTED edge and\n  // applies the precision floor before adding the edge to the graph.\n  confidenceKind: ExtractedConfidenceKind\n}\n\nexport async function walkSourceFiles(dir: string): Promise<string[]> {\n  const out: string[] = []\n  async function walk(current: string): Promise<void> {\n    const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => [])\n    for (const entry of entries) {\n      const full = path.join(current, entry.name)\n      if (entry.isDirectory()) {\n        if (IGNORED_DIRS.has(entry.name)) continue\n        if (await isPythonVenvDir(full)) continue\n        await walk(full)\n      } else if (\n        entry.isFile() &&\n        SERVICE_FILE_EXTENSIONS.has(path.extname(entry.name)) &&\n        // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it\n        // would attribute our instrumentation imports to the user's service.\n        !isNeatAuthoredSourceFile(entry.name)\n      ) {\n        out.push(full)\n      }\n    }\n  }\n  await walk(dir)\n  return out\n}\n\nexport async function loadSourceFiles(dir: string): Promise<SourceFile[]> {\n  const paths = await walkSourceFiles(dir)\n  const out: SourceFile[] = []\n  for (const p of paths) {\n    try {\n      const content = await fs.readFile(p, 'utf8')\n      out.push({ path: p, content })\n    } catch {\n      // unreadable, skip\n    }\n  }\n  return out\n}\n\n// Locate the line of the first occurrence of `needle` in `text`, 1-indexed.\n// Falls back to line 1 if the needle isn't found verbatim — better to point at\n// the file than to drop the evidence entirely.\nexport function lineOf(text: string, needle: string): number {\n  const idx = text.indexOf(needle)\n  if (idx < 0) return 1\n  return text.slice(0, idx).split('\\n').length\n}\n\nexport function snippet(text: string, line: number): string {\n  const lines = text.split('\\n')\n  return (lines[line - 1] ?? '').trim()\n}\n\n// Forward-slash a path so a FileNode id is byte-stable across platforms (the\n// `relPath` segment of `file:<service>:<relPath>` must not vary by OS).\nexport function toPosix(p: string): string {\n  return p.split('\\\\').join('/')\n}\n\n// Extension → language tag for a FileNode. Returns undefined for extensions we\n// don't name rather than guessing — evidence is never fabricated (§6).\nexport function languageForPath(relPath: string): string | undefined {\n  switch (path.extname(relPath).toLowerCase()) {\n    case '.py':\n      return 'python'\n    case '.ts':\n    case '.tsx':\n      return 'typescript'\n    case '.js':\n    case '.jsx':\n    case '.mjs':\n    case '.cjs':\n      return 'javascript'\n    default:\n      return undefined\n  }\n}\n\n// File-first emission (file-awareness.md §1–2). Ensure the FileNode for\n// `relPath` and the owning `service ──CONTAINS──▶ file` edge both exist, then\n// return the FileNode id so the caller can originate a relationship from it.\n// `relPath` must already be service-relative and forward-slashed (use toPosix).\n// CONTAINS is structural ownership — graded at the 'structural' tier like\n// CONFIGURED_BY, never a flat value. Idempotent: re-running extraction over an\n// unchanged file is a no-op.\nexport function ensureFileNode(\n  graph: NeatGraph,\n  serviceName: string,\n  serviceNodeId: string,\n  relPath: string,\n): { fileNodeId: string; nodesAdded: number; edgesAdded: number } {\n  let nodesAdded = 0\n  let edgesAdded = 0\n  const fileNodeId = fileId(serviceName, relPath)\n  if (!graph.hasNode(fileNodeId)) {\n    const language = languageForPath(relPath)\n    const node: FileNode = {\n      id: fileNodeId,\n      type: NodeType.FileNode,\n      service: serviceName,\n      path: relPath,\n      ...(language ? { language } : {}),\n      discoveredVia: 'static',\n    }\n    graph.addNode(fileNodeId, node)\n    nodesAdded++\n  }\n  const containsId = extractedEdgeId(serviceNodeId, fileNodeId, EdgeType.CONTAINS)\n  if (!graph.hasEdge(containsId)) {\n    const edge: GraphEdge = {\n      id: containsId,\n      source: serviceNodeId,\n      target: fileNodeId,\n      type: EdgeType.CONTAINS,\n      provenance: Provenance.EXTRACTED,\n      confidence: confidenceForExtracted('structural'),\n      evidence: { file: relPath },\n    }\n    graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge)\n    edgesAdded++\n  }\n  return { fileNodeId, nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport Parser from 'tree-sitter'\nimport JavaScript from 'tree-sitter-javascript'\nimport Python from 'tree-sitter-python'\nimport type { GraphEdge } from '@neat.is/types'\nimport {\n  EdgeType,\n  Provenance,\n  confidenceForExtracted,\n  extractedEdgeId,\n  fileId,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport { isTestPath, type DiscoveredService } from './shared.js'\nimport { recordExtractionError } from './errors.js'\nimport { loadSourceFiles, toPosix } from './calls/shared.js'\n\n// Phase 2 — import graph extraction (ADR-092, file-awareness.md §10). Walks\n// every source file's AST for import / require statements and emits IMPORTS\n// edges between FileNodes within the same service. Cross-service and\n// unresolvable specifiers are silent skips — Phase 3's CALLS producers cover\n// the cross-service case where an external-call pattern matches.\n\n// Same chunked-parse approach as calls/shared.ts — tree-sitter's bare string\n// path throws \"Invalid argument\" once the source clears ~32K code units. The\n// callback form streams the source in bounded slices instead.\nconst PARSE_CHUNK = 16384\n\nfunction parseSource(parser: Parser, source: string): Parser.Tree {\n  return parser.parse((index: number) =>\n    index >= source.length ? '' : source.slice(index, index + PARSE_CHUNK),\n  )\n}\n\nfunction makeJsParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(JavaScript)\n  return p\n}\n\nfunction makePyParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(Python)\n  return p\n}\n\n// ── string-literal text ────────────────────────────────────────────────────\n\n// The interior text of a string literal node, stripped of quote characters.\n// tree-sitter-javascript wraps the content in a `string_fragment` child;\n// fall back to slicing the raw text when that shape isn't present (e.g. an\n// empty string literal has no fragment child at all).\nfunction stringLiteralText(node: Parser.SyntaxNode): string | null {\n  for (let i = 0; i < node.childCount; i++) {\n    const child = node.child(i)\n    if (child?.type === 'string_fragment') return child.text\n  }\n  const raw = node.text\n  if (raw.length >= 2) return raw.slice(1, -1)\n  return raw.length === 0 ? null : ''\n}\n\nfunction clipSnippet(text: string): string {\n  const oneLine = text.split('\\n')[0] ?? text\n  return oneLine.length > 120 ? oneLine.slice(0, 120) : oneLine\n}\n\n// ── JS / TS import collection ──────────────────────────────────────────────\n\ninterface RawImport {\n  specifier: string\n  line: number // 1-indexed\n  snippet: string\n}\n\n// Walks the AST for `import ... from 'spec'` / `import 'spec'` (ES modules)\n// and `require('spec')` (CommonJS). Doesn't recurse into import_statement —\n// its only string child is the source specifier, never a nested import.\nfunction collectJsImports(node: Parser.SyntaxNode, out: RawImport[]): void {\n  if (node.type === 'import_statement') {\n    const source = node.childForFieldName('source')\n    if (source) {\n      const specifier = stringLiteralText(source)\n      if (specifier) {\n        out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) })\n      }\n    }\n    return\n  }\n\n  if (node.type === 'call_expression') {\n    const fn = node.childForFieldName('function')\n    if (fn?.type === 'identifier' && fn.text === 'require') {\n      const args = node.childForFieldName('arguments')\n      const firstArg = args?.namedChild(0)\n      if (firstArg?.type === 'string') {\n        const specifier = stringLiteralText(firstArg)\n        if (specifier) {\n          out.push({ specifier, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) })\n        }\n      }\n    }\n  }\n\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) collectJsImports(child, out)\n  }\n}\n\n// ── Python import collection ───────────────────────────────────────────────\n\ninterface RawPyImport {\n  modulePath: string // dotted path with the leading dots stripped, e.g. 'utils.auth'\n  level: number // count of leading dots; 0 = absolute import\n  names: string[] // the imported names after `import` — each may be a submodule\n  line: number\n  snippet: string\n}\n\n// Collects the imported names from the tail of a `from`-import — the part\n// after the `import` keyword. Pushes each `name` (and the original name of an\n// `as`-aliased import); descends into a parenthesised list so `from app import\n// (config, db)` reads the same as the unparenthesised form. A wildcard `*`\n// has no name and is left for the module-file fallback in resolution.\nfunction collectImportedNames(node: Parser.SyntaxNode, out: string[]): void {\n  if (node.type === 'aliased_import') {\n    const nameNode = node.childForFieldName('name')\n    if (nameNode) out.push(nameNode.text)\n    return\n  }\n  if (node.type === 'dotted_name') {\n    out.push(node.text)\n    return\n  }\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) collectImportedNames(child, out)\n  }\n}\n\n// Walks the AST for `from X import a, b` / `from .X import a`. Bare `import X`\n// statements name modules without resolving to a single file (`import os.path`\n// doesn't tell us which file in `os` got used), so Phase 2 limits itself to\n// the `from`-form per the resolution rules in the contract. The imported names\n// matter: `from app import config` names the `config` submodule, not a symbol\n// on `app/__init__.py`, so resolution keys on them (see resolvePyImport).\nfunction collectPyImports(node: Parser.SyntaxNode, out: RawPyImport[]): void {\n  if (node.type === 'import_from_statement') {\n    let level = 0\n    let modulePath = ''\n    const names: string[] = []\n    let pastFrom = false\n    let pastImport = false\n\n    for (let i = 0; i < node.childCount; i++) {\n      const child = node.child(i)\n      if (!child) continue\n      if (!pastFrom) {\n        if (child.type === 'from') pastFrom = true\n        continue\n      }\n      if (!pastImport) {\n        if (child.type === 'import') {\n          pastImport = true\n          continue\n        }\n        // The `from`-module spec, before the `import` keyword.\n        if (child.type === 'relative_import') {\n          for (let j = 0; j < child.childCount; j++) {\n            const rc = child.child(j)\n            if (!rc) continue\n            // tree-sitter-python groups every leading dot into a single\n            // import_prefix node — `..` parses as one import_prefix with two\n            // `.` children, not two import_prefix nodes. Count the `.` tokens,\n            // not the prefix nodes, or multi-dot imports under-count `level`\n            // and resolve onto the wrong (sometimes self) target (#457).\n            if (rc.type === 'import_prefix') {\n              for (let k = 0; k < rc.childCount; k++) {\n                if (rc.child(k)?.type === '.') level++\n              }\n            } else if (rc.type === 'dotted_name') modulePath = rc.text\n          }\n        } else if (child.type === 'dotted_name') {\n          modulePath = child.text\n        }\n        continue\n      }\n      // Past the `import` keyword — the imported names.\n      collectImportedNames(child, names)\n    }\n\n    if (level > 0 || modulePath) {\n      out.push({ modulePath, level, names, line: node.startPosition.row + 1, snippet: clipSnippet(node.text) })\n    }\n  }\n\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) collectPyImports(child, out)\n  }\n}\n\n// ── filesystem resolution helpers ──────────────────────────────────────────\n\nasync function fileExists(p: string): Promise<boolean> {\n  try {\n    await fs.access(p)\n    return true\n  } catch {\n    return false\n  }\n}\n\n// An import that escapes the service directory (`../../other-service/x`)\n// isn't an intra-service edge — Phase 2 is scoped to within-service module\n// dependencies (file-awareness.md §10).\nfunction isWithinServiceDir(candidate: string, serviceDir: string): boolean {\n  const rel = path.relative(serviceDir, candidate)\n  return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel)\n}\n\nconst JS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']\nconst JS_INDEX_FILES = JS_EXTENSIONS.map((ext) => `index${ext}`)\n\n// Try `base`, `base.<ext>`, and `base/index.<ext>` in TypeScript-resolution\n// order. Returns the service-relative posix path of the first hit.\nasync function firstExistingCandidate(\n  base: string,\n  serviceDir: string,\n): Promise<string | null> {\n  for (const ext of JS_EXTENSIONS) {\n    const candidate = base + ext\n    if (isWithinServiceDir(candidate, serviceDir) && (await fileExists(candidate))) {\n      return toPosix(path.relative(serviceDir, candidate))\n    }\n  }\n  for (const indexFile of JS_INDEX_FILES) {\n    const candidate = path.join(base, indexFile)\n    if (isWithinServiceDir(candidate, serviceDir) && (await fileExists(candidate))) {\n      return toPosix(path.relative(serviceDir, candidate))\n    }\n  }\n  return null\n}\n\ninterface TsPathConfig {\n  paths: Record<string, string[]>\n  baseDir: string // absolute directory compilerOptions.baseUrl resolves against\n}\n\n// `tsconfig.json` at the service root. The contract names scanPath as a\n// fallback location too, but every discovered service already carries its own\n// root — the scanPath fallback only matters for configs that live above the\n// service tree, which the resolver doesn't have a path back to from here.\nasync function loadTsPathConfig(serviceDir: string): Promise<TsPathConfig | null> {\n  const tsconfigPath = path.join(serviceDir, 'tsconfig.json')\n  let raw: string\n  try {\n    raw = await fs.readFile(tsconfigPath, 'utf8')\n  } catch {\n    return null\n  }\n  try {\n    const parsed = JSON.parse(raw) as {\n      compilerOptions?: { paths?: Record<string, string[]>; baseUrl?: string }\n    }\n    const paths = parsed.compilerOptions?.paths\n    if (!paths || Object.keys(paths).length === 0) return null\n    const baseUrl = parsed.compilerOptions?.baseUrl\n    return { paths, baseDir: baseUrl ? path.resolve(serviceDir, baseUrl) : serviceDir }\n  } catch (err) {\n    recordExtractionError('import alias resolution', tsconfigPath, err)\n    return null\n  }\n}\n\n// Resolve a bare specifier against `compilerOptions.paths`. Matches the first\n// alias whose pattern fits — exact (`@db`) or wildcard (`@db/*`) — and tries\n// each mapped target in declaration order. No match anywhere → null, the\n// silent-skip the contract calls for.\nasync function resolveTsAlias(\n  specifier: string,\n  config: TsPathConfig,\n  serviceDir: string,\n): Promise<string | null> {\n  for (const [pattern, targets] of Object.entries(config.paths)) {\n    let suffix: string | null = null\n    if (pattern === specifier) {\n      suffix = ''\n    } else if (pattern.endsWith('/*')) {\n      const prefix = pattern.slice(0, -1) // keep the trailing '/'\n      if (specifier.startsWith(prefix)) suffix = specifier.slice(prefix.length)\n    }\n    if (suffix === null) continue\n\n    for (const target of targets) {\n      const targetBase = target.endsWith('/*') ? target.slice(0, -2) : target.replace(/\\*$/, '')\n      const resolvedBase = path.resolve(config.baseDir, targetBase, suffix)\n      const hit = await firstExistingCandidate(resolvedBase, serviceDir)\n      if (hit) return hit\n      // The candidate may already carry its own extension (`@db/mongo.ts`).\n      if (isWithinServiceDir(resolvedBase, serviceDir) && (await fileExists(resolvedBase))) {\n        return toPosix(path.relative(serviceDir, resolvedBase))\n      }\n    }\n  }\n  return null\n}\n\n// Resolves a JS/TS module specifier to a service-relative posix path, or\n// null when it names something outside the service (node_modules, Node\n// builtins, an alias with no tsconfig match, an escaping relative path).\nasync function resolveJsImport(\n  specifier: string,\n  importerDir: string,\n  serviceDir: string,\n  tsPaths: TsPathConfig | null,\n): Promise<string | null> {\n  if (!specifier) return null\n\n  if (specifier.startsWith('./') || specifier.startsWith('../')) {\n    const base = path.resolve(importerDir, specifier)\n    const ext = path.extname(specifier)\n\n    if (ext) {\n      // `./foo.js` written against a `.ts` source — the TypeScript ESM\n      // convention of naming the post-build extension in source. Try the\n      // TS sibling before the literal path.\n      if (ext === '.js' || ext === '.jsx') {\n        const tsExt = ext === '.jsx' ? '.tsx' : '.ts'\n        const tsSibling = base.slice(0, -ext.length) + tsExt\n        if (isWithinServiceDir(tsSibling, serviceDir) && (await fileExists(tsSibling))) {\n          return toPosix(path.relative(serviceDir, tsSibling))\n        }\n      }\n      if (isWithinServiceDir(base, serviceDir) && (await fileExists(base))) {\n        return toPosix(path.relative(serviceDir, base))\n      }\n      return null\n    }\n\n    return firstExistingCandidate(base, serviceDir)\n  }\n\n  // Bare specifier — only a registered TS path alias can make this\n  // intra-service. Anything else is node_modules / a Node builtin.\n  if (tsPaths) return resolveTsAlias(specifier, tsPaths, serviceDir)\n  return null\n}\n\n// Resolves a Python `from`-import to the service-relative posix paths it\n// touches — one per imported module. Relative imports (level > 0) walk up from\n// the importing module's directory; absolute imports match against the service\n// source tree directly.\n//\n// `from app import config` names the `config` *submodule* (app/config.py), not\n// a symbol re-exported by app/__init__.py. Resolving the whole statement onto\n// the package's __init__.py was wrong: every intra-package import collapsed\n// onto one target and the per-(source,target) edge dedup then made the second\n// such dependency invisible. So we key on each imported name, resolving it to\n// its own module file, and fall back to the package/module file only for names\n// that aren't submodules (plain symbols, or a `*` wildcard).\nasync function resolvePyImport(\n  imp: RawPyImport,\n  importerPath: string,\n  serviceDir: string,\n): Promise<string[]> {\n  let baseDir: string\n  if (imp.level > 0) {\n    baseDir = path.dirname(importerPath)\n    for (let i = 1; i < imp.level; i++) baseDir = path.dirname(baseDir)\n  } else {\n    baseDir = serviceDir\n  }\n\n  // The on-disk base of the `from`-module. `from app import x` → app/ ;\n  // `from . import x` → the importer's own package directory (modulePath empty).\n  const moduleBase = imp.modulePath\n    ? path.join(baseDir, imp.modulePath.split('.').join('/'))\n    : baseDir\n\n  const resolved = new Set<string>()\n  let needModuleFile = imp.names.length === 0 // wildcard / unparsed → the module itself\n\n  for (const name of imp.names) {\n    const submoduleFile = path.join(moduleBase, `${name}.py`)\n    const subpackageInit = path.join(moduleBase, name, '__init__.py')\n    if (isWithinServiceDir(submoduleFile, serviceDir) && (await fileExists(submoduleFile))) {\n      resolved.add(toPosix(path.relative(serviceDir, submoduleFile)))\n    } else if (isWithinServiceDir(subpackageInit, serviceDir) && (await fileExists(subpackageInit))) {\n      resolved.add(toPosix(path.relative(serviceDir, subpackageInit)))\n    } else {\n      // Not a submodule — the name is a symbol defined in the module itself.\n      needModuleFile = true\n    }\n  }\n\n  if (needModuleFile) {\n    // An empty modulePath (`from . import x`) has no module file of its own —\n    // the package is the directory, so only its __init__.py applies.\n    const moduleFileCandidates = imp.modulePath\n      ? [`${moduleBase}.py`, path.join(moduleBase, '__init__.py')]\n      : [path.join(moduleBase, '__init__.py')]\n    for (const candidate of moduleFileCandidates) {\n      if (isWithinServiceDir(candidate, serviceDir) && (await fileExists(candidate))) {\n        resolved.add(toPosix(path.relative(serviceDir, candidate)))\n        break\n      }\n    }\n  }\n\n  return [...resolved]\n}\n\n// ── edge emission ──────────────────────────────────────────────────────────\n\nfunction emitImportEdge(\n  graph: NeatGraph,\n  serviceName: string,\n  importerFileId: string,\n  importerRelPath: string,\n  importeeRelPath: string,\n  line: number,\n  snippet: string,\n): number {\n  const importeeFileId = fileId(serviceName, importeeRelPath)\n  // Phase 1 enumerates every source file unconditionally; a resolved path\n  // that isn't a FileNode is outside SERVICE_FILE_EXTENSIONS or otherwise\n  // wasn't walked — not an intra-service module edge.\n  if (!graph.hasNode(importeeFileId)) return 0\n\n  const edgeId = extractedEdgeId(importerFileId, importeeFileId, EdgeType.IMPORTS)\n  if (graph.hasEdge(edgeId)) return 0\n\n  const edge: GraphEdge = {\n    id: edgeId,\n    source: importerFileId,\n    target: importeeFileId,\n    type: EdgeType.IMPORTS,\n    provenance: Provenance.EXTRACTED,\n    confidence: confidenceForExtracted('structural'),\n    evidence: { file: importerRelPath, line, snippet },\n  }\n  graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge)\n  return 1\n}\n\n// ── producer ───────────────────────────────────────────────────────────────\n\nexport async function addImports(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  const jsParser = makeJsParser()\n  const pyParser = makePyParser()\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const tsPaths = await loadTsPathConfig(service.dir)\n    const files = await loadSourceFiles(service.dir)\n\n    for (const file of files) {\n      // ADR-065 §1 — test-scope exclusion. The file stays a FileNode (Phase 1);\n      // only its outbound module edges are filtered (file-awareness.md §10).\n      if (isTestPath(file.path)) continue\n\n      const relFile = toPosix(path.relative(service.dir, file.path))\n      const importerFileId = fileId(service.pkg.name, relFile)\n      const isPython = path.extname(file.path) === '.py'\n\n      if (isPython) {\n        let pyImports: RawPyImport[] = []\n        try {\n          const tree = parseSource(pyParser, file.content)\n          collectPyImports(tree.rootNode, pyImports)\n        } catch (err) {\n          recordExtractionError('import extraction', file.path, err)\n          continue\n        }\n        for (const imp of pyImports) {\n          const resolvedPaths = await resolvePyImport(imp, file.path, service.dir)\n          for (const resolved of resolvedPaths) {\n            edgesAdded += emitImportEdge(\n              graph,\n              service.pkg.name,\n              importerFileId,\n              relFile,\n              resolved,\n              imp.line,\n              imp.snippet,\n            )\n          }\n        }\n        continue\n      }\n\n      let jsImports: RawImport[] = []\n      try {\n        const tree = parseSource(jsParser, file.content)\n        collectJsImports(tree.rootNode, jsImports)\n      } catch (err) {\n        recordExtractionError('import extraction', file.path, err)\n        continue\n      }\n      for (const imp of jsImports) {\n        const resolved = await resolveJsImport(imp.specifier, path.dirname(file.path), service.dir, tsPaths)\n        if (!resolved) continue\n        edgesAdded += emitImportEdge(\n          graph,\n          service.pkg.name,\n          importerFileId,\n          relFile,\n          resolved,\n          imp.line,\n          imp.snippet,\n        )\n      }\n    }\n  }\n\n  return { nodesAdded: 0, edgesAdded }\n}\n","import path from 'node:path'\nimport type {\n  CompatibleDriver,\n  ConfigNode,\n  DatabaseNode,\n  GraphEdge,\n  GraphNode,\n  ServiceNode,\n} from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  configId,\n  databaseId,\n  confidenceForExtracted,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport {\n  checkCompatibility,\n  checkDeprecatedApi,\n  checkNodeEngineConstraint,\n  checkPackageConflict,\n  compatPairs,\n  deprecatedApis,\n  nodeEngineConstraints,\n  packageConflicts,\n} from '../../compat.js'\nimport {\n  cleanVersion,\n  isConfigFile,\n  makeEdgeId,\n  type DiscoveredService,\n} from '../shared.js'\nimport { ensureFileNode, toPosix } from '../calls/shared.js'\nimport { dbConfigYamlParser } from './db-config-yaml.js'\nimport { dotenvParser } from './dotenv.js'\nimport { prismaParser } from './prisma.js'\nimport { drizzleParser } from './drizzle.js'\nimport { knexParser } from './knex.js'\nimport { ormconfigParser } from './ormconfig.js'\nimport { typeormParser } from './typeorm.js'\nimport { sequelizeParser } from './sequelize.js'\nimport { dockerComposeParser } from './docker-compose.js'\nimport type { DbConfig } from './shared.js'\n\nexport type { DbConfig } from './shared.js'\n\nexport interface DbParser {\n  name: string\n  parse(serviceDir: string): Promise<DbConfig[]>\n}\n\n// Registry — order is for tie-breaking only (first wins on identical host).\n// db-config.yaml stays first so the canonical demo behaviour matches today's\n// extraction byte-for-byte.\nexport const DB_PARSERS: DbParser[] = [\n  dbConfigYamlParser,\n  dotenvParser,\n  prismaParser,\n  drizzleParser,\n  knexParser,\n  ormconfigParser,\n  typeormParser,\n  sequelizeParser,\n  dockerComposeParser,\n]\n\nfunction compatibleDriversFor(engine: string): CompatibleDriver[] {\n  return compatPairs()\n    .filter((p) => p.engine === engine)\n    .map((p) => ({ name: p.driver, minVersion: p.minDriverVersion }))\n}\n\nfunction toDatabaseNode(config: DbConfig): DatabaseNode {\n  return {\n    id: databaseId(config.host),\n    type: NodeType.DatabaseNode,\n    name: config.database || config.host,\n    engine: config.engine,\n    engineVersion: config.engineVersion,\n    compatibleDrivers: compatibleDriversFor(config.engine),\n    host: config.host,\n    port: config.port,\n  }\n}\n\nexport function attachIncompatibilities(\n  service: DiscoveredService,\n  configs: DbConfig[],\n): void {\n  const deps = { ...(service.pkg.dependencies ?? {}), ...(service.pkg.devDependencies ?? {}) }\n  const incompatibilities: NonNullable<ServiceNode['incompatibilities']> = []\n  const seen = new Set<string>()\n\n  // 1. driver-engine — original behaviour. Per (db config, configured driver\n  // pair) check that the declared driver version meets the engine threshold.\n  for (const config of configs) {\n    for (const pair of compatPairs()) {\n      if (pair.engine !== config.engine) continue\n      const declaredVersion = cleanVersion(deps[pair.driver])\n      if (!declaredVersion) continue\n      const result = checkCompatibility(\n        pair.driver,\n        declaredVersion,\n        config.engine,\n        config.engineVersion,\n      )\n      if (!result.compatible && result.reason) {\n        const key = `driver-engine|${pair.driver}@${declaredVersion}|${config.engine}@${config.engineVersion}`\n        if (seen.has(key)) continue\n        seen.add(key)\n        incompatibilities.push({\n          kind: 'driver-engine',\n          driver: pair.driver,\n          driverVersion: declaredVersion,\n          engine: config.engine,\n          engineVersion: config.engineVersion,\n          reason: result.reason,\n        })\n      }\n    }\n  }\n\n  // 2. node-engine — service's `engines.node` vs each declared dep that has a\n  // matrix-recorded minimum.\n  const serviceNodeEngine = service.node.nodeEngine ?? service.pkg.engines?.node\n  for (const constraint of nodeEngineConstraints()) {\n    const declared = cleanVersion(deps[constraint.package])\n    if (!declared) continue\n    const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine)\n    if (!result.compatible && result.reason) {\n      const key = `node-engine|${constraint.package}@${declared}|${serviceNodeEngine ?? ''}`\n      if (seen.has(key)) continue\n      seen.add(key)\n      incompatibilities.push({\n        kind: 'node-engine',\n        package: constraint.package,\n        packageVersion: declared,\n        requiredNodeVersion: result.requiredNodeVersion ?? constraint.minNodeVersion,\n        ...(serviceNodeEngine ? { declaredNodeEngine: serviceNodeEngine } : {}),\n        reason: result.reason,\n      })\n    }\n  }\n\n  // 3. package-conflict — pair like react-query 5+ requiring react 18+.\n  for (const conflict of packageConflicts()) {\n    const declared = cleanVersion(deps[conflict.package])\n    if (!declared) continue\n    const requiredVersion = cleanVersion(deps[conflict.requires.name])\n    const result = checkPackageConflict(conflict, declared, requiredVersion)\n    if (!result.compatible && result.reason) {\n      const key = `package-conflict|${conflict.package}@${declared}|${conflict.requires.name}@${requiredVersion ?? 'missing'}`\n      if (seen.has(key)) continue\n      seen.add(key)\n      incompatibilities.push({\n        kind: 'package-conflict',\n        package: conflict.package,\n        packageVersion: declared,\n        requires: conflict.requires,\n        ...(requiredVersion ? { foundVersion: requiredVersion } : {}),\n        reason: result.reason,\n      })\n    }\n  }\n\n  // 4. deprecated-api — flag presence of a known-deprecated package.\n  for (const rule of deprecatedApis()) {\n    const declared = cleanVersion(deps[rule.package])\n    if (declared === undefined) continue\n    const result = checkDeprecatedApi(rule, declared)\n    if (!result.compatible && result.reason) {\n      const key = `deprecated-api|${rule.package}@${declared}`\n      if (seen.has(key)) continue\n      seen.add(key)\n      incompatibilities.push({\n        kind: 'deprecated-api',\n        package: rule.package,\n        packageVersion: declared,\n        reason: result.reason,\n      })\n    }\n  }\n\n  if (incompatibilities.length > 0) service.node.incompatibilities = incompatibilities\n}\n\n// Phase 2 — for each service, run every parser and merge their DbConfigs by\n// host. Each unique host produces one DatabaseNode + CONNECTS_TO edge from the\n// service. The parser registry decides priority on tie; the demo's\n// db-config.yaml stays first so its `engineVersion: 15` continues to win.\nexport async function addDatabasesAndCompat(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n  scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const merged = new Map<string, DbConfig>()\n    for (const parser of DB_PARSERS) {\n      let configs: DbConfig[]\n      try {\n        configs = await parser.parse(service.dir)\n      } catch (err) {\n        console.warn(\n          `[neat] ${parser.name} parser failed on ${service.node.name}: ${(err as Error).message}`,\n        )\n        continue\n      }\n      for (const config of configs) {\n        if (!config.host) continue\n        if (!merged.has(config.host)) merged.set(config.host, config)\n      }\n    }\n\n    const allConfigs = [...merged.values()]\n    for (const config of allConfigs) {\n      const dbNode = toDatabaseNode(config)\n      if (!graph.hasNode(dbNode.id)) {\n        graph.addNode(dbNode.id, { ...dbNode, discoveredVia: 'static' })\n        nodesAdded++\n      } else {\n        // OTel ingest may have auto-created a minimal node at this id. Merge\n        // per ADR-033: static fields override OTel-derived fields, discoveredVia\n        // flips to 'merged' when both layers contributed.\n        const existing = graph.getNodeAttributes(dbNode.id) as DatabaseNode\n        const mergedDiscoveredVia: 'static' | 'otel' | 'merged' =\n          existing.discoveredVia === 'otel' ? 'merged' : 'static'\n        graph.replaceNodeAttributes(dbNode.id, {\n          ...existing,\n          ...dbNode,\n          discoveredVia: mergedDiscoveredVia,\n        })\n      }\n      // file-awareness §1 — the connection is declared in a config file; that\n      // file is the relationship origin. ensureFileNode creates the FileNode +\n      // CONTAINS edge so the CONNECTS_TO lands file-grained, not service-level.\n      const relConfigFile = toPosix(path.relative(service.dir, config.sourceFile))\n      const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(\n        graph,\n        service.pkg.name,\n        service.node.id,\n        relConfigFile,\n      )\n      nodesAdded += fn\n      edgesAdded += fe\n      const evidenceFile = toPosix(path.relative(scanPath, config.sourceFile))\n      const edge: GraphEdge = {\n        id: makeEdgeId(fileNodeId, dbNode.id, EdgeType.CONNECTS_TO),\n        source: fileNodeId,\n        target: dbNode.id,\n        type: EdgeType.CONNECTS_TO,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: { file: evidenceFile },\n      }\n      if (!graph.hasEdge(edge.id)) {\n        graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge)\n        edgesAdded++\n      }\n    }\n\n    // Service-level declared DB target. When a service declares exactly one DB\n    // connection in config — a `postgres://…` string in .env, a db-config.yaml,\n    // a prisma/drizzle/knex datasource — record where it's pointed on the\n    // ServiceNode as `dbConnectionTarget` (host[:port]) and link the service to\n    // the config that declared it with an EXTRACTED CONFIGURED_BY edge. This is\n    // the service-grained declared intent the host-mismatch divergence compares\n    // against the service-grained OBSERVED CONNECTS_TO (file-awareness §7 —\n    // compare at the shared grain; a DB OTel span carries no call site, so the\n    // comparison is service-level). Without this, `declaredHostFor` reads an\n    // unpopulated field and host-mismatch never fires.\n    //\n    // We hold to a *single* declared target: with two or more distinct hosts\n    // the single-target model is ambiguous and would flag every observed host\n    // but one as a false mismatch, so we decline rather than guess.\n    if (allConfigs.length === 1) {\n      const primary = allConfigs[0]!\n      service.node.dbConnectionTarget = primary.port\n        ? `${primary.host}:${primary.port}`\n        : primary.host\n\n      // Link to the config file that declared the connection. Mirror configs.ts's\n      // ConfigNode id (Phase 3 runs after this and is idempotent on the node);\n      // the CONFIGURED_BY edge is service-grained here, matching the grain of\n      // the declared target it backs.\n      const relPath = path.relative(scanPath, primary.sourceFile)\n      const cfgId = configId(relPath)\n      if (!graph.hasNode(cfgId)) {\n        const cfgNode: ConfigNode = {\n          id: cfgId,\n          type: NodeType.ConfigNode,\n          name: path.basename(primary.sourceFile),\n          path: relPath,\n          fileType: isConfigFile(path.basename(primary.sourceFile)).fileType || 'config',\n        }\n        graph.addNode(cfgId, cfgNode)\n        nodesAdded++\n      }\n      const cfgEdge: GraphEdge = {\n        id: makeEdgeId(service.node.id, cfgId, EdgeType.CONFIGURED_BY),\n        source: service.node.id,\n        target: cfgId,\n        type: EdgeType.CONFIGURED_BY,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: { file: toPosix(relPath) },\n      }\n      if (!graph.hasEdge(cfgEdge.id)) {\n        graph.addEdgeWithKey(cfgEdge.id, cfgEdge.source, cfgEdge.target, cfgEdge)\n        edgesAdded++\n      }\n    }\n\n    // Run all kinds of incompat checks even for services with no db connection\n    // — node-engine / package-conflict / deprecated-api don't depend on db.\n    attachIncompatibilities(service, allConfigs)\n    if (graph.hasNode(service.node.id)) {\n      // Merge with whatever's on the node already (aliases from γ #75 land\n      // before this phase), so the writeback doesn't drop fields populated by\n      // earlier passes.\n      const current = graph.getNodeAttributes(service.node.id) as ServiceNode\n      const updated: ServiceNode = {\n        ...current,\n        ...(service.node as ServiceNode),\n        ...(current.aliases ? { aliases: current.aliases } : {}),\n      }\n      // attachIncompatibilities only sets the field when there's something to\n      // flag. On a re-extract (`neat watch`, `POST /graph/scan`), a stale\n      // entry on `current` would otherwise survive the spread and leave the\n      // graph reporting a problem the new manifest no longer has.\n      if (!service.node.incompatibilities || service.node.incompatibilities.length === 0) {\n        delete (updated as { incompatibilities?: unknown }).incompatibilities\n      }\n      // Same stale-survivor guard for the declared DB target: if this pass found\n      // no single declared connection, a value left on `current` from a prior\n      // extract would otherwise survive the spread.\n      if (!service.node.dbConnectionTarget) {\n        delete (updated as { dbConnectionTarget?: unknown }).dbConnectionTarget\n      }\n      graph.replaceNodeAttributes(service.node.id, updated as unknown as GraphNode)\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport { exists, readYaml } from '../shared.js'\nimport type { DbConfig } from './shared.js'\n\ninterface DbConfigYaml {\n  host: string\n  port?: number\n  database: string\n  engine: string\n  engineVersion?: string | number\n}\n\n// The original db-config.yaml format, kept as a parser so the demo continues\n// to work. Engine + version are explicit here, so this is the one source that\n// can produce a real engineVersion without inference.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const yamlPath = path.join(serviceDir, 'db-config.yaml')\n  if (!(await exists(yamlPath))) return []\n  const raw = await readYaml<DbConfigYaml>(yamlPath)\n  return [\n    {\n      host: raw.host,\n      port: raw.port,\n      database: raw.database,\n      engine: raw.engine,\n      engineVersion: raw.engineVersion !== undefined ? String(raw.engineVersion) : 'unknown',\n      sourceFile: yamlPath,\n    },\n  ]\n}\n\nexport const dbConfigYamlParser = { name: 'db-config.yaml', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { isConfigFile } from '../shared.js'\nimport { parseConnectionString, type DbConfig } from './shared.js'\n\nconst CONNECTION_KEYS = new Set([\n  'DATABASE_URL',\n  'DB_URL',\n  'POSTGRES_URL',\n  'POSTGRESQL_URL',\n  'MYSQL_URL',\n  'MONGODB_URI',\n  'MONGO_URL',\n  'MONGO_URI',\n  'REDIS_URL',\n])\n\n// Per ADR-016, .env contents do not land in any snapshot. We read them here\n// only to derive a transient DbConfig — the value never reaches a ConfigNode.\nfunction parseDotenvLine(line: string): { key: string; value: string } | null {\n  const trimmed = line.trim()\n  if (!trimmed || trimmed.startsWith('#')) return null\n  const eq = trimmed.indexOf('=')\n  if (eq < 0) return null\n  const key = trimmed.slice(0, eq).trim()\n  let value = trimmed.slice(eq + 1).trim()\n  if (\n    (value.startsWith('\"') && value.endsWith('\"')) ||\n    (value.startsWith(\"'\") && value.endsWith(\"'\"))\n  ) {\n    value = value.slice(1, -1)\n  }\n  return { key, value }\n}\n\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const entries = await fs.readdir(serviceDir, { withFileTypes: true }).catch(() => [])\n  const configs: DbConfig[] = []\n  const seen = new Set<string>()\n\n  for (const entry of entries) {\n    if (!entry.isFile()) continue\n    const match = isConfigFile(entry.name)\n    if (!match.match || match.fileType !== 'env') continue\n\n    const filePath = path.join(serviceDir, entry.name)\n    const content = await fs.readFile(filePath, 'utf8')\n    for (const line of content.split('\\n')) {\n      const parsed = parseDotenvLine(line)\n      if (!parsed) continue\n      if (!CONNECTION_KEYS.has(parsed.key.toUpperCase())) continue\n      const config = parseConnectionString(parsed.value)\n      if (!config) continue\n      const key = `${config.engine}://${config.host}:${config.port ?? ''}/${config.database}`\n      if (seen.has(key)) continue\n      seen.add(key)\n      configs.push({ ...config, sourceFile: filePath })\n    }\n  }\n  return configs\n}\n\nexport const dotenvParser = { name: '.env', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface DbConfig {\n  host: string\n  port?: number\n  database: string\n  engine: string\n  engineVersion: string // \"unknown\" when not statically determinable\n  // Absolute path to the file the parser read this config from. Required —\n  // ghost-edge cleanup keys CONNECTS_TO retirement on `evidence.file` per\n  // ADR-032 / #140. Parsers that synthesize a DbConfig from a partial\n  // source still set this to the file the partial came from.\n  sourceFile: string\n}\n\n// Map a connection-string scheme to the engine name our compat matrix uses.\n// Schemes like \"postgres+asyncpg\" are normalised by stripping the dialect\n// suffix; anything we don't recognise returns null so the parser can decline.\nexport function schemeToEngine(scheme: string): string | null {\n  const s = scheme.toLowerCase().split('+')[0]\n  switch (s) {\n    case 'postgres':\n    case 'postgresql':\n      return 'postgresql'\n    case 'mysql':\n    case 'mariadb':\n      return 'mysql'\n    case 'mongodb':\n    case 'mongodb+srv':\n      return 'mongodb'\n    case 'redis':\n    case 'rediss':\n      return 'redis'\n    case 'sqlite':\n      return 'sqlite'\n    default:\n      return null\n  }\n}\n\n// Returns the inner DbConfig fields without sourceFile — every caller spreads\n// the parser's own file path on top. Type makes that contract explicit.\nexport type ParsedConnectionConfig = Omit<DbConfig, 'sourceFile'>\n\nexport function parseConnectionString(url: string): ParsedConnectionConfig | null {\n  const m = url.match(\n    /^(?<scheme>[a-z][a-z+]*):\\/\\/(?:[^@/]+(?::[^@]*)?@)?(?<host>[^:/?]+)(?::(?<port>\\d+))?(?:\\/(?<db>[^?#]*))?/i,\n  )\n  if (!m || !m.groups) return null\n  const engine = schemeToEngine(m.groups.scheme!)\n  if (!engine) return null\n  return {\n    host: m.groups.host!,\n    port: m.groups.port ? Number(m.groups.port) : undefined,\n    database: m.groups.db ?? '',\n    engine,\n    engineVersion: 'unknown',\n  }\n}\n\nexport async function readIfExists(filePath: string): Promise<string | null> {\n  try {\n    return await fs.readFile(filePath, 'utf8')\n  } catch {\n    return null\n  }\n}\n\nexport async function findFirst(\n  serviceDir: string,\n  candidates: string[],\n): Promise<string | null> {\n  for (const rel of candidates) {\n    const abs = path.join(serviceDir, rel)\n    const content = await readIfExists(abs)\n    if (content !== null) return abs\n  }\n  return null\n}\n\n// Engine name from a docker-compose `image:` value like \"postgres:15-alpine\"\n// or \"mysql/mysql-server:8.0\". Returns the engine + version when both are\n// resolvable, or null if the image isn't one we recognise.\nexport function engineFromImage(\n  image: string,\n): { engine: string; engineVersion: string } | null {\n  const lower = image.toLowerCase()\n  const colon = lower.lastIndexOf(':')\n  const repo = colon >= 0 ? lower.slice(0, colon) : lower\n  const tag = colon >= 0 ? lower.slice(colon + 1) : 'latest'\n  const last = repo.split('/').pop() ?? repo\n  let engine: string | null = null\n  if (last.startsWith('postgres')) engine = 'postgresql'\n  else if (last.startsWith('mysql') || last.startsWith('mariadb')) engine = 'mysql'\n  else if (last.startsWith('mongo')) engine = 'mongodb'\n  else if (last.startsWith('redis')) engine = 'redis'\n  else if (last.startsWith('sqlite')) engine = 'sqlite'\n  if (!engine) return null\n  // Strip everything after the major version digit run; \"15-alpine\" -> \"15\".\n  const versionMatch = tag.match(/^(\\d+(?:\\.\\d+){0,2})/)\n  return {\n    engine,\n    engineVersion: versionMatch ? versionMatch[1]! : 'unknown',\n  }\n}\n","import path from 'node:path'\nimport { readIfExists, parseConnectionString, schemeToEngine, type DbConfig } from './shared.js'\n\n// Prisma's schema file declares datasources of the form:\n//\n//   datasource db {\n//     provider = \"postgresql\"\n//     url      = env(\"DATABASE_URL\")\n//   }\n//\n// We match the provider directly. URLs come in via env() so we can't resolve\n// them statically; host/database fall back to placeholders so the DatabaseNode\n// id remains deterministic per service.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const schemaPath = path.join(serviceDir, 'prisma', 'schema.prisma')\n  const content = await readIfExists(schemaPath)\n  if (!content) return []\n\n  const block = content.match(/datasource\\s+\\w+\\s*\\{([^}]*)\\}/s)\n  if (!block) return []\n  const body = block[1] ?? ''\n\n  const providerMatch = body.match(/provider\\s*=\\s*\"([^\"]+)\"/)\n  if (!providerMatch) return []\n  const engine = schemeToEngine(providerMatch[1]!)\n  if (!engine) return []\n\n  const urlMatch = body.match(/url\\s*=\\s*\"([^\"]+)\"/)\n  if (urlMatch) {\n    const config = parseConnectionString(urlMatch[1]!)\n    if (config) return [{ ...config, sourceFile: schemaPath }]\n  }\n\n  return [\n    {\n      host: `${engine}-prisma`,\n      database: '',\n      engine,\n      engineVersion: 'unknown',\n      sourceFile: schemaPath,\n    },\n  ]\n}\n\nexport const prismaParser = { name: 'prisma', parse }\n","import { findFirst, readIfExists, parseConnectionString, schemeToEngine, type DbConfig } from './shared.js'\n\nconst DIALECT_TO_ENGINE: Record<string, string> = {\n  postgresql: 'postgresql',\n  postgres: 'postgresql',\n  pg: 'postgresql',\n  mysql: 'mysql',\n  mysql2: 'mysql',\n  sqlite: 'sqlite',\n  'better-sqlite': 'sqlite',\n}\n\n// Drizzle's drizzle.config.{ts,js,mjs} declares a `dialect` plus credentials.\n// We can't safely eval the file, but the relevant bits are simple key/value\n// expressions — regex-extracted to stay sandbox-clean and dep-free.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const filePath = await findFirst(serviceDir, [\n    'drizzle.config.ts',\n    'drizzle.config.js',\n    'drizzle.config.mjs',\n  ])\n  if (!filePath) return []\n  const content = await readIfExists(filePath)\n  if (!content) return []\n\n  const dialectMatch = content.match(/dialect\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n  if (!dialectMatch) return []\n  const engine =\n    DIALECT_TO_ENGINE[dialectMatch[1]!.toLowerCase()] ?? schemeToEngine(dialectMatch[1]!)\n  if (!engine) return []\n\n  const urlMatch = content.match(\n    /(?:url|connectionString)\\s*:\\s*['\"`]([a-z][a-z+]*:\\/\\/[^'\"`]+)['\"`]/i,\n  )\n  if (urlMatch) {\n    const config = parseConnectionString(urlMatch[1]!)\n    if (config) return [{ ...config, sourceFile: filePath }]\n  }\n  const hostMatch = content.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n  if (hostMatch) {\n    const portMatch = content.match(/port\\s*:\\s*(\\d+)/)\n    const dbMatch = content.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n    return [\n      {\n        host: hostMatch[1]!,\n        port: portMatch ? Number(portMatch[1]) : undefined,\n        database: dbMatch?.[1] ?? '',\n        engine,\n        engineVersion: 'unknown',\n        sourceFile: filePath,\n      },\n    ]\n  }\n  return [\n    { host: `${engine}-drizzle`, database: '', engine, engineVersion: 'unknown', sourceFile: filePath },\n  ]\n}\n\nexport const drizzleParser = { name: 'drizzle', parse }\n","import { findFirst, readIfExists, parseConnectionString, type DbConfig } from './shared.js'\n\nconst CLIENT_TO_ENGINE: Record<string, string> = {\n  pg: 'postgresql',\n  postgres: 'postgresql',\n  postgresql: 'postgresql',\n  mysql: 'mysql',\n  mysql2: 'mysql',\n  sqlite3: 'sqlite',\n  'better-sqlite3': 'sqlite',\n}\n\n// knexfile.{js,ts} declares a client (one of pg/mysql/sqlite/...) plus a\n// connection string or host/port object. We pick whichever shape is in the\n// file and ignore environment-driven values we can't resolve statically.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const filePath = await findFirst(serviceDir, [\n    'knexfile.js',\n    'knexfile.ts',\n    'knexfile.cjs',\n    'knexfile.mjs',\n  ])\n  if (!filePath) return []\n  const content = await readIfExists(filePath)\n  if (!content) return []\n\n  const clientMatch = content.match(/client\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n  if (!clientMatch) return []\n  const engine = CLIENT_TO_ENGINE[clientMatch[1]!.toLowerCase()]\n  if (!engine) return []\n\n  const urlMatch = content.match(\n    /connection\\s*:\\s*['\"`]([a-z][a-z+]*:\\/\\/[^'\"`]+)['\"`]/i,\n  )\n  if (urlMatch) {\n    const config = parseConnectionString(urlMatch[1]!)\n    if (config) return [{ ...config, sourceFile: filePath }]\n  }\n\n  const host = content.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1]\n  if (host) {\n    const port = content.match(/port\\s*:\\s*(\\d+)/)?.[1]\n    const database = content.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1] ?? ''\n    return [\n      {\n        host,\n        port: port ? Number(port) : undefined,\n        database,\n        engine,\n        engineVersion: 'unknown',\n        sourceFile: filePath,\n      },\n    ]\n  }\n\n  return [{ host: `${engine}-knex`, database: '', engine, engineVersion: 'unknown', sourceFile: filePath }]\n}\n\nexport const knexParser = { name: 'knex', parse }\n","import path from 'node:path'\nimport { exists, readJson, readYaml } from '../shared.js'\nimport { schemeToEngine, type DbConfig } from './shared.js'\n\ninterface OrmConfigEntry {\n  type?: string\n  host?: string\n  port?: number\n  database?: string\n}\n\n// ormconfig.{json,yaml,yml} — TypeORM's legacy config file. Single object or\n// an array of named connections; we walk both.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  for (const candidate of ['ormconfig.json', 'ormconfig.yaml', 'ormconfig.yml']) {\n    const abs = path.join(serviceDir, candidate)\n    if (!(await exists(abs))) continue\n    const raw = candidate.endsWith('.json')\n      ? await readJson<OrmConfigEntry | OrmConfigEntry[]>(abs)\n      : await readYaml<OrmConfigEntry | OrmConfigEntry[]>(abs)\n    const entries = Array.isArray(raw) ? raw : [raw]\n\n    const out: DbConfig[] = []\n    for (const entry of entries) {\n      if (!entry?.type || !entry.host) continue\n      const engine = schemeToEngine(entry.type)\n      if (!engine) continue\n      out.push({\n        host: entry.host,\n        port: entry.port,\n        database: entry.database ?? '',\n        engine,\n        engineVersion: 'unknown',\n        sourceFile: abs,\n      })\n    }\n    if (out.length > 0) return out\n  }\n  return []\n}\n\nexport const ormconfigParser = { name: 'ormconfig', parse }\n","import { findFirst, readIfExists, schemeToEngine, type DbConfig } from './shared.js'\n\n// TypeORM's modern shape: `new DataSource({ type: 'postgres', host, port, ... })`\n// in a data-source.ts (or .js). We regex for the type/host/port/database keys\n// in the first DataSource literal we find.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const filePath = await findFirst(serviceDir, [\n    'data-source.ts',\n    'data-source.js',\n    'src/data-source.ts',\n    'src/data-source.js',\n  ])\n  if (!filePath) return []\n  const content = await readIfExists(filePath)\n  if (!content) return []\n\n  const block = content.match(/new\\s+DataSource\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/)\n  const body = block ? block[1]! : content\n\n  const typeMatch = body.match(/type\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n  const host = body.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1]\n  if (!typeMatch || !host) return []\n\n  const engine = schemeToEngine(typeMatch[1]!)\n  if (!engine) return []\n\n  const port = body.match(/port\\s*:\\s*(\\d+)/)?.[1]\n  const database = body.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1] ?? ''\n\n  return [\n    {\n      host,\n      port: port ? Number(port) : undefined,\n      database,\n      engine,\n      engineVersion: 'unknown',\n      sourceFile: filePath,\n    },\n  ]\n}\n\nexport const typeormParser = { name: 'typeorm', parse }\n","import path from 'node:path'\nimport { exists, readJson } from '../shared.js'\nimport { schemeToEngine, type DbConfig } from './shared.js'\n\ninterface SequelizeConfigEntry {\n  dialect?: string\n  host?: string\n  port?: number\n  database?: string\n}\n\ntype SequelizeConfig = Record<string, SequelizeConfigEntry>\n\n// Sequelize stores per-environment configs under config/config.json. We read\n// every named environment so a service that declares production + staging\n// surfaces both DB targets.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  const configPath = path.join(serviceDir, 'config', 'config.json')\n  if (!(await exists(configPath))) return []\n  const raw = await readJson<SequelizeConfig>(configPath)\n\n  const out: DbConfig[] = []\n  const seen = new Set<string>()\n  for (const entry of Object.values(raw)) {\n    if (!entry?.dialect || !entry.host) continue\n    const engine = schemeToEngine(entry.dialect)\n    if (!engine) continue\n    const key = `${engine}://${entry.host}:${entry.port ?? ''}/${entry.database ?? ''}`\n    if (seen.has(key)) continue\n    seen.add(key)\n    out.push({\n      host: entry.host,\n      port: entry.port,\n      database: entry.database ?? '',\n      engine,\n      engineVersion: 'unknown',\n      sourceFile: configPath,\n    })\n  }\n  return out\n}\n\nexport const sequelizeParser = { name: 'sequelize', parse }\n","import path from 'node:path'\nimport { exists, readYaml } from '../shared.js'\nimport { engineFromImage, type DbConfig } from './shared.js'\n\ninterface ComposeService {\n  image?: string\n  ports?: (string | number)[]\n  environment?: Record<string, string> | string[]\n}\n\ninterface ComposeFile {\n  services?: Record<string, ComposeService>\n}\n\nfunction portFromService(svc: ComposeService): number | undefined {\n  for (const raw of svc.ports ?? []) {\n    const str = String(raw)\n    // \"5432:5432\", \"5432\", or \"host:5432\" → take the trailing port.\n    const last = str.split(':').pop()\n    const n = Number(last)\n    if (Number.isFinite(n) && n > 0) return n\n  }\n  return undefined\n}\n\nfunction databaseFromEnv(svc: ComposeService): string {\n  const env = svc.environment\n  const get = (key: string): string | undefined => {\n    if (!env) return undefined\n    if (Array.isArray(env)) {\n      for (const line of env) {\n        const [k, v] = line.split('=')\n        if (k === key) return v\n      }\n      return undefined\n    }\n    return env[key]\n  }\n  return get('POSTGRES_DB') ?? get('MYSQL_DATABASE') ?? get('MONGO_INITDB_DATABASE') ?? ''\n}\n\n// Service-local docker-compose.yml — every service whose image we recognise\n// becomes a candidate DB. The compose service name doubles as the host since\n// that's how peer services on the same compose network reach it.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n  for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n    const abs = path.join(serviceDir, name)\n    if (!(await exists(abs))) continue\n    const raw = await readYaml<ComposeFile>(abs)\n    if (!raw?.services) return []\n\n    const out: DbConfig[] = []\n    for (const [serviceName, svc] of Object.entries(raw.services)) {\n      if (!svc.image) continue\n      const meta = engineFromImage(svc.image)\n      if (!meta) continue\n      out.push({\n        host: serviceName,\n        port: portFromService(svc),\n        database: databaseFromEnv(svc),\n        engine: meta.engine,\n        engineVersion: meta.engineVersion,\n        sourceFile: abs,\n      })\n    }\n    return out\n  }\n  return []\n}\n\nexport const dockerComposeParser = { name: 'docker-compose', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { ConfigNode, GraphEdge } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  configId,\n  confidenceForExtracted,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport {\n  IGNORED_DIRS,\n  isConfigFile,\n  isPythonVenvDir,\n  makeEdgeId,\n  type DiscoveredService,\n} from './shared.js'\nimport { ensureFileNode, toPosix } from './calls/shared.js'\n\n// Walk a service directory and collect every config file path\n// (yaml/yml + .env-shaped). We deliberately stop at file paths here so nothing\n// in this module reads file contents — .env files routinely carry secrets\n// (ADR-016).\nexport async function walkConfigFiles(dir: string): Promise<string[]> {\n  const out: string[] = []\n  async function walk(current: string): Promise<void> {\n    const entries = await fs.readdir(current, { withFileTypes: true })\n    for (const entry of entries) {\n      const full = path.join(current, entry.name)\n      if (entry.isDirectory()) {\n        if (IGNORED_DIRS.has(entry.name)) continue\n        if (await isPythonVenvDir(full)) continue\n        await walk(full)\n      } else if (entry.isFile() && isConfigFile(entry.name).match) {\n        out.push(full)\n      }\n    }\n  }\n  await walk(dir)\n  return out\n}\n\n// Phase 3 — turn each config file into a ConfigNode with a CONFIGURED_BY edge\n// from its owning service.\nexport async function addConfigNodes(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n  scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n  for (const service of services) {\n    const configFiles = await walkConfigFiles(service.dir)\n    for (const file of configFiles) {\n      const relPath = path.relative(scanPath, file)\n      const node: ConfigNode = {\n        id: configId(relPath),\n        type: NodeType.ConfigNode,\n        name: path.basename(file),\n        path: relPath,\n        fileType: isConfigFile(path.basename(file)).fileType,\n      }\n      if (!graph.hasNode(node.id)) {\n        graph.addNode(node.id, node)\n        nodesAdded++\n      }\n      // file-awareness §1 — the config file IS the relationship origin.\n      // ensureFileNode creates the FileNode + CONTAINS edge so CONFIGURED_BY\n      // lands file-grained rather than service-level.\n      const relToService = toPosix(path.relative(service.dir, file))\n      const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(\n        graph,\n        service.pkg.name,\n        service.node.id,\n        relToService,\n      )\n      nodesAdded += fn\n      edgesAdded += fe\n      const edge: GraphEdge = {\n        id: makeEdgeId(fileNodeId, node.id, EdgeType.CONFIGURED_BY),\n        source: fileNodeId,\n        target: node.id,\n        type: EdgeType.CONFIGURED_BY,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: { file: relPath.split(path.sep).join('/') },\n      }\n      if (!graph.hasEdge(edge.id)) {\n        graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge)\n        edgesAdded++\n      }\n    }\n  }\n  return { nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport Parser from 'tree-sitter'\nimport JavaScript from 'tree-sitter-javascript'\nimport type { GraphEdge, RouteNode } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForExtracted,\n  extractedEdgeId,\n  routeId,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport { isTestPath, type DiscoveredService } from './shared.js'\nimport { recordExtractionError } from './errors.js'\nimport { loadSourceFiles, snippet, toPosix } from './calls/shared.js'\n\n// Server-route extraction (ADR-119). Reads a mainstream router's route table —\n// Express (`app.get`/`router.post`/…), Fastify (`fastify.get`, `fastify.route`),\n// Next.js (app-router `route.*` handlers, pages `api/` handlers) — and\n// materialises each route as a RouteNode at (method, path-template) grain, owned\n// by its service through a `service ──CONTAINS──▶ route` edge. This is the\n// server half of the static contract-matching in calls/route-match.ts: a\n// client call site is matched to the route it names, bridging the two static\n// islands into a route-grained cross-service CALLS edge.\n//\n// Scope is mainstream routers only, gated by manifest dependency (the extensible\n// registry pattern — coverage grows one router at a time, not by exhaustive\n// heuristics). A service with none of these deps is skipped.\n\nconst PARSE_CHUNK = 16384\n\nfunction parseSource(parser: Parser, source: string): Parser.Tree {\n  return parser.parse((index: number) =>\n    index >= source.length ? '' : source.slice(index, index + PARSE_CHUNK),\n  )\n}\n\nfunction makeJsParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(JavaScript)\n  return p\n}\n\n// The HTTP verbs an Express / Fastify router registers a route under. `all`\n// registers a method-agnostic route; it normalises to the `ALL` method token.\nconst ROUTER_METHODS = new Set([\n  'get',\n  'post',\n  'put',\n  'patch',\n  'delete',\n  'options',\n  'head',\n  'all',\n])\n\n// The exported handler names a Next.js app-router `route.*` file uses, one per\n// HTTP method it serves.\nconst NEXT_APP_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n\nconst JS_ROUTE_EXTENSIONS = new Set(['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'])\n\nexport interface ExtractedRoute {\n  method: string // upper-cased HTTP method, or 'ALL' for a method-agnostic route\n  pathTemplate: string // canonicalised declared template, e.g. '/users/:id'\n  line: number // 1-indexed line the route is declared on\n  framework: string // 'express' | 'fastify' | 'next'\n}\n\n// ── path-template canonicalisation ──────────────────────────────────────────\n\n// Canonicalise a declared route path for use as a RouteNode's stable template:\n// drop any query/hash, ensure a leading slash, drop a trailing slash (except\n// root). The template keeps its declared params verbatim (`:id`, `{id}`) so an\n// OBSERVED server span carrying the same `http.route` lands on the same node.\nexport function canonicalizeTemplate(raw: string): string {\n  let p = raw.split('?')[0]!.split('#')[0]!\n  if (!p.startsWith('/')) p = '/' + p\n  if (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1)\n  return p\n}\n\n// A path segment is dynamic when it names a parameter rather than a literal.\n// Covers the router param syntaxes (`:id`, `{id}`, `[id]`, `[...slug]`), a\n// reconstructed client interpolation (`:param`), and a concrete value a client\n// URL carries in a param position (all-digits, uuid, long hex / Mongo id).\nfunction isDynamicSegment(seg: string): boolean {\n  if (seg.length === 0) return false\n  if (seg.includes(':')) return true // :id (express/fastify) or reconstructed :param\n  if (seg.startsWith('{') || seg.startsWith('[')) return true // {id} openapi, [id] next\n  if (/^\\d+$/.test(seg)) return true // concrete numeric id\n  if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seg)) return true // uuid\n  if (/^[0-9a-f]{24,}$/i.test(seg)) return true // mongo objectid / long hex token\n  return false\n}\n\n// Normalise a path-template to a param-agnostic matching key: every dynamic\n// segment collapses to `:param`, every literal segment lowercases. This is the\n// comparison form both a server route's declared template and a client call's\n// URL path reduce to, so `/users/:id` (server) matches `/users/123` and\n// `/users/${userId}` (client). The declared template is kept intact on the\n// node; only matching uses this reduction.\nexport function normalizePathTemplate(raw: string): string {\n  const canonical = canonicalizeTemplate(raw)\n  const segments = canonical.split('/').filter((s) => s.length > 0)\n  const normalised = segments.map((seg) => (isDynamicSegment(seg) ? ':param' : seg.toLowerCase()))\n  return '/' + normalised.join('/')\n}\n\n// ── AST helpers ─────────────────────────────────────────────────────────────\n\nfunction walk(node: Parser.SyntaxNode, visit: (n: Parser.SyntaxNode) => void): void {\n  visit(node)\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) walk(child, visit)\n  }\n}\n\n// The interior text of a string literal, stripped of quotes. Returns null for a\n// template string carrying interpolation (a route path is a static literal).\nfunction staticStringText(node: Parser.SyntaxNode): string | null {\n  if (node.type === 'string') {\n    for (let i = 0; i < node.namedChildCount; i++) {\n      const child = node.namedChild(i)\n      if (child?.type === 'string_fragment') return child.text\n    }\n    // Empty string literal ('' — no fragment child).\n    return ''\n  }\n  if (node.type === 'template_string') {\n    // Only a template with no substitution is a usable static path.\n    for (let i = 0; i < node.namedChildCount; i++) {\n      if (node.namedChild(i)?.type === 'template_substitution') return null\n    }\n    const raw = node.text\n    return raw.length >= 2 ? raw.slice(1, -1) : ''\n  }\n  return null\n}\n\n// Read a string-valued property off an object-expression node (`{ url: '/x' }`).\nfunction objectStringProp(objNode: Parser.SyntaxNode, key: string): string | null {\n  for (let i = 0; i < objNode.namedChildCount; i++) {\n    const pair = objNode.namedChild(i)\n    if (!pair || pair.type !== 'pair') continue\n    const k = pair.childForFieldName('key')\n    if (!k) continue\n    const kText = k.type === 'string' ? staticStringText(k) : k.text\n    if (kText !== key) continue\n    const v = pair.childForFieldName('value')\n    if (v) return staticStringText(v)\n  }\n  return null\n}\n\n// Read the `method` property off a Fastify route-options object. Accepts a\n// single string (`method: 'GET'`) or an array (`method: ['GET','POST']`).\nfunction fastifyRouteMethods(objNode: Parser.SyntaxNode): string[] {\n  for (let i = 0; i < objNode.namedChildCount; i++) {\n    const pair = objNode.namedChild(i)\n    if (!pair || pair.type !== 'pair') continue\n    const k = pair.childForFieldName('key')\n    const kText = k ? (k.type === 'string' ? staticStringText(k) : k.text) : null\n    if (kText !== 'method') continue\n    const v = pair.childForFieldName('value')\n    if (!v) return []\n    if (v.type === 'string' || v.type === 'template_string') {\n      const s = staticStringText(v)\n      return s ? [s.toUpperCase()] : []\n    }\n    if (v.type === 'array') {\n      const out: string[] = []\n      for (let j = 0; j < v.namedChildCount; j++) {\n        const el = v.namedChild(j)\n        if (el && (el.type === 'string' || el.type === 'template_string')) {\n          const s = staticStringText(el)\n          if (s) out.push(s.toUpperCase())\n        }\n      }\n      return out\n    }\n  }\n  return []\n}\n\n// ── Express / Fastify call-expression routes ────────────────────────────────\n\n// Recognise route registrations of the shape `<router>.<method>('/path', …)`\n// and Fastify's `<fastify>.route({ method, url })`. The guard that keeps this\n// off `db.get('key')` / `_.get(obj, path)` is a string first argument that\n// starts with '/', combined with the caller-side dep gate in addRoutes (only\n// services that depend on express / fastify reach here). Mount-prefix\n// resolution (`app.use('/api', router)`) and `.route().get()` chaining are out\n// of scope for this slice — the literal declared path is captured as-is.\nexport function serverRoutesFromSource(\n  source: string,\n  parser: Parser,\n  hasExpress: boolean,\n  hasFastify: boolean,\n): ExtractedRoute[] {\n  const tree = parseSource(parser, source)\n  const out: ExtractedRoute[] = []\n  const framework = hasExpress ? 'express' : 'fastify'\n  walk(tree.rootNode, (node) => {\n    if (node.type !== 'call_expression') return\n    const fn = node.childForFieldName('function')\n    if (!fn || fn.type !== 'member_expression') return\n    const prop = fn.childForFieldName('property')\n    if (!prop) return\n    const method = prop.text.toLowerCase()\n    const args = node.childForFieldName('arguments')\n    const first = args?.namedChild(0)\n    if (!first) return\n    const line = node.startPosition.row + 1\n\n    if (ROUTER_METHODS.has(method)) {\n      const p = staticStringText(first)\n      if (p && p.startsWith('/')) {\n        out.push({\n          method: method === 'all' ? 'ALL' : method.toUpperCase(),\n          pathTemplate: canonicalizeTemplate(p),\n          line,\n          framework,\n        })\n      }\n      return\n    }\n\n    // Fastify's generic form: fastify.route({ method, url }).\n    if (method === 'route' && hasFastify && first.type === 'object') {\n      const url = objectStringProp(first, 'url')\n      if (!url || !url.startsWith('/')) return\n      const methods = fastifyRouteMethods(first)\n      const list = methods.length > 0 ? methods : ['ALL']\n      for (const m of list) {\n        out.push({\n          method: m === 'ALL' ? 'ALL' : m.toUpperCase(),\n          pathTemplate: canonicalizeTemplate(url),\n          line,\n          framework: 'fastify',\n        })\n      }\n    }\n  })\n  return out\n}\n\n// ── Next.js file-convention routes ──────────────────────────────────────────\n\nfunction segmentsOf(relFile: string): string[] {\n  return toPosix(relFile).split('/').filter((s) => s.length > 0)\n}\n\n// An app-router route handler file: `<…>/app/**/route.{js,ts,jsx,tsx,…}` (also\n// `src/app`). Route handlers live only in a file literally named `route`.\nexport function isNextAppRouteFile(relFile: string): boolean {\n  const segs = segmentsOf(relFile)\n  if (!segs.includes('app')) return false\n  const base = segs[segs.length - 1] ?? ''\n  return /^route\\.(?:js|jsx|mjs|cjs|ts|tsx)$/.test(base)\n}\n\n// A pages-router API file: `<…>/pages/api/**/*.{js,ts,…}`. Skips Next's special\n// `_app` / `_document` / `_middleware` files, which aren't routes.\nexport function isNextPagesApiFile(relFile: string): boolean {\n  const segs = segmentsOf(relFile)\n  const pagesIdx = segs.indexOf('pages')\n  if (pagesIdx === -1 || segs[pagesIdx + 1] !== 'api') return false\n  const base = segs[segs.length - 1] ?? ''\n  if (/^_(app|document|middleware)\\./.test(base)) return false\n  return JS_ROUTE_EXTENSIONS.has(path.extname(base))\n}\n\n// Convert one Next path segment to its template form: route groups `(group)`\n// drop out, `[...slug]` / `[[...slug]]` catch-alls and `[id]` dynamics become\n// `:name`, everything else stays literal.\nfunction nextSegment(seg: string): string | null {\n  if (seg.startsWith('(') && seg.endsWith(')')) return null // route group — not in the URL\n  const catchAll = seg.match(/^\\[\\[?\\.\\.\\.(.+?)\\]?\\]$/)\n  if (catchAll) return ':' + catchAll[1]\n  const dynamic = seg.match(/^\\[(.+?)\\]$/)\n  if (dynamic) return ':' + dynamic[1]\n  return seg\n}\n\n// Derive the URL path-template from an app-router `route.*` file's directory:\n// `app/users/[id]/route.ts` → `/users/:id`.\nfunction nextAppPathTemplate(relFile: string): string {\n  const segs = segmentsOf(relFile)\n  const appIdx = segs.lastIndexOf('app')\n  const between = segs.slice(appIdx + 1, segs.length - 1) // dirs between app/ and route.*\n  const parts: string[] = []\n  for (const seg of between) {\n    const mapped = nextSegment(seg)\n    if (mapped !== null) parts.push(mapped)\n  }\n  return '/' + parts.join('/')\n}\n\n// Derive the URL path-template from a pages `api/` file:\n// `pages/api/users/[id].ts` → `/api/users/:id`, `pages/api/index.ts` → `/api`.\nfunction nextPagesApiPathTemplate(relFile: string): string {\n  const segs = segmentsOf(relFile)\n  const pagesIdx = segs.indexOf('pages')\n  const rest = segs.slice(pagesIdx + 1) // api/...\n  const parts: string[] = []\n  for (let i = 0; i < rest.length; i++) {\n    let seg = rest[i]!\n    if (i === rest.length - 1) {\n      seg = seg.replace(/\\.(?:js|jsx|mjs|cjs|ts|tsx)$/, '')\n      if (seg === 'index') continue\n    }\n    const mapped = nextSegment(seg)\n    if (mapped !== null) parts.push(mapped)\n  }\n  return '/' + parts.join('/')\n}\n\n// The exported HTTP-method handler names in an app-router `route.*` file:\n// `export async function GET() {}` / `export const POST = …`. Each is one route.\nfunction nextAppMethods(root: Parser.SyntaxNode): { method: string; line: number }[] {\n  const out: { method: string; line: number }[] = []\n  walk(root, (node) => {\n    if (node.type !== 'export_statement') return\n    const decl = node.childForFieldName('declaration')\n    if (!decl) return\n    const line = node.startPosition.row + 1\n    if (decl.type === 'function_declaration') {\n      const name = decl.childForFieldName('name')?.text\n      if (name && NEXT_APP_METHODS.has(name)) out.push({ method: name, line })\n      return\n    }\n    if (decl.type === 'lexical_declaration' || decl.type === 'variable_declaration') {\n      for (let i = 0; i < decl.namedChildCount; i++) {\n        const d = decl.namedChild(i)\n        if (d?.type !== 'variable_declarator') continue\n        const name = d.childForFieldName('name')?.text\n        if (name && NEXT_APP_METHODS.has(name)) out.push({ method: name, line })\n      }\n    }\n  })\n  return out\n}\n\nfunction nextRoutesFromFile(\n  source: string,\n  relFile: string,\n  parser: Parser,\n): ExtractedRoute[] {\n  if (isNextAppRouteFile(relFile)) {\n    const tree = parseSource(parser, source)\n    const template = nextAppPathTemplate(relFile)\n    return nextAppMethods(tree.rootNode).map(({ method, line }) => ({\n      method,\n      pathTemplate: canonicalizeTemplate(template),\n      line,\n      framework: 'next',\n    }))\n  }\n  if (isNextPagesApiFile(relFile)) {\n    // A pages API handler is the module's default export and serves every\n    // method — recorded as a single method-agnostic route.\n    return [\n      {\n        method: 'ALL',\n        pathTemplate: canonicalizeTemplate(nextPagesApiPathTemplate(relFile)),\n        line: 1,\n        framework: 'next',\n      },\n    ]\n  }\n  return []\n}\n\n// ── producer ────────────────────────────────────────────────────────────────\n\nexport async function addRoutes(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  const jsParser = makeJsParser()\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const deps = {\n      ...(service.pkg.dependencies ?? {}),\n      ...(service.pkg.devDependencies ?? {}),\n    }\n    const hasExpress = deps['express'] !== undefined\n    const hasFastify = deps['fastify'] !== undefined\n    const hasNext = deps['next'] !== undefined\n    if (!hasExpress && !hasFastify && !hasNext) continue\n\n    const files = await loadSourceFiles(service.dir)\n    for (const file of files) {\n      // ADR-065 #1 — test-scope exclusion. A test that spins up a router isn't\n      // the service's declared route surface.\n      if (isTestPath(file.path)) continue\n      if (!JS_ROUTE_EXTENSIONS.has(path.extname(file.path))) continue\n      const relFile = toPosix(path.relative(service.dir, file.path))\n\n      let routes: ExtractedRoute[]\n      try {\n        if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {\n          routes = nextRoutesFromFile(file.content, relFile, jsParser)\n        } else if (hasExpress || hasFastify) {\n          routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify)\n        } else {\n          routes = []\n        }\n      } catch (err) {\n        recordExtractionError('route extraction', file.path, err)\n        continue\n      }\n      if (routes.length === 0) continue\n\n      for (const route of routes) {\n        const rid = routeId(service.pkg.name, route.method, route.pathTemplate)\n        if (!graph.hasNode(rid)) {\n          const node: RouteNode = {\n            id: rid,\n            type: NodeType.RouteNode,\n            name: `${route.method} ${route.pathTemplate}`,\n            service: service.pkg.name,\n            method: route.method,\n            pathTemplate: route.pathTemplate,\n            path: relFile,\n            line: route.line,\n            framework: route.framework,\n            discoveredVia: 'static',\n          }\n          graph.addNode(rid, node)\n          nodesAdded++\n        }\n        // `service ──CONTAINS──▶ route` — the service owns its routes the same\n        // way it owns its files (file-awareness.md §2). Structural ownership,\n        // evidence pinned to the defining file:line.\n        const containsId = extractedEdgeId(service.node.id, rid, EdgeType.CONTAINS)\n        if (!graph.hasEdge(containsId)) {\n          const edge: GraphEdge = {\n            id: containsId,\n            source: service.node.id,\n            target: rid,\n            type: EdgeType.CONTAINS,\n            provenance: Provenance.EXTRACTED,\n            confidence: confidenceForExtracted('structural'),\n            evidence: {\n              file: relFile,\n              line: route.line,\n              snippet: snippet(file.content, route.line),\n            },\n          }\n          graph.addEdgeWithKey(containsId, service.node.id, rid, edge)\n          edgesAdded++\n        }\n      }\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { GraphEdge, GrpcMethodNode } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForExtracted,\n  extractedEdgeId,\n  grpcMethodId,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport { IGNORED_DIRS, isPythonVenvDir, isTestPath, type DiscoveredService } from './shared.js'\nimport { recordExtractionError } from './errors.js'\nimport { snippet, toPosix } from './calls/shared.js'\n\n// gRPC `.proto` service/method extraction (ADR-123). gRPC used to engage only at\n// service grain — the client-stub detector in calls/grpc.ts maps a\n// `new OrderServiceClient()` to one `infra:grpc-service:*` node, and nothing read\n// the method surface at all. This producer reads the `.proto` service contract\n// as DATA (a bounded line-scan, no tree-sitter grammar — polyglot files are read\n// as data, CLAUDE.md) and materialises each `rpc` as a `GrpcMethodNode`, owned by\n// the service the proto lives in through a `service ──CONTAINS──▶ method` edge.\n//\n// This is the static half of two-sided gRPC observation: the node is keyed on the\n// fully-qualified `<package>.<Service>` name — the exact `rpc.service` an OBSERVED\n// execution span carries — so a declared method and its observed counterpart fuse\n// onto one node into a method-grain divergence (docs/contracts/otel-ingest.md\n// §gRPC methods). Scope is the service/method definitions only; message/field\n// grain, `import` resolution across proto files, and error-detail enrichment are\n// out of scope for this slice.\n\nconst PROTO_EXTENSION = '.proto'\n\n// A `.proto` service/method definition parsed out of one file.\nexport interface ExtractedGrpcMethod {\n  rpcService: string // fully-qualified `<package>.<Service>` (or bare `<Service>` when the file declares no package)\n  rpcMethod: string // bare method name, e.g. `GetOrder`\n  line: number // 1-indexed line the `rpc` is declared on\n}\n\n// ── `.proto` parsing (data, not a grammar) ──────────────────────────────────\n\n// The file-level `package foo.bar;` declaration, if present. gRPC fully-qualifies\n// `rpc.service` as `<package>.<Service>`, so the package prefixes every service\n// name in the file. proto2/proto3 both use this single-line form.\nfunction packageOf(content: string): string | null {\n  const m = content.match(/^\\s*package\\s+([A-Za-z_][A-Za-z0-9_.]*)\\s*;/m)\n  return m ? m[1]! : null\n}\n\n// The 1-indexed line an absolute character offset falls on.\nfunction lineAt(content: string, offset: number): number {\n  return content.slice(0, offset).split('\\n').length\n}\n\n// Scan a `.proto` file's `service X { rpc M(Req) returns (Res); }` blocks and\n// emit one method per `rpc`. Brace-balanced so a service body is read to its\n// close; the `rpc` scan is confined to that body. Streaming qualifiers\n// (`rpc M(stream Req) returns (stream Res)`) don't change the method identity, so\n// they're accepted and ignored. Comment-only or option lines that merely mention\n// `rpc`/`service` don't match — both anchors require the keyword followed by an\n// identifier and the structural token (`{` for a service, `(` for an rpc).\nexport function grpcMethodsFromProto(content: string, fqPackage: string | null): ExtractedGrpcMethod[] {\n  const out: ExtractedGrpcMethod[] = []\n  const serviceRe = /\\bservice\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\{/g\n  let sm: RegExpExecArray | null\n  while ((sm = serviceRe.exec(content)) !== null) {\n    const serviceName = sm[1]!\n    const rpcService = fqPackage ? `${fqPackage}.${serviceName}` : serviceName\n    // Walk from the opening brace to its matching close, tracking depth so a\n    // nested block (an inline `option`/message) doesn't end the service early.\n    const bodyStart = serviceRe.lastIndex // index just past the `{`\n    let depth = 1\n    let i = bodyStart\n    for (; i < content.length && depth > 0; i++) {\n      const ch = content[i]\n      if (ch === '{') depth++\n      else if (ch === '}') depth--\n    }\n    const body = content.slice(bodyStart, i - 1)\n    const rpcRe = /\\brpc\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\(/g\n    let rm: RegExpExecArray | null\n    while ((rm = rpcRe.exec(body)) !== null) {\n      const rpcMethod = rm[1]!\n      const line = lineAt(content, bodyStart + rm.index)\n      out.push({ rpcService, rpcMethod, line })\n    }\n    // Continue the outer scan past this service body.\n    serviceRe.lastIndex = i\n  }\n  return out\n}\n\n// ── file discovery ──────────────────────────────────────────────────────────\n\n// Walk a service directory for `.proto` files, honouring the shared ignore set\n// (node_modules, .git, …) and Python venvs the same way walkSourceFiles does.\n// `.proto` isn't a SERVICE_FILE_EXTENSION, so the source-file walker skips it —\n// this is a dedicated pass over the proto contract surface.\nasync function walkProtoFiles(dir: string): Promise<string[]> {\n  const out: string[] = []\n  async function walk(current: string): Promise<void> {\n    const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => [])\n    for (const entry of entries) {\n      const full = path.join(current, entry.name)\n      if (entry.isDirectory()) {\n        if (IGNORED_DIRS.has(entry.name)) continue\n        if (await isPythonVenvDir(full)) continue\n        await walk(full)\n      } else if (entry.isFile() && path.extname(entry.name) === PROTO_EXTENSION) {\n        out.push(full)\n      }\n    }\n  }\n  await walk(dir)\n  return out\n}\n\n// ── producer ─────────────────────────────────────────────────────────────────\n\nexport async function addGrpcMethods(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const protoPaths = await walkProtoFiles(service.dir)\n    for (const protoPath of protoPaths) {\n      // ADR-065 #1 — test-scope exclusion. A `.proto` under a test tree isn't the\n      // service's declared contract surface.\n      if (isTestPath(protoPath)) continue\n      const relFile = toPosix(path.relative(service.dir, protoPath))\n\n      let content: string\n      try {\n        content = await fs.readFile(protoPath, 'utf8')\n      } catch (err) {\n        recordExtractionError('proto extraction', protoPath, err)\n        continue\n      }\n\n      let methods: ExtractedGrpcMethod[]\n      try {\n        methods = grpcMethodsFromProto(content, packageOf(content))\n      } catch (err) {\n        recordExtractionError('proto extraction', protoPath, err)\n        continue\n      }\n      if (methods.length === 0) continue\n\n      for (const method of methods) {\n        const mid = grpcMethodId(method.rpcService, method.rpcMethod)\n        if (!graph.hasNode(mid)) {\n          const node: GrpcMethodNode = {\n            id: mid,\n            type: NodeType.GrpcMethodNode,\n            name: `${method.rpcService}/${method.rpcMethod}`,\n            rpcService: method.rpcService,\n            rpcMethod: method.rpcMethod,\n            path: relFile,\n            line: method.line,\n            discoveredVia: 'static',\n          }\n          graph.addNode(mid, node)\n          nodesAdded++\n        }\n        // `service ──CONTAINS──▶ method` — the service owns the methods its\n        // `.proto` declares, the same structural verb it has over its routes\n        // (ADR-119) and files (file-awareness.md §2). Evidence pinned to the\n        // `rpc` line. The node id is the wire-canonical FQN, so an OBSERVED\n        // execution span lands on this same node — declared and observed fuse.\n        const containsId = extractedEdgeId(service.node.id, mid, EdgeType.CONTAINS)\n        if (!graph.hasEdge(containsId)) {\n          const edge: GraphEdge = {\n            id: containsId,\n            source: service.node.id,\n            target: mid,\n            type: EdgeType.CONTAINS,\n            provenance: Provenance.EXTRACTED,\n            confidence: confidenceForExtracted('structural'),\n            evidence: {\n              file: relFile,\n              line: method.line,\n              snippet: snippet(content, method.line),\n            },\n          }\n          graph.addEdgeWithKey(containsId, service.node.id, mid, edge)\n          edgesAdded++\n        }\n      }\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import type { GraphEdge, InfraNode } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForExtracted,\n  passesExtractedFloor,\n} from '@neat.is/types'\nimport { noteExtractedDropped } from '../errors.js'\nimport type { NeatGraph } from '../../graph.js'\nimport {\n  isTestPath,\n  makeEdgeId,\n  maskCommentsInSource,\n  type DiscoveredService,\n} from '../shared.js'\nimport { addHttpCallEdges } from './http.js'\nimport { addRouteCallEdges } from './route-match.js'\nimport { ensureFileNode, loadSourceFiles, toPosix, type ExternalEndpoint } from './shared.js'\nimport { kafkaEndpointsFromFile } from './kafka.js'\nimport { redisEndpointsFromFile } from './redis.js'\nimport { awsEndpointsFromFile } from './aws.js'\nimport { grpcEndpointsFromFile } from './grpc.js'\nimport { supabaseEndpointsFromFile } from './supabase.js'\n\nexport interface CallExtractResult {\n  nodesAdded: number\n  edgesAdded: number\n}\n\nfunction edgeTypeFromEndpoint(ep: ExternalEndpoint): (typeof EdgeType)[keyof typeof EdgeType] {\n  switch (ep.edgeType) {\n    case 'PUBLISHES_TO':\n      return EdgeType.PUBLISHES_TO\n    case 'CONSUMES_FROM':\n      return EdgeType.CONSUMES_FROM\n    default:\n      return EdgeType.CALLS\n  }\n}\n\nfunction isAwsKind(kind: string): boolean {\n  return (\n    kind.startsWith('aws-') ||\n    kind.startsWith('s3') ||\n    kind.startsWith('dynamodb')\n  )\n}\n\nasync function addExternalEndpointEdges(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<CallExtractResult> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const files = await loadSourceFiles(service.dir)\n    const endpoints: ExternalEndpoint[] = []\n    for (const file of files) {\n      // ADR-065 #1 — test-scope exclusion. Tests stay registered as\n      // service-internal (via the file walk earlier); only outbound\n      // endpoint inference from them is filtered.\n      if (isTestPath(file.path)) continue\n      // ADR-065 #2 — comment-body exclusion. The regex-based extractors\n      // (redis / kafka / aws / grpc) scan raw file.content; URLs inside\n      // JSDoc / line / block comments leaked through to the graph in the\n      // v0.3.0 medusa run. Mask comments while preserving line/column for\n      // evidence line-mapping.\n      const masked = maskCommentsInSource(file.content)\n      const maskedFile = { path: file.path, content: masked }\n      endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir))\n      endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir))\n      endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir))\n      endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir))\n      endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir))\n    }\n    if (endpoints.length === 0) continue\n\n    const seenEdges = new Set<string>()\n    for (const ep of endpoints) {\n      if (!graph.hasNode(ep.infraId)) {\n        const node: InfraNode = {\n          id: ep.infraId,\n          type: NodeType.InfraNode,\n          name: ep.name,\n          // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,\n          // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the\n          // bucket / table kinds from aws.ts.\n          provider: isAwsKind(ep.kind) ? 'aws' : 'self',\n          kind: ep.kind,\n        }\n        graph.addNode(node.id, node)\n        nodesAdded++\n      }\n\n      const edgeType = edgeTypeFromEndpoint(ep)\n      const confidence = confidenceForExtracted(ep.confidenceKind)\n      // File-first (file-awareness.md §1): the endpoint relationship originates\n      // from the file the call site lives in, with the owning service\n      // ──CONTAINS──▶ file edge alongside it (§2). File-node existence is\n      // independent of edge-target precision (ADR-089 amendment) — a matched\n      // call site is a parsed fact, so the FileNode + CONTAINS materialize\n      // regardless of how confident we are about the resolved target.\n      const relFile = toPosix(ep.evidence.file)\n      const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(\n        graph,\n        service.pkg.name,\n        service.node.id,\n        relFile,\n      )\n      nodesAdded += n\n      edgesAdded += e\n      // Precision floor (ADR-066 §3). Only the file→target edge is gated:\n      // sub-threshold candidates are recorded as drops (banner accounting) and\n      // never added to the graph; the file and its call site still surface.\n      if (!passesExtractedFloor(confidence)) {\n        noteExtractedDropped({\n          source: fileNodeId,\n          target: ep.infraId,\n          type: edgeType,\n          confidence,\n          confidenceKind: ep.confidenceKind,\n          evidence: ep.evidence,\n        })\n        continue\n      }\n      const edgeId = makeEdgeId(fileNodeId, ep.infraId, edgeType)\n      if (seenEdges.has(edgeId)) continue\n      seenEdges.add(edgeId)\n      if (!graph.hasEdge(edgeId)) {\n        const edge: GraphEdge = {\n          id: edgeId,\n          source: fileNodeId,\n          target: ep.infraId,\n          type: edgeType,\n          provenance: Provenance.EXTRACTED,\n          confidence,\n          evidence: ep.evidence,\n        }\n        graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n        edgesAdded++\n      }\n    }\n  }\n  return { nodesAdded, edgesAdded }\n}\n\nexport async function addCallEdges(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<CallExtractResult> {\n  const http = await addHttpCallEdges(graph, services)\n  const ext = await addExternalEndpointEdges(graph, services)\n  // Cross-service contract matching (ADR-119). Runs after the RouteNodes are in\n  // the graph (addRoutes, a prior phase) so client call sites can be matched\n  // against the full route table, minting route-grained CALLS edges.\n  const routes = await addRouteCallEdges(graph, services)\n  return {\n    nodesAdded: http.nodesAdded + ext.nodesAdded + routes.nodesAdded,\n    edgesAdded: http.edgesAdded + ext.edgesAdded + routes.edgesAdded,\n  }\n}\n","import path from 'node:path'\nimport Parser from 'tree-sitter'\nimport JavaScript from 'tree-sitter-javascript'\nimport Python from 'tree-sitter-python'\nimport type { GraphEdge } from '@neat.is/types'\nimport {\n  EdgeType,\n  Provenance,\n  confidenceForExtracted,\n  passesExtractedFloor,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport {\n  isTestPath,\n  makeEdgeId,\n  urlMatchesHost,\n  type DiscoveredService,\n} from '../shared.js'\nimport { recordExtractionError, noteExtractedDropped } from '../errors.js'\nimport {\n  buildServiceHostIndex,\n  ensureFileNode,\n  loadSourceFiles,\n  snippet,\n  toPosix,\n} from './shared.js'\n\n// JS uses `string_fragment` for the textual interior of a template/string;\n// Python uses `string_content` inside a `string` node. Either way we want the\n// raw textual content (no quotes), so we accept both.\nconst STRING_LITERAL_NODE_TYPES = new Set(['string_fragment', 'string_content'])\n\n// ADR-065 #3 — JSX external-link exclusion. Tags whose URL-attr strings are\n// user-clickable hyperlinks, not service-to-service calls.\nconst JSX_EXTERNAL_LINK_TAGS = new Set(['a', 'Link', 'NavLink', 'ExternalLink', 'Anchor'])\n\n// Walk upward from a string-literal node to detect whether it sits inside a\n// JSX attribute on an external-link element. Returns true if the literal\n// should be filtered.\nfunction isInsideJsxExternalLink(node: Parser.SyntaxNode): boolean {\n  let cursor: Parser.SyntaxNode | null = node.parent\n  // Step out of the string wrapper if needed (parent is `string` /\n  // `template_string`).\n  while (cursor) {\n    if (cursor.type === 'jsx_attribute') {\n      // The element that owns this attribute. jsx_attribute lives inside\n      // jsx_opening_element / jsx_self_closing_element.\n      let owner: Parser.SyntaxNode | null = cursor.parent\n      while (owner && owner.type !== 'jsx_opening_element' && owner.type !== 'jsx_self_closing_element') {\n        owner = owner.parent\n      }\n      if (!owner) return false\n      // First named child of an opening/self-closing element is the tag name\n      // (`identifier` or `member_expression`).\n      const tagNode = owner.namedChild(0)\n      const tagName = tagNode?.text ?? ''\n      // For `<Foo.Bar>` we just want the rightmost ident; pick after the\n      // last dot.\n      const right = tagName.includes('.') ? tagName.split('.').pop()! : tagName\n      return JSX_EXTERNAL_LINK_TAGS.has(right)\n    }\n    cursor = cursor.parent\n  }\n  return false\n}\n\n// Collect (literal text, ast-node) pairs so the JSX-context check has the\n// node available. Comment tokens have no string_fragment / string_content\n// children in tree-sitter — JSDoc text lives inside `comment` nodes — so\n// comment-body exclusion comes for free with this AST walk (ADR-065 #2).\nfunction collectStringLiterals(\n  node: Parser.SyntaxNode,\n  out: { text: string; node: Parser.SyntaxNode }[],\n): void {\n  if (STRING_LITERAL_NODE_TYPES.has(node.type)) out.push({ text: node.text, node })\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) collectStringLiterals(child, out)\n  }\n}\n\n// A matched outbound call: the host the URL literal names and the 1-indexed\n// line it sits on. File-first extraction (file-awareness.md §1) originates a\n// CALLS edge from the file the call site lives in, so the position travels with\n// the host rather than being recovered after the fact.\nexport interface HttpCallSite {\n  host: string\n  line: number\n}\n\n// tree-sitter's node binding copies a string handed to `parser.parse` into a\n// fixed scratch buffer of ~32K code units and throws a bare \"Invalid argument\"\n// once the source is larger — it never names the size as the cause. NEAT's own\n// `cli.ts`, `ingest.ts`, and `installers/javascript.ts` all clear 40K, so the\n// http-call extractor was quietly skipping the three most interesting files in\n// the repo while dogfooding NEAT-on-NEAT. The callback form sidesteps the\n// buffer entirely: tree-sitter pulls the text in chunks we keep well under the\n// limit, so a file of any length parses. Chunking is by `.length` (UTF-16 code\n// units), which is exactly what the buffer counts.\nconst PARSE_CHUNK = 16384\n\nfunction parseSource(parser: Parser, source: string): Parser.Tree {\n  return parser.parse((index: number) =>\n    index >= source.length ? '' : source.slice(index, index + PARSE_CHUNK),\n  )\n}\n\nexport function callsFromSource(\n  source: string,\n  parser: Parser,\n  knownHosts: Set<string>,\n): HttpCallSite[] {\n  const tree = parseSource(parser, source)\n  const literals: { text: string; node: Parser.SyntaxNode }[] = []\n  collectStringLiterals(tree.rootNode, literals)\n  const out: HttpCallSite[] = []\n  for (const lit of literals) {\n    // ADR-065 #3 — JSX external-link exclusion. URL strings on <a>, <Link>,\n    // <NavLink>, <ExternalLink>, <Anchor> are user-clickable hyperlinks, not\n    // service calls.\n    if (isInsideJsxExternalLink(lit.node)) continue\n    for (const host of knownHosts) {\n      // ADR-065 #5 — exact hostname match (not substring containment).\n      // `medusa.cloud` no longer matches `@medusajs/medusa`.\n      if (urlMatchesHost(lit.text, host)) {\n        out.push({ host, line: lit.node.startPosition.row + 1 })\n      }\n    }\n  }\n  return out\n}\n\nfunction makeJsParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(JavaScript)\n  return p\n}\n\nfunction makePyParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(Python)\n  return p\n}\n\n// HTTP CALLS via URL hostname match. Parser is picked per file extension:\n// .py uses tree-sitter-python; everything else uses tree-sitter-javascript.\n//\n// File-first (file-awareness.md §1): each matched call site originates a\n// `file:<svc>:<relPath> ──CALLS──▶ target` edge plus the owning service's\n// CONTAINS edge, rather than collapsing every call in a service to one\n// service-level edge.\n//\n// File-node existence is independent of edge-target precision (file-awareness.md\n// §1, ADR-089 amendment). A matched call site is a parsed fact — the file and\n// its `service ──CONTAINS──▶ file` edge are certain regardless of how confident\n// we are about *what* it calls. So the FileNode + CONTAINS materialize for every\n// matched site; only the file→target CALLS edge is subject to the precision\n// floor. A scheme-qualified URL literal to a registered service grades at the\n// floor (url-literal-service-target, 0.7) so the declared HTTP dependency enters\n// the graph; if the floor is raised past it for diagnostics the file and its\n// call site still surface, without claiming the resolved target.\nexport async function addHttpCallEdges(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  const jsParser = makeJsParser()\n  const pyParser = makePyParser()\n\n  // Host → owning ServiceNode id (ADR-065 #5), shared with the route-matching\n  // producer so both resolve a URL's host to a service the same way.\n  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services)\n\n  let nodesAdded = 0\n  let edgesAdded = 0\n  for (const service of services) {\n    const files = await loadSourceFiles(service.dir)\n    // File grain: one file→target CALLS per (file, target) pair, even when a\n    // file names the same host on several lines (function-level is deferred).\n    const seen = new Set<string>()\n    for (const file of files) {\n      // ADR-065 #1 — test-scope exclusion.\n      if (isTestPath(file.path)) continue\n      const parser = path.extname(file.path) === '.py' ? pyParser : jsParser\n      let sites: HttpCallSite[]\n      try {\n        sites = callsFromSource(file.content, parser, knownHosts)\n      } catch (err) {\n        recordExtractionError('http call extraction', file.path, err)\n        continue\n      }\n      if (sites.length === 0) continue\n      const relFile = toPosix(path.relative(service.dir, file.path))\n      for (const site of sites) {\n        const targetId = hostToNodeId.get(site.host)\n        if (!targetId || targetId === service.node.id) continue\n        const dedupKey = `${relFile}|${targetId}`\n        if (seen.has(dedupKey)) continue\n        seen.add(dedupKey)\n        // A scheme-qualified URL literal that resolves to a registered service\n        // is a declared HTTP dependency (static-extraction contract §5). It\n        // grades at the precision floor rather than below it, so the EXTRACTED\n        // CALLS edge enters the graph and missing-observed can flag a declared-\n        // but-never-driven upstream (issue #592). Still below structural /\n        // verified-call-site: no call expression wraps the literal.\n        const confidence = confidenceForExtracted('url-literal-service-target')\n        const ev = {\n          file: relFile,\n          line: site.line,\n          snippet: snippet(file.content, site.line),\n        }\n        // The matched call site is a parsed fact: materialize the FileNode and\n        // its `service ──CONTAINS──▶ file` edge regardless of target precision\n        // (file-awareness.md §1, ADR-089 amendment). The file surfaces even\n        // when the resolved target sits below the floor.\n        const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(\n          graph,\n          service.pkg.name,\n          service.node.id,\n          relFile,\n        )\n        nodesAdded += n\n        edgesAdded += e\n        // The file→target CALLS edge alone is subject to the precision floor.\n        // A sub-floor target is recorded as a drop (banner accounting) and not\n        // claimed as a resolved edge — the file and its call site still stand.\n        if (!passesExtractedFloor(confidence)) {\n          noteExtractedDropped({\n            source: fileNodeId,\n            target: targetId,\n            type: EdgeType.CALLS,\n            confidence,\n            confidenceKind: 'url-literal-service-target',\n            evidence: ev,\n          })\n          continue\n        }\n        const edgeId = makeEdgeId(fileNodeId, targetId, EdgeType.CALLS)\n        if (!graph.hasEdge(edgeId)) {\n          const edge: GraphEdge = {\n            id: edgeId,\n            source: fileNodeId,\n            target: targetId,\n            type: EdgeType.CALLS,\n            provenance: Provenance.EXTRACTED,\n            confidence,\n            evidence: ev,\n          }\n          graph.addEdgeWithKey(edgeId, fileNodeId, targetId, edge)\n          edgesAdded++\n        }\n      }\n    }\n  }\n  return { nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport Parser from 'tree-sitter'\nimport JavaScript from 'tree-sitter-javascript'\nimport type { GraphEdge, RouteNode } from '@neat.is/types'\nimport {\n  EdgeType,\n  NodeType,\n  Provenance,\n  confidenceForExtracted,\n  passesExtractedFloor,\n  serviceId,\n} from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { isTestPath, makeEdgeId, urlMatchesHost, type DiscoveredService } from '../shared.js'\nimport { recordExtractionError, noteExtractedDropped } from '../errors.js'\nimport { normalizePathTemplate } from '../routes.js'\nimport {\n  buildServiceHostIndex,\n  ensureFileNode,\n  loadSourceFiles,\n  snippet,\n  toPosix,\n} from './shared.js'\n\n// Cross-service contract matching (ADR-119). This is the bridge between the two\n// static islands: a client call site names a URL (host + method + path); a\n// server RouteNode (extracted by routes.ts) declares (method, path-template).\n// When a client call's (host→service, method, normalised path) resolves to a\n// server route, this producer mints a route-grained EXTRACTED CALLS edge from\n// the client's FileNode to the server's RouteNode. It reuses the host→service\n// resolution the HTTP producers share (buildServiceHostIndex / urlMatchesHost),\n// adding path-template matching for the route half.\n//\n// The edge pairs with the OBSERVED server-span edge landing on the same\n// RouteNode (#576), giving get_divergences a file-precise, two-sided comparison\n// at route grain instead of only at service grain.\n//\n// Mainstream clients only: `fetch`, `axios` (default instance + method calls),\n// and node `http`/`https` `.request`/`.get`. The host and path must sit in the\n// same URL literal (or template literal) for a match — split base-URL + path\n// across variables is out of scope for this slice.\n\nconst PARSE_CHUNK = 16384\n\nfunction parseSource(parser: Parser, source: string): Parser.Tree {\n  return parser.parse((index: number) =>\n    index >= source.length ? '' : source.slice(index, index + PARSE_CHUNK),\n  )\n}\n\nfunction makeJsParser(): Parser {\n  const p = new Parser()\n  p.setLanguage(JavaScript)\n  return p\n}\n\nconst JS_CLIENT_EXTENSIONS = new Set(['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'])\nconst AXIOS_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request'])\n\nexport interface ClientCallSite {\n  host: string // the known host the URL literal named (basename or pkg name)\n  method?: string // upper-cased; undefined when not statically determinable\n  pathTemplate: string // the URL path, with `:param` for interpolations\n  line: number\n  snippet: string\n}\n\n// ── AST helpers ─────────────────────────────────────────────────────────────\n\nfunction walk(node: Parser.SyntaxNode, visit: (n: Parser.SyntaxNode) => void): void {\n  visit(node)\n  for (let i = 0; i < node.namedChildCount; i++) {\n    const child = node.namedChild(i)\n    if (child) walk(child, visit)\n  }\n}\n\n// Reconstruct the URL text a string / template-string argument names. A template\n// substitution (`${id}`) becomes the literal `:param` so the reconstructed URL\n// is a valid string with a param-shaped path segment: `/users/${id}` →\n// `/users/:param`. Returns null for anything that isn't a string-ish literal.\nfunction reconstructUrl(node: Parser.SyntaxNode): string | null {\n  if (node.type === 'string') {\n    for (let i = 0; i < node.namedChildCount; i++) {\n      const child = node.namedChild(i)\n      if (child?.type === 'string_fragment') return child.text\n    }\n    return ''\n  }\n  if (node.type === 'template_string') {\n    let out = ''\n    for (let i = 0; i < node.namedChildCount; i++) {\n      const child = node.namedChild(i)\n      if (!child) continue\n      if (child.type === 'string_fragment') out += child.text\n      else if (child.type === 'template_substitution') out += ':param'\n    }\n    // A template with no fragments/subs (empty) — fall back to stripped text.\n    if (out.length === 0) {\n      const raw = node.text\n      return raw.length >= 2 ? raw.slice(1, -1) : ''\n    }\n    return out\n  }\n  return null\n}\n\n// Read the `method` string off an options / config object (`{ method: 'POST' }`).\nfunction methodFromOptions(objNode: Parser.SyntaxNode): string | undefined {\n  for (let i = 0; i < objNode.namedChildCount; i++) {\n    const pair = objNode.namedChild(i)\n    if (!pair || pair.type !== 'pair') continue\n    const k = pair.childForFieldName('key')\n    const kText = k ? (k.type === 'string' ? stringText(k) : k.text) : null\n    if (kText !== 'method') continue\n    const v = pair.childForFieldName('value')\n    if (v && (v.type === 'string' || v.type === 'template_string')) {\n      const s = stringText(v)\n      return s ? s.toUpperCase() : undefined\n    }\n  }\n  return undefined\n}\n\nfunction stringText(node: Parser.SyntaxNode): string | null {\n  if (node.type === 'string') {\n    for (let i = 0; i < node.namedChildCount; i++) {\n      const child = node.namedChild(i)\n      if (child?.type === 'string_fragment') return child.text\n    }\n    return ''\n  }\n  return null\n}\n\n// The URL string a config object names (`axios({ url: '…', method })`).\nfunction urlNodeFromConfig(objNode: Parser.SyntaxNode): Parser.SyntaxNode | null {\n  for (let i = 0; i < objNode.namedChildCount; i++) {\n    const pair = objNode.namedChild(i)\n    if (!pair || pair.type !== 'pair') continue\n    const k = pair.childForFieldName('key')\n    const kText = k ? (k.type === 'string' ? stringText(k) : k.text) : null\n    if (kText === 'url') return pair.childForFieldName('value')\n  }\n  return null\n}\n\n// Parse the path out of a reconstructed URL string. Returns null when the string\n// isn't URL-shaped (no scheme + host). `:param` in the path survives parsing.\nfunction pathOf(urlStr: string): string | null {\n  try {\n    const candidate = urlStr.startsWith('//') ? `http:${urlStr}` : urlStr\n    const parsed = new URL(candidate)\n    return parsed.pathname || '/'\n  } catch {\n    return null\n  }\n}\n\n// Resolve which known host a reconstructed URL names (ADR-065 #5 — scheme +\n// exact hostname). Returns the host token or null.\nfunction matchHost(urlStr: string, knownHosts: Set<string>): string | null {\n  for (const host of knownHosts) {\n    if (urlMatchesHost(urlStr, host)) return host\n  }\n  return null\n}\n\n// ── client call-site recognition ────────────────────────────────────────────\n\n// Extract every recognised HTTP client call site whose URL literal names a\n// known host. Each site carries the method (when statically determinable) and\n// the path-template, ready to be matched against the server route table.\nexport function clientCallSitesFromSource(\n  source: string,\n  parser: Parser,\n  knownHosts: Set<string>,\n): ClientCallSite[] {\n  const tree = parseSource(parser, source)\n  const out: ClientCallSite[] = []\n\n  const push = (\n    urlNode: Parser.SyntaxNode,\n    method: string | undefined,\n    callNode: Parser.SyntaxNode,\n  ): void => {\n    const urlStr = reconstructUrl(urlNode)\n    if (!urlStr) return\n    const host = matchHost(urlStr, knownHosts)\n    if (!host) return\n    const p = pathOf(urlStr)\n    if (p === null) return\n    const line = callNode.startPosition.row + 1\n    out.push({\n      host,\n      method,\n      pathTemplate: p,\n      line,\n      snippet: snippet(source, line),\n    })\n  }\n\n  walk(tree.rootNode, (node) => {\n    if (node.type !== 'call_expression') return\n    const fn = node.childForFieldName('function')\n    if (!fn) return\n    const args = node.childForFieldName('arguments')\n    const first = args?.namedChild(0)\n    if (!first) return\n\n    // fetch(url, opts?) — global or member (globalThis.fetch).\n    const fnName =\n      fn.type === 'identifier'\n        ? fn.text\n        : fn.type === 'member_expression'\n          ? (fn.childForFieldName('property')?.text ?? '')\n          : ''\n\n    if (fn.type === 'identifier' && fnName === 'fetch') {\n      const opts = args?.namedChild(1)\n      const method = opts && opts.type === 'object' ? (methodFromOptions(opts) ?? 'GET') : 'GET'\n      push(first, method, node)\n      return\n    }\n\n    // axios(url | config) — default instance called directly.\n    if (fn.type === 'identifier' && fnName === 'axios') {\n      if (first.type === 'object') {\n        const urlNode = urlNodeFromConfig(first)\n        if (urlNode) push(urlNode, methodFromOptions(first) ?? 'GET', node)\n      } else {\n        const opts = args?.namedChild(1)\n        const method = opts && opts.type === 'object' ? (methodFromOptions(opts) ?? 'GET') : 'GET'\n        push(first, method, node)\n      }\n      return\n    }\n\n    if (fn.type === 'member_expression') {\n      const obj = fn.childForFieldName('object')\n      const objName = obj?.text ?? ''\n\n      // axios.get('/x') / axios.post('/x', body) / axios.request({ url, method }).\n      if (objName === 'axios' && AXIOS_METHODS.has(fnName)) {\n        if (fnName === 'request' && first.type === 'object') {\n          const urlNode = urlNodeFromConfig(first)\n          if (urlNode) push(urlNode, methodFromOptions(first) ?? 'GET', node)\n        } else {\n          push(first, fnName.toUpperCase(), node)\n        }\n        return\n      }\n\n      // node http/https .request(url, …) / .get(url, …).\n      if ((objName === 'http' || objName === 'https') && (fnName === 'request' || fnName === 'get')) {\n        const opts = args?.namedChild(1)\n        const method =\n          opts && opts.type === 'object'\n            ? (methodFromOptions(opts) ?? (fnName === 'get' ? 'GET' : 'GET'))\n            : 'GET'\n        push(first, method, node)\n        return\n      }\n    }\n  })\n\n  return out\n}\n\n// ── route index + matching ──────────────────────────────────────────────────\n\ninterface RouteEntry {\n  method: string // upper, or 'ALL'\n  normalizedPath: string\n  routeNodeId: string\n}\n\n// Group every RouteNode in the graph by its owning ServiceNode id, keyed for\n// (method, normalised-path) lookup. Built once per pass so client matching is a\n// map read, not a graph scan per call site.\nfunction buildRouteIndex(graph: NeatGraph): Map<string, RouteEntry[]> {\n  const index = new Map<string, RouteEntry[]>()\n  graph.forEachNode((_id, attrs) => {\n    const node = attrs as unknown as { type?: string }\n    if (node.type !== NodeType.RouteNode) return\n    const route = attrs as unknown as RouteNode\n    const owner = serviceId(route.service)\n    const entry: RouteEntry = {\n      method: route.method.toUpperCase(),\n      normalizedPath: normalizePathTemplate(route.pathTemplate),\n      routeNodeId: route.id,\n    }\n    const list = index.get(owner)\n    if (list) list.push(entry)\n    else index.set(owner, [entry])\n  })\n  return index\n}\n\n// A client call matches a route when the normalised paths agree and the methods\n// are compatible: exact, or the route is method-agnostic (`ALL`), or the client\n// method couldn't be read statically.\nfunction findRoute(\n  entries: RouteEntry[],\n  method: string | undefined,\n  normalizedPath: string,\n): RouteEntry | undefined {\n  return entries.find(\n    (e) =>\n      e.normalizedPath === normalizedPath &&\n      (e.method === 'ALL' || method === undefined || e.method === method),\n  )\n}\n\nexport async function addRouteCallEdges(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  const jsParser = makeJsParser()\n  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services)\n  const routeIndex = buildRouteIndex(graph)\n  if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 }\n\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const files = await loadSourceFiles(service.dir)\n    // One edge per (client file, route) pair even if a file calls the route on\n    // several lines (function grain is deferred, matching http.ts).\n    const seen = new Set<string>()\n    for (const file of files) {\n      // ADR-065 #1 — test-scope exclusion.\n      if (isTestPath(file.path)) continue\n      if (!JS_CLIENT_EXTENSIONS.has(path.extname(file.path))) continue\n\n      let sites: ClientCallSite[]\n      try {\n        sites = clientCallSitesFromSource(file.content, jsParser, knownHosts)\n      } catch (err) {\n        recordExtractionError('route-match call extraction', file.path, err)\n        continue\n      }\n      if (sites.length === 0) continue\n\n      const relFile = toPosix(path.relative(service.dir, file.path))\n      for (const site of sites) {\n        const serverServiceId = hostToNodeId.get(site.host)\n        // Skip an unresolved host or a self-call (intra-service — no\n        // cross-service contract to match, mirroring http.ts).\n        if (!serverServiceId || serverServiceId === service.node.id) continue\n        const entries = routeIndex.get(serverServiceId)\n        if (!entries) continue\n        const normalizedPath = normalizePathTemplate(site.pathTemplate)\n        const match = findRoute(entries, site.method, normalizedPath)\n        if (!match) continue\n\n        const dedupKey = `${relFile}|${match.routeNodeId}`\n        if (seen.has(dedupKey)) continue\n        seen.add(dedupKey)\n\n        // The matched call site is a parsed fact — the client FileNode and its\n        // service ──CONTAINS──▶ file edge materialise regardless (file-awareness\n        // §1). Only the file→route edge is gated by the precision floor.\n        const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(\n          graph,\n          service.pkg.name,\n          service.node.id,\n          relFile,\n        )\n        nodesAdded += n\n        edgesAdded += e\n\n        // A matched client↔route contract grades at verified-call-site (0.85):\n        // both endpoints are recognised — a framework-aware client shape and a\n        // parsed route definition — so it clears the floor and enters the graph\n        // (ADR-119).\n        const confidence = confidenceForExtracted('verified-call-site')\n        const ev = {\n          file: relFile,\n          line: site.line,\n          snippet: site.snippet,\n          method: site.method ?? match.method,\n          pathTemplate: site.pathTemplate,\n        }\n        if (!passesExtractedFloor(confidence)) {\n          noteExtractedDropped({\n            source: fileNodeId,\n            target: match.routeNodeId,\n            type: EdgeType.CALLS,\n            confidence,\n            confidenceKind: 'verified-call-site',\n            evidence: ev,\n          })\n          continue\n        }\n        const edgeId = makeEdgeId(fileNodeId, match.routeNodeId, EdgeType.CALLS)\n        if (!graph.hasEdge(edgeId)) {\n          const edge: GraphEdge = {\n            id: edgeId,\n            source: fileNodeId,\n            target: match.routeNodeId,\n            type: EdgeType.CALLS,\n            provenance: Provenance.EXTRACTED,\n            confidence,\n            evidence: ev,\n          }\n          graph.addEdgeWithKey(edgeId, fileNodeId, match.routeNodeId, edge)\n          edgesAdded++\n        }\n      }\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Match `producer.send({ topic: \"orders\" ... })` and `producer.send({\n// topic: 'orders' })` plus the two-arg form `producer.send(\"orders\", ...)`.\n// Kafka client libraries vary; these two forms cover kafkajs + node-rdkafka\n// well enough for static extraction. Same shape covers consumer.subscribe.\nconst PRODUCER_TOPIC_RE =\n  /(?:producer|kafkaProducer)[\\s\\S]{0,40}?\\.send\\s*\\(\\s*\\{[\\s\\S]{0,200}?topic\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\nconst CONSUMER_TOPIC_RE =\n  /(?:consumer|kafkaConsumer)[\\s\\S]{0,40}?\\.(?:subscribe|run)\\s*\\(\\s*\\{[\\s\\S]{0,200}?topic[s]?\\s*:\\s*(?:\\[\\s*)?['\"`]([^'\"`]+)['\"`]/g\n\nfunction findAll(re: RegExp, text: string): { topic: string; index: number }[] {\n  re.lastIndex = 0\n  const out: { topic: string; index: number }[] = []\n  let m: RegExpExecArray | null\n  while ((m = re.exec(text)) !== null) {\n    out.push({ topic: m[1]!, index: m.index })\n  }\n  return out\n}\n\nexport function kafkaEndpointsFromFile(\n  file: SourceFile,\n  serviceDir: string,\n): ExternalEndpoint[] {\n  const out: ExternalEndpoint[] = []\n  const seen = new Set<string>()\n  const make = (topic: string, edgeType: 'PUBLISHES_TO' | 'CONSUMES_FROM'): void => {\n    const key = `${edgeType}|${topic}`\n    if (seen.has(key)) return\n    seen.add(key)\n    const line = lineOf(file.content, topic)\n    out.push({\n      infraId: infraId('kafka-topic', topic),\n      name: topic,\n      kind: 'kafka-topic',\n      edgeType,\n      // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —\n      // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site\n      // tier (ADR-066).\n      confidenceKind: 'verified-call-site',\n      evidence: {\n        file: path.relative(serviceDir, file.path),\n        line,\n        snippet: snippet(file.content, line),\n      },\n    })\n  }\n\n  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, 'PUBLISHES_TO')\n  for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, 'CONSUMES_FROM')\n  return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Redis URLs in source — `redis://host[:port]` or `rediss://...`. We only\n// catch literal strings; env-driven URLs go through the database parsers\n// (.env, ormconfig, etc.) and don't need a CALLS edge.\nconst REDIS_URL_RE = /redis(?:s)?:\\/\\/(?:[^@'\"`\\s]+@)?([^:/'\"`\\s]+)(?::(\\d+))?/g\n\nexport function redisEndpointsFromFile(\n  file: SourceFile,\n  serviceDir: string,\n): ExternalEndpoint[] {\n  const out: ExternalEndpoint[] = []\n  const seen = new Set<string>()\n  REDIS_URL_RE.lastIndex = 0\n  let m: RegExpExecArray | null\n  while ((m = REDIS_URL_RE.exec(file.content)) !== null) {\n    const host = m[1]!\n    if (seen.has(host)) continue\n    seen.add(host)\n    const line = lineOf(file.content, host)\n    out.push({\n      infraId: infraId('redis', host),\n      name: host,\n      kind: 'redis',\n      edgeType: 'CALLS',\n      // `redis://host` URL literal — the scheme is structural support, but no\n      // call expression is verified to wire it through. URL-with-structural-\n      // support tier (ADR-066).\n      confidenceKind: 'url-with-structural-support',\n      evidence: {\n        file: path.relative(serviceDir, file.path),\n        line,\n        snippet: snippet(file.content, line),\n      },\n    })\n  }\n  return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// AWS SDK v3 calls. We catch S3 (`Bucket: \"x\"` near a `S3Client`-using\n// PutObjectCommand / GetObjectCommand / DeleteObjectCommand) and DynamoDB\n// (`TableName: \"x\"` near GetCommand / PutCommand / DynamoDBClient). The\n// pattern is intentionally permissive: a literal Bucket/TableName near an\n// SDK constant is good enough evidence; misses are fine because non-static\n// resources can't be catalogued anyway.\nconst S3_BUCKET_RE = /Bucket\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\nconst DYNAMO_TABLE_RE = /TableName\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\n\nfunction hasMarker(text: string, markers: string[]): boolean {\n  return markers.some((m) => text.includes(m))\n}\n\nfunction findAll(re: RegExp, text: string): { name: string; index: number }[] {\n  re.lastIndex = 0\n  const out: { name: string; index: number }[] = []\n  let m: RegExpExecArray | null\n  while ((m = re.exec(text)) !== null) {\n    out.push({ name: m[1]!, index: m.index })\n  }\n  return out\n}\n\nexport function awsEndpointsFromFile(\n  file: SourceFile,\n  serviceDir: string,\n): ExternalEndpoint[] {\n  const out: ExternalEndpoint[] = []\n  const seen = new Set<string>()\n  const make = (kind: string, name: string): void => {\n    const key = `${kind}|${name}`\n    if (seen.has(key)) return\n    seen.add(key)\n    const line = lineOf(file.content, name)\n    out.push({\n      infraId: infraId(kind, name),\n      name,\n      kind,\n      edgeType: 'CALLS',\n      // SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName\n      // literal — framework-aware recognizer, verified-call-site tier\n      // (ADR-066).\n      confidenceKind: 'verified-call-site',\n      evidence: {\n        file: path.relative(serviceDir, file.path),\n        line,\n        snippet: snippet(file.content, line),\n      },\n    })\n  }\n\n  if (hasMarker(file.content, ['S3Client', 'PutObjectCommand', 'GetObjectCommand', 'DeleteObjectCommand'])) {\n    for (const { name } of findAll(S3_BUCKET_RE, file.content)) make('s3-bucket', name)\n  }\n  if (\n    hasMarker(file.content, [\n      'DynamoDBClient',\n      'DynamoDBDocumentClient',\n      'GetCommand',\n      'PutCommand',\n      'QueryCommand',\n      'UpdateCommand',\n      'DeleteCommand',\n    ])\n  ) {\n    for (const { name } of findAll(DYNAMO_TABLE_RE, file.content)) make('dynamodb-table', name)\n  }\n  return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Client-construction in JS/TS:\n//\n//   const client = new orders_proto.OrderService(...)\n//   const client = new OrdersClient('orders.internal:50051', ...)\n//   const client = new S3Client({ region: 'us-east-1' })\n//\n// The same `new <Name>Client(...)` shape is used by gRPC client stubs, AWS\n// SDK v3 service clients, and a long tail of other SDKs. v0.3.0 mapped every\n// `*Client(...)` to `infra:grpc-service:*`, which was true for the demo and\n// false for every AWS service medusa imported.\n//\n// Per ADR-065 / #238, classification is import-aware:\n//   1. file imports `@aws-sdk/client-<suffix>` and `<Name>` lowercases to the\n//      same alphanumeric tail (`S3Client` ↔ `client-s3`,\n//      `CognitoIdentityProviderClient` ↔ `client-cognito-identity-provider`)\n//      → kind `aws-<suffix>` (e.g. `infra:aws-s3:S3`).\n//   2. file imports `@grpc/grpc-js` or any `*_grpc_pb` generated stub\n//      → kind `grpc-service` (the legitimate gRPC path; demo unchanged).\n//   3. otherwise → kind `service` (the safe default — accurate but\n//      uninformative, the v0.3.0 grpc lie is removed).\nconst GRPC_CLIENT_RE = /new\\s+([A-Z][A-Za-z0-9_]*)Client\\s*\\(\\s*['\"`]?([^,'\"`)]+)?/g\nconst AWS_SDK_IMPORT_RE =\n  /(?:from\\s+['\"`]|require\\(\\s*['\"`])@aws-sdk\\/client-([a-z0-9-]+)['\"`]/g\nconst GRPC_IMPORT_RE =\n  /(?:from\\s+['\"`]|require\\(\\s*['\"`])@grpc\\/grpc-js['\"`]|_grpc_pb['\"`]/\n\nfunction isLikelyAddress(value: string | undefined): boolean {\n  if (!value) return false\n  // Reject code expressions: object literals, anything with whitespace.\n  // process.env.X contains dots and would otherwise pass the dot check.\n  if (/\\s/.test(value) || value.startsWith('{')) return false\n  return /:\\d{2,5}$/.test(value) || value.includes('.')\n}\n\nfunction normaliseForMatch(s: string): string {\n  return s.toLowerCase().replace(/[^a-z0-9]/g, '')\n}\n\ninterface ImportContext {\n  awsSdkSuffixes: Map<string, string>  // normalised → raw suffix (e.g. 's3' → 's3')\n  hasGrpcImport: boolean\n}\n\nfunction readImports(content: string): ImportContext {\n  const awsSdkSuffixes = new Map<string, string>()\n  AWS_SDK_IMPORT_RE.lastIndex = 0\n  let m: RegExpExecArray | null\n  while ((m = AWS_SDK_IMPORT_RE.exec(content)) !== null) {\n    const raw = m[1]!\n    awsSdkSuffixes.set(normaliseForMatch(raw), raw)\n  }\n  return {\n    awsSdkSuffixes,\n    hasGrpcImport: GRPC_IMPORT_RE.test(content),\n  }\n}\n\nfunction classifyClient(\n  symbol: string,\n  ctx: ImportContext,\n): { kind: string } | null {\n  const key = normaliseForMatch(symbol)\n  const awsRaw = ctx.awsSdkSuffixes.get(key)\n  if (awsRaw) return { kind: `aws-${awsRaw}` }\n  if (ctx.hasGrpcImport) return { kind: 'grpc-service' }\n  // ADR-065 #5-adjacent — a bare `new <Name>Client()` with no AWS or gRPC\n  // import context is ambiguous; could be an in-process client object\n  // (`new QueryClient()` from @tanstack/react-query, `new PrismaClient()`,\n  // a domain Client class, etc.). Refuse to emit an edge rather than guess\n  // a `service` kind that produces false positives. v0.3.3 medusa run had\n  // a QueryClient false positive before this guard.\n  return null\n}\n\nexport function grpcEndpointsFromFile(\n  file: SourceFile,\n  serviceDir: string,\n): ExternalEndpoint[] {\n  const out: ExternalEndpoint[] = []\n  const seen = new Set<string>()\n  const ctx = readImports(file.content)\n  GRPC_CLIENT_RE.lastIndex = 0\n  let m: RegExpExecArray | null\n  while ((m = GRPC_CLIENT_RE.exec(file.content)) !== null) {\n    const symbol = m[1]!\n    const addr = m[2]?.trim()\n    const name = isLikelyAddress(addr) ? addr! : symbol\n    if (seen.has(name)) continue\n    const classified = classifyClient(symbol, ctx)\n    if (!classified) continue\n    seen.add(name)\n    const { kind } = classified\n    const line = lineOf(file.content, m[0])\n    out.push({\n      infraId: infraId(kind, name),\n      name,\n      kind,\n      edgeType: 'CALLS',\n      // `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import\n      // context — import-aware classification per #238. Verified-call-site\n      // tier (ADR-066).\n      confidenceKind: 'verified-call-site',\n      evidence: {\n        file: path.relative(serviceDir, file.path),\n        line,\n        snippet: snippet(file.content, line),\n      },\n    })\n  }\n  return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Supabase clients in JS/TS:\n//\n//   import { createClient } from '@supabase/supabase-js'\n//   const supabase = createClient(process.env.SUPABASE_URL, key)\n//   const db = createClient('https://abc.supabase.co', key)\n//   supabase.from('orders').select()\n//   db.auth.getUser()\n//\n//   import { createServerClient } from '@supabase/ssr'\n//   const supabase = createServerClient(url, key, { cookies })\n//\n// Supabase apps talk to *.supabase.co exclusively through these client\n// constructors. The HTTP-call extractor never sees the host: it lives behind\n// `createClient`, and in the common case the URL is `process.env.SUPABASE_URL`\n// (or `EXPO_PUBLIC_SUPABASE_URL`), not a literal. So a production-observed\n// `service → *.supabase.co` CALLS edge arrives with no declared twin and shows\n// up as a false `missing-extracted` divergence. This extractor closes that gap.\n//\n// Classification is import-aware, the same discipline grpc.ts / aws.ts use\n// (#238): a bare `createClient(...)` is too common a name to claim without the\n// `@supabase/*` import in scope. The constructor names map to the package that\n// owns them:\n//   - `createClient`                       ← @supabase/supabase-js\n//   - `createServerClient` / `createBrowserClient` ← @supabase/ssr\nconst SUPABASE_JS_IMPORT_RE =\n  /(?:from\\s+['\"`]|require\\(\\s*['\"`])@supabase\\/supabase-js['\"`]/\nconst SUPABASE_SSR_IMPORT_RE =\n  /(?:from\\s+['\"`]|require\\(\\s*['\"`])@supabase\\/ssr['\"`]/\n\n// First argument of a Supabase client constructor. Group 1 is the constructor\n// name, group 2 (when present) is a string-literal URL. A non-literal first arg\n// (`process.env.SUPABASE_URL`, a config getter) leaves group 2 undefined — we\n// still match the construction, we just can't resolve the literal host.\nconst SUPABASE_CLIENT_RE =\n  /\\b(createClient|createServerClient|createBrowserClient)\\s*\\(\\s*(?:['\"`]([^'\"`]*)['\"`])?/g\n\n// A *.supabase.co host pulled out of a literal URL argument. We only treat the\n// constructor's URL as a resolvable host when the literal actually names a\n// supabase.co domain — anything else (a self-hosted Supabase behind a custom\n// domain, a placeholder) stays the unresolved env target rather than a guessed\n// host. Reading the literal is the only honest host source (file-awareness §6).\nfunction hostFromLiteral(literal: string | undefined): string | null {\n  if (!literal) return null\n  const m = /^https?:\\/\\/([^/:'\"`\\s]+)/.exec(literal.trim())\n  if (!m) return null\n  const host = m[1]!\n  if (!/\\.supabase\\.(co|in)$/i.test(host)) return null\n  return host\n}\n\ninterface ImportContext {\n  hasSupabaseJs: boolean\n  hasSupabaseSsr: boolean\n}\n\nfunction readImports(content: string): ImportContext {\n  return {\n    hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),\n    hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content),\n  }\n}\n\n// A constructor name is only a Supabase client when the package that owns it is\n// imported in the same file. `createClient` needs @supabase/supabase-js;\n// `createServerClient` / `createBrowserClient` need @supabase/ssr. Without the\n// import in scope we refuse to emit — a bare `createClient` could be anything.\nfunction constructorMatchesImport(name: string, ctx: ImportContext): boolean {\n  if (name === 'createClient') return ctx.hasSupabaseJs\n  return ctx.hasSupabaseSsr\n}\n\nexport function supabaseEndpointsFromFile(\n  file: SourceFile,\n  serviceDir: string,\n): ExternalEndpoint[] {\n  const ctx = readImports(file.content)\n  if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return []\n\n  const out: ExternalEndpoint[] = []\n  const seen = new Set<string>()\n  SUPABASE_CLIENT_RE.lastIndex = 0\n  let m: RegExpExecArray | null\n  while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {\n    const ctor = m[1]!\n    if (!constructorMatchesImport(ctor, ctx)) continue\n\n    // When the first argument is a literal *.supabase.co URL we resolve the\n    // real host. Otherwise the URL is env-driven (`process.env.SUPABASE_URL`,\n    // a config getter) and unknowable statically — the edge still has to land,\n    // so it resolves to a stable `supabase:env` target. That target is honest:\n    // we read the @supabase import and the `createClient` call, but not a host,\n    // so we don't fabricate one (file-awareness §6).\n    const host = hostFromLiteral(m[2])\n    const name = host ?? 'env'\n    if (seen.has(name)) continue\n    seen.add(name)\n\n    const line = lineOf(file.content, m[0])\n    out.push({\n      infraId: infraId('supabase', name),\n      name,\n      kind: 'supabase',\n      edgeType: 'CALLS',\n      // `createClient(...)` from @supabase/supabase-js (or createServerClient /\n      // createBrowserClient from @supabase/ssr) with the import in scope — a\n      // framework-aware recognizer matched the SDK shape. Verified-call-site\n      // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.\n      confidenceKind: 'verified-call-site',\n      evidence: {\n        file: path.relative(serviceDir, file.path),\n        line,\n        snippet: snippet(file.content, line),\n      },\n    })\n  }\n  return out\n}\n","import type { NeatGraph } from '../../graph.js'\nimport { type DiscoveredService } from '../shared.js'\nimport { addComposeInfra } from './docker-compose.js'\nimport { addDockerfileRuntimes } from './dockerfile.js'\nimport { addTerraformResources } from './terraform.js'\nimport { addK8sResources } from './k8s.js'\n\nexport interface InfraExtractResult {\n  nodesAdded: number\n  edgesAdded: number\n}\n\n// Phase 5 — infrastructure. Runs after services so RUNS_ON edges have a\n// ServiceNode to anchor on. Each sub-source contributes its own nodes/edges\n// independently; nothing here mutates ServiceNodes themselves.\nexport async function addInfra(\n  graph: NeatGraph,\n  scanPath: string,\n  services: DiscoveredService[],\n): Promise<InfraExtractResult> {\n  const compose = await addComposeInfra(graph, scanPath, services)\n  const dockerfile = await addDockerfileRuntimes(graph, services, scanPath)\n  const terraform = await addTerraformResources(graph, scanPath)\n  const k8s = await addK8sResources(graph, scanPath)\n\n  return {\n    nodesAdded:\n      compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded,\n    edgesAdded:\n      compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded,\n  }\n}\n","import path from 'node:path'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance, confidenceForExtracted } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { exists, makeEdgeId, readYaml, type DiscoveredService } from '../shared.js'\nimport { recordExtractionError } from '../errors.js'\nimport { classifyImage, makeInfraNode } from './shared.js'\n\ninterface ComposeService {\n  image?: string\n  build?: string | { context?: string }\n  depends_on?: string[] | Record<string, unknown>\n}\n\ninterface ComposeFile {\n  services?: Record<string, ComposeService>\n}\n\nfunction dependsOnList(value: ComposeService['depends_on']): string[] {\n  if (!value) return []\n  if (Array.isArray(value)) return value\n  return Object.keys(value)\n}\n\nfunction serviceNameToServiceNode(\n  name: string,\n  services: DiscoveredService[],\n): string | null {\n  for (const s of services) {\n    if (s.node.name === name || path.basename(s.dir) === name) return s.node.id\n  }\n  return null\n}\n\n// Project-level docker-compose.yml describes deployment topology. Each compose\n// service that is *not* one of the discovered ServiceNodes becomes an\n// InfraNode (databases, brokers, caches). depends_on lists become DEPENDS_ON\n// edges from the dependent to its dependency, regardless of whether the\n// endpoint is a ServiceNode or InfraNode — the edge itself is the deployment\n// fact, not the role.\nexport async function addComposeInfra(\n  graph: NeatGraph,\n  scanPath: string,\n  services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  let composePath: string | null = null\n  for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n    const abs = path.join(scanPath, name)\n    if (await exists(abs)) {\n      composePath = abs\n      break\n    }\n  }\n  if (!composePath) return { nodesAdded, edgesAdded }\n\n  let compose: ComposeFile\n  try {\n    compose = await readYaml<ComposeFile>(composePath)\n  } catch (err) {\n    recordExtractionError(\n      'infra docker-compose',\n      path.relative(scanPath, composePath),\n      err,\n    )\n    return { nodesAdded, edgesAdded }\n  }\n  if (!compose?.services) return { nodesAdded, edgesAdded }\n  const evidenceFile = path.relative(scanPath, composePath).split(path.sep).join('/')\n\n  const composeNameToNodeId = new Map<string, string>()\n  for (const [composeName, svc] of Object.entries(compose.services)) {\n    const matchedServiceId = serviceNameToServiceNode(composeName, services)\n    if (matchedServiceId) {\n      composeNameToNodeId.set(composeName, matchedServiceId)\n      continue\n    }\n    const kind = svc.image ? classifyImage(svc.image) : 'container'\n    const node = makeInfraNode(kind, composeName)\n    if (!graph.hasNode(node.id)) {\n      graph.addNode(node.id, node)\n      nodesAdded++\n    }\n    composeNameToNodeId.set(composeName, node.id)\n  }\n\n  for (const [composeName, svc] of Object.entries(compose.services)) {\n    const sourceId = composeNameToNodeId.get(composeName)\n    if (!sourceId) continue\n    for (const dep of dependsOnList(svc.depends_on)) {\n      const targetId = composeNameToNodeId.get(dep)\n      if (!targetId) continue\n      const edgeId = makeEdgeId(sourceId, targetId, EdgeType.DEPENDS_ON)\n      if (graph.hasEdge(edgeId)) continue\n      // depends_on declaration from docker-compose.yml — structural deployment\n      // fact, structural tier per ADR-066.\n      const edge: GraphEdge = {\n        id: edgeId,\n        source: sourceId,\n        target: targetId,\n        type: EdgeType.DEPENDS_ON,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: { file: evidenceFile },\n      }\n      graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n      edgesAdded++\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import type { InfraNode } from '@neat.is/types'\nimport { NodeType, infraId } from '@neat.is/types'\n\n// ADR-010 reserves the `infra:` prefix; the kind segment lets traversal and\n// MCP tools sub-type without inventing a new top-level NodeType per source.\nexport function makeInfraNode(\n  kind: string,\n  name: string,\n  provider = 'self',\n  extras?: { region?: string },\n): InfraNode {\n  return {\n    id: infraId(kind, name),\n    type: NodeType.InfraNode,\n    name,\n    provider,\n    kind,\n    ...(extras?.region ? { region: extras.region } : {}),\n  }\n}\n\n// Stable kind for an image string like \"postgres:15-alpine\" or \"mysql:8\".\n// The image name itself ends up in the InfraNode `name` field; this function\n// only classifies what the image *is*, so callers can group similar runtimes.\nexport function classifyImage(image: string): string {\n  const lower = image.toLowerCase()\n  const repo = lower.split(':')[0]!\n  const last = repo.split('/').pop() ?? repo\n  if (last.startsWith('postgres')) return 'postgres'\n  if (last.startsWith('mysql') || last.startsWith('mariadb')) return 'mysql'\n  if (last.startsWith('mongo')) return 'mongodb'\n  if (last.startsWith('redis')) return 'redis'\n  if (last.startsWith('rabbitmq')) return 'rabbitmq'\n  if (last.startsWith('kafka') || last.includes('kafka')) return 'kafka'\n  if (last.startsWith('memcached')) return 'memcached'\n  return 'container'\n}\n","import path from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance, confidenceForExtracted } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { exists, makeEdgeId, type DiscoveredService } from '../shared.js'\nimport { recordExtractionError } from '../errors.js'\nimport { makeInfraNode } from './shared.js'\nimport { ensureFileNode, toPosix } from '../calls/shared.js'\n\ninterface DockerfileFacts {\n  image: string | null\n  // Ports declared with `EXPOSE`, in declaration order, deduped. Each one is a\n  // network endpoint the container listens on — the structural answer to \"what\n  // is this service reachable at\".\n  ports: number[]\n  // The trimmed `ENTRYPOINT` / `CMD` line that names the process the container\n  // runs. Carried as edge evidence so the entrypoint is queryable instead of\n  // being a silent fact only the Dockerfile knows.\n  entrypoint: string | null\n}\n\n// Read the runtime-shaping instructions out of a Dockerfile in one pass:\n//\n//   - FROM   → the runtime image. Multi-stage builds report the *runtime* image\n//              (the last FROM that isn't `scratch` / a stage alias).\n//   - EXPOSE → the ports the container listens on (`EXPOSE 8080 9090`,\n//              `EXPOSE 8080/tcp`).\n//   - CMD / ENTRYPOINT → the process the container runs. ENTRYPOINT wins when\n//              both are present, matching Docker's precedence.\nfunction readDockerfile(content: string): DockerfileFacts {\n  let image: string | null = null\n  const ports: number[] = []\n  let cmd: string | null = null\n  let entrypoint: string | null = null\n  for (const raw of content.split('\\n')) {\n    const line = raw.trim()\n    if (!line || line.startsWith('#')) continue\n    if (/^from\\s+/i.test(line)) {\n      const candidate = line.split(/\\s+/)[1]\n      if (candidate && candidate.toLowerCase() !== 'scratch') image = candidate\n    } else if (/^expose\\s+/i.test(line)) {\n      for (const token of line.split(/\\s+/).slice(1)) {\n        const port = Number.parseInt(token.split('/')[0]!, 10)\n        if (Number.isInteger(port) && !ports.includes(port)) ports.push(port)\n      }\n    } else if (/^entrypoint\\s+/i.test(line)) {\n      entrypoint = line\n    } else if (/^cmd\\s+/i.test(line)) {\n      cmd = line\n    }\n  }\n  return { image, ports, entrypoint: entrypoint ?? cmd }\n}\n\n// For each ServiceNode that has a Dockerfile in its dir, emit a\n// `infra:container-image:<image>` InfraNode and a RUNS_ON edge from the\n// service to the image.\nexport async function addDockerfileRuntimes(\n  graph: NeatGraph,\n  services: DiscoveredService[],\n  scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n\n  for (const service of services) {\n    const dockerfilePath = path.join(service.dir, 'Dockerfile')\n    if (!(await exists(dockerfilePath))) continue\n    let content: string\n    try {\n      content = await fs.readFile(dockerfilePath, 'utf8')\n    } catch (err) {\n      recordExtractionError(\n        'infra dockerfile',\n        path.relative(scanPath, dockerfilePath),\n        err,\n      )\n      continue\n    }\n    const facts = readDockerfile(content)\n    if (!facts.image) continue\n\n    const node = makeInfraNode('container-image', facts.image)\n    if (!graph.hasNode(node.id)) {\n      graph.addNode(node.id, node)\n      nodesAdded++\n    }\n\n    // file-awareness §1 — the Dockerfile IS the file that declares the runtime;\n    // anchor the infra edges on a FileNode for it, not on the service. The file\n    // node is service-scoped, so two services that both `FROM node:20` keep\n    // distinct entrypoints and exposed ports instead of colliding on the shared\n    // image node.\n    const relDockerfile = toPosix(path.relative(service.dir, dockerfilePath))\n    const evidenceFile = toPosix(path.relative(scanPath, dockerfilePath))\n    const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(\n      graph,\n      service.pkg.name,\n      service.node.id,\n      relDockerfile,\n    )\n    nodesAdded += fn\n    edgesAdded += fe\n\n    // RUNS_ON carries the runtime image, and — when the Dockerfile declares one\n    // — the entrypoint/CMD line as evidence.snippet, so the process the\n    // container runs is queryable instead of a silent fact.\n    const edgeId = makeEdgeId(fileNodeId, node.id, EdgeType.RUNS_ON)\n    if (!graph.hasEdge(edgeId)) {\n      const edge: GraphEdge = {\n        id: edgeId,\n        source: fileNodeId,\n        target: node.id,\n        type: EdgeType.RUNS_ON,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: {\n          file: evidenceFile,\n          ...(facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}),\n        },\n      }\n      graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n      edgesAdded++\n    }\n\n    // Each EXPOSE port becomes an `infra:port:<n>` node the Dockerfile\n    // CONNECTS_TO — the structural answer to \"what is this service reachable\n    // at\". Without this, a declared listener is a fact only the Dockerfile\n    // knows; with it, the port is a first-class node the topology can reach.\n    for (const port of facts.ports) {\n      const portNode = makeInfraNode('port', String(port))\n      if (!graph.hasNode(portNode.id)) {\n        graph.addNode(portNode.id, portNode)\n        nodesAdded++\n      }\n      const portEdgeId = makeEdgeId(fileNodeId, portNode.id, EdgeType.CONNECTS_TO)\n      if (graph.hasEdge(portEdgeId)) continue\n      const portEdge: GraphEdge = {\n        id: portEdgeId,\n        source: fileNodeId,\n        target: portNode.id,\n        type: EdgeType.CONNECTS_TO,\n        provenance: Provenance.EXTRACTED,\n        confidence: confidenceForExtracted('structural'),\n        evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` },\n      }\n      graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge)\n      edgesAdded++\n    }\n  }\n\n  return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance, confidenceForExtracted } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { IGNORED_DIRS, isPythonVenvDir, makeEdgeId } from '../shared.js'\nimport { toPosix } from '../calls/shared.js'\nimport { makeInfraNode } from './shared.js'\n\n// Light pass: catalogue `resource \"aws_*\" \"name\"` blocks in any *.tf file and\n// wire the references between them. We don't run a real Terraform backend, but\n// the body of every resource block names the other resources it consumes\n// (`aws_db_instance.main.endpoint`, `${aws_security_group.db.id}`, …). Those\n// `<type>.<name>` references are the structural fact that turns an edgeless\n// catalogue into a topology: a resource nothing points at is declared-but-unused,\n// a resource something points at is in use. Without this, an RDS instance the\n// app server depends on looks identical to a forgotten S3 bucket.\nconst RESOURCE_RE = /resource\\s+\"(aws_[A-Za-z0-9_]+)\"\\s+\"([A-Za-z0-9_-]+)\"/g\n// A reference to another declared resource. The negative look-behind keeps us\n// from matching the tail of a longer identifier (`my_aws_thing.x`).\nconst REFERENCE_RE = /(?<![\\w.])(aws_[A-Za-z0-9_]+)\\.([A-Za-z0-9_-]+)/g\n\ninterface TfResource {\n  type: string\n  name: string\n  nodeId: string\n  body: string\n  bodyOffset: number\n}\n\nasync function walkTfFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n  if (depth > max) return []\n  const out: string[] = []\n  const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n  for (const entry of entries) {\n    if (entry.isDirectory()) {\n      if (IGNORED_DIRS.has(entry.name) || entry.name === '.terraform') continue\n      const child = path.join(start, entry.name)\n      if (await isPythonVenvDir(child)) continue\n      out.push(...(await walkTfFiles(child, depth + 1, max)))\n    } else if (entry.isFile() && entry.name.endsWith('.tf')) {\n      out.push(path.join(start, entry.name))\n    }\n  }\n  return out\n}\n\n// Find the index just past the `}` that closes the block whose opening `{`\n// sits at or after `from`. Returns the body span (exclusive of the braces).\n// Balanced-brace scan — good enough for HCL, which doesn't put bare braces in\n// string literals often enough to matter for a reference catalogue.\nfunction blockBody(content: string, from: number): { body: string; offset: number } | null {\n  const open = content.indexOf('{', from)\n  if (open === -1) return null\n  let depth = 0\n  for (let i = open; i < content.length; i++) {\n    const ch = content[i]\n    if (ch === '{') depth++\n    else if (ch === '}') {\n      depth--\n      if (depth === 0) return { body: content.slice(open + 1, i), offset: open + 1 }\n    }\n  }\n  return null\n}\n\nfunction lineAt(content: string, index: number): number {\n  let line = 1\n  for (let i = 0; i < index && i < content.length; i++) {\n    if (content[i] === '\\n') line++\n  }\n  return line\n}\n\nexport async function addTerraformResources(\n  graph: NeatGraph,\n  scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  let edgesAdded = 0\n  const files = await walkTfFiles(scanPath)\n  for (const file of files) {\n    const content = await fs.readFile(file, 'utf8')\n    const evidenceFile = toPosix(path.relative(scanPath, file))\n\n    // First pass: register every resource as a node and remember its body so\n    // the second pass can resolve references against the set of declared names.\n    const resources: TfResource[] = []\n    const byKey = new Map<string, TfResource>()\n    RESOURCE_RE.lastIndex = 0\n    let m: RegExpExecArray | null\n    while ((m = RESOURCE_RE.exec(content)) !== null) {\n      const type = m[1]!\n      const name = m[2]!\n      const node = makeInfraNode(type, name, 'aws')\n      if (!graph.hasNode(node.id)) {\n        graph.addNode(node.id, node)\n        nodesAdded++\n      }\n      const span = blockBody(content, RESOURCE_RE.lastIndex)\n      const resource: TfResource = {\n        type,\n        name,\n        nodeId: node.id,\n        body: span?.body ?? '',\n        bodyOffset: span?.offset ?? RESOURCE_RE.lastIndex,\n      }\n      resources.push(resource)\n      byKey.set(`${type}.${name}`, resource)\n    }\n\n    // Second pass: a `<type>.<name>` reference inside a resource body that\n    // names another declared resource becomes a DEPENDS_ON edge from the\n    // referencing resource to the one it consumes. Structural tier (ADR-066) —\n    // the HCL literally says it depends on it.\n    for (const resource of resources) {\n      const seen = new Set<string>()\n      REFERENCE_RE.lastIndex = 0\n      let ref: RegExpExecArray | null\n      while ((ref = REFERENCE_RE.exec(resource.body)) !== null) {\n        const key = `${ref[1]!}.${ref[2]!}`\n        if (key === `${resource.type}.${resource.name}`) continue\n        const target = byKey.get(key)\n        if (!target) continue\n        if (seen.has(target.nodeId)) continue\n        seen.add(target.nodeId)\n        const edgeId = makeEdgeId(resource.nodeId, target.nodeId, EdgeType.DEPENDS_ON)\n        if (graph.hasEdge(edgeId)) continue\n        const line = lineAt(content, resource.bodyOffset + ref.index)\n        const edge: GraphEdge = {\n          id: edgeId,\n          source: resource.nodeId,\n          target: target.nodeId,\n          type: EdgeType.DEPENDS_ON,\n          provenance: Provenance.EXTRACTED,\n          confidence: confidenceForExtracted('structural'),\n          evidence: { file: evidenceFile, line, snippet: key },\n        }\n        graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n        edgesAdded++\n      }\n    }\n  }\n  return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parseAllDocuments } from 'yaml'\nimport type { NeatGraph } from '../../graph.js'\nimport { CONFIG_FILE_EXTENSIONS, IGNORED_DIRS, isPythonVenvDir } from '../shared.js'\nimport { makeInfraNode } from './shared.js'\n\ninterface K8sDoc {\n  kind?: string\n  metadata?: { name?: string; namespace?: string }\n}\n\nconst K8S_KIND_TO_INFRA_KIND: Record<string, string> = {\n  Service: 'k8s-service',\n  Deployment: 'k8s-deployment',\n  StatefulSet: 'k8s-statefulset',\n  DaemonSet: 'k8s-daemonset',\n  CronJob: 'k8s-cronjob',\n  Job: 'k8s-job',\n  Ingress: 'k8s-ingress',\n}\n\nasync function walkYamlFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n  if (depth > max) return []\n  const out: string[] = []\n  const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n  for (const entry of entries) {\n    if (entry.isDirectory()) {\n      if (IGNORED_DIRS.has(entry.name)) continue\n      const child = path.join(start, entry.name)\n      if (await isPythonVenvDir(child)) continue\n      out.push(...(await walkYamlFiles(child, depth + 1, max)))\n    } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path.extname(entry.name))) {\n      out.push(path.join(start, entry.name))\n    }\n  }\n  return out\n}\n\n// Multi-document YAML with kind/metadata.name. We keep the matching simple:\n// any file whose first doc looks k8s-shaped. The match is on `kind` only —\n// random YAML configs (db-config.yaml, etc.) are usually flat objects with no\n// `kind` field, so they're ignored without false positives.\nexport async function addK8sResources(\n  graph: NeatGraph,\n  scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n  let nodesAdded = 0\n  const files = await walkYamlFiles(scanPath)\n  for (const file of files) {\n    const content = await fs.readFile(file, 'utf8')\n    let docs: K8sDoc[]\n    try {\n      docs = parseAllDocuments(content).map((d) => d.toJSON() as K8sDoc)\n    } catch {\n      continue\n    }\n    for (const doc of docs) {\n      if (!doc?.kind || !doc.metadata?.name) continue\n      const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind]\n      if (!infraKind) continue\n      const namespaced = doc.metadata.namespace\n        ? `${doc.metadata.namespace}/${doc.metadata.name}`\n        : doc.metadata.name\n      const node = makeInfraNode(infraKind, namespaced, 'kubernetes')\n      if (!graph.hasNode(node.id)) {\n        graph.addNode(node.id, node)\n        nodesAdded++\n      }\n    }\n  }\n  return { nodesAdded, edgesAdded: 0 }\n}\n","import { existsSync } from 'node:fs'\nimport path from 'node:path'\nimport type { GraphEdge, GraphNode } from '@neat.is/types'\nimport { NodeType, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\n\n// Drop any FileNode left with no edges. A FileNode exists to originate a\n// relationship (file-awareness.md §1); once its CALLS / CONTAINS edges are\n// retired and no OBSERVED traffic remains, the bare node carries nothing and\n// goes too. Called after edge retirement so the snapshot stays consistent with\n// what's on disk. Returns the count dropped.\nfunction dropOrphanedFileNodes(graph: NeatGraph): number {\n  const orphans: string[] = []\n  graph.forEachNode((id, attrs) => {\n    if ((attrs as GraphNode).type !== NodeType.FileNode) return\n    if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {\n      orphans.push(id)\n    }\n  })\n  for (const id of orphans) graph.dropNode(id)\n  return orphans.length\n}\n\n// Drop every EXTRACTED edge whose evidence.file matches the given path, then\n// sweep any FileNode the retirement left orphaned. Called from watch.ts before\n// re-running an extract phase, so the producer's idempotent re-write recreates\n// only the edges that still apply. Edges from the deleted code stay deleted.\n// See docs/contracts/static-extraction.md §Ghost-edge cleanup. Mutation\n// authority lives under extract/* per ADR-030, so the dropEdge call must happen\n// here, not in watch.ts. The returned count is edges dropped (FileNode cleanup\n// is a structural side effect, not a ghost-edge count).\nexport function retireEdgesByFile(graph: NeatGraph, file: string): number {\n  const normalized = file.split('\\\\').join('/')\n  const toDrop: string[] = []\n  graph.forEachEdge((id, attrs) => {\n    const edge = attrs as GraphEdge\n    if (edge.provenance !== Provenance.EXTRACTED) return\n    if (!edge.evidence?.file) return\n    if (edge.evidence.file === normalized) toDrop.push(id)\n  })\n  for (const id of toDrop) graph.dropEdge(id)\n  dropOrphanedFileNodes(graph)\n  return toDrop.length\n}\n\n// #140 — full-pass cleanup. Walk every EXTRACTED edge in the graph; if its\n// `evidence.file` cannot be resolved on disk against the scan root or any\n// discovered service directory, drop it. extractFromDirectory calls this at\n// the end of every pass so a daemon bootstrap (or a re-init after the\n// operator deleted some source) gets a snapshot consistent with what's\n// actually on disk.\n//\n// Handles the deleted-file half of the ghost-edge bug. The edited-file half\n// (file still exists, producer no longer emits the edge) is handled by\n// watch.ts's per-file `retireEdgesByFile` on the mtime trigger.\n//\n// Path resolution is tolerant: producers in this tree are inconsistent about\n// whether `evidence.file` is scanPath-relative (configs, databases, infra)\n// or service-dir-relative (calls/*). We try every candidate base before\n// concluding the file is gone — the cost is one extra `existsSync` per\n// service dir per ghost candidate, which is cheap.\nexport function retireExtractedEdgesByMissingFile(\n  graph: NeatGraph,\n  scanPath: string,\n  serviceDirs: readonly string[] = [],\n): number {\n  const toDrop: string[] = []\n  const bases = [scanPath, ...serviceDirs]\n  graph.forEachEdge((id, attrs) => {\n    const edge = attrs as GraphEdge\n    if (edge.provenance !== Provenance.EXTRACTED) return\n    const evidenceFile = edge.evidence?.file\n    if (!evidenceFile) return\n    if (path.isAbsolute(evidenceFile)) {\n      if (!existsSync(evidenceFile)) toDrop.push(id)\n      return\n    }\n    // Tolerant: the file is \"present\" if any base resolves it.\n    const found = bases.some((base) => existsSync(path.join(base, evidenceFile)))\n    if (!found) toDrop.push(id)\n  })\n  for (const id of toDrop) graph.dropEdge(id)\n  dropOrphanedFileNodes(graph)\n  return toDrop.length\n}\n","import { promises as fs } from 'node:fs'\nimport type { GraphEdge, GraphNode } from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\n\n// Diff a snapshot on disk (or fetched over HTTP) against the live in-memory\n// graph. The \"base\" is the snapshot you supplied; the \"current\" is whatever\n// the server has loaded right now. Symmetric in shape, but consumers usually\n// want to read it as \"what changed since base.\"\n//\n// The shape mirrors the issue's spec (#77):\n//   { added: { nodes, edges }, removed: { nodes, edges },\n//     changed: { nodes: [{id, before, after}], edges: [{id, before, after}] } }\n//\n// Both timestamps are echoed back so the caller doesn't have to track them\n// separately.\n\ninterface PersistedNodeEntry {\n  key?: string\n  attributes?: Record<string, unknown>\n}\n\ninterface PersistedEdgeEntry {\n  key?: string\n  source?: string\n  target?: string\n  attributes?: Record<string, unknown>\n}\n\nexport interface PersistedSnapshot {\n  schemaVersion?: number\n  exportedAt?: string\n  graph?: {\n    nodes?: PersistedNodeEntry[]\n    edges?: PersistedEdgeEntry[]\n  }\n}\n\nexport interface GraphDiff {\n  base: { exportedAt?: string }\n  current: { exportedAt: string }\n  added: { nodes: GraphNode[]; edges: GraphEdge[] }\n  removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n  changed: {\n    nodes: { id: string; before: GraphNode; after: GraphNode }[]\n    edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n  }\n}\n\nexport async function loadSnapshotForDiff(target: string): Promise<PersistedSnapshot> {\n  if (/^https?:\\/\\//i.test(target)) {\n    const res = await fetch(target)\n    if (!res.ok) {\n      throw new Error(`fetch ${target} failed: ${res.status} ${res.statusText}`)\n    }\n    return (await res.json()) as PersistedSnapshot\n  }\n  const raw = await fs.readFile(target, 'utf8')\n  return JSON.parse(raw) as PersistedSnapshot\n}\n\nfunction indexEntries<T>(\n  entries: { key?: string; attributes?: Record<string, unknown> }[] | undefined,\n): Map<string, T> {\n  const m = new Map<string, T>()\n  if (!entries) return m\n  for (const entry of entries) {\n    const id = (entry.attributes?.id as string | undefined) ?? entry.key\n    if (!id) continue\n    m.set(id, entry.attributes as T)\n  }\n  return m\n}\n\nexport function computeGraphDiff(\n  liveGraph: NeatGraph,\n  baseSnapshot: PersistedSnapshot,\n  currentExportedAt: string = new Date().toISOString(),\n): GraphDiff {\n  const baseNodes = indexEntries<GraphNode>(baseSnapshot.graph?.nodes)\n  const baseEdges = indexEntries<GraphEdge>(baseSnapshot.graph?.edges)\n\n  const liveNodes = new Map<string, GraphNode>()\n  liveGraph.forEachNode((id, attrs) => liveNodes.set(id, attrs as GraphNode))\n  const liveEdges = new Map<string, GraphEdge>()\n  liveGraph.forEachEdge((id, attrs) => liveEdges.set(id, attrs as GraphEdge))\n\n  const result: GraphDiff = {\n    base: { exportedAt: baseSnapshot.exportedAt },\n    current: { exportedAt: currentExportedAt },\n    added: { nodes: [], edges: [] },\n    removed: { nodes: [], edges: [] },\n    changed: { nodes: [], edges: [] },\n  }\n\n  for (const [id, after] of liveNodes) {\n    const before = baseNodes.get(id)\n    if (!before) {\n      result.added.nodes.push(after)\n    } else if (!shallowEqual(before, after)) {\n      result.changed.nodes.push({ id, before, after })\n    }\n  }\n  for (const [id, before] of baseNodes) {\n    if (!liveNodes.has(id)) result.removed.nodes.push(before)\n  }\n  for (const [id, after] of liveEdges) {\n    const before = baseEdges.get(id)\n    if (!before) {\n      result.added.edges.push(after)\n    } else if (!shallowEqual(before, after)) {\n      result.changed.edges.push({ id, before, after })\n    }\n  }\n  for (const [id, before] of baseEdges) {\n    if (!liveEdges.has(id)) result.removed.edges.push(before)\n  }\n\n  return result\n}\n\n// Stable JSON comparison. Snapshot order isn't guaranteed, so canonicalising\n// keys before stringify keeps the comparison robust against re-ordered fields.\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n  return canonicalJson(a) === canonicalJson(b)\n}\n\nfunction canonicalJson(value: unknown): string {\n  return JSON.stringify(value, (_key, v) => {\n    if (v && typeof v === 'object' && !Array.isArray(v)) {\n      return Object.keys(v as Record<string, unknown>)\n        .sort()\n        .reduce<Record<string, unknown>>((acc, k) => {\n          acc[k] = (v as Record<string, unknown>)[k]\n          return acc\n        }, {})\n    }\n    return v\n  })\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { Provenance, observedEdgeId } from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\n\nexport const SCHEMA_VERSION = 4\n\nexport interface PersistedGraph {\n  schemaVersion: number\n  exportedAt: string\n  graph: ReturnType<NeatGraph['export']>\n}\n\n// v1 → v2: ServiceNode shed `pgDriverVersion` (ADR-019). Compat traversal reads\n// `dependencies[driver]` instead. Strip the field from any v1 snapshot rather\n// than hard-failing — a stale snapshot on disk shouldn't cost a re-extract.\nfunction migrateV1ToV2(payload: PersistedGraph): PersistedGraph {\n  const nodes = (payload.graph as { nodes?: Array<{ attributes?: Record<string, unknown> }> })\n    .nodes\n  if (Array.isArray(nodes)) {\n    for (const node of nodes) {\n      if (node.attributes && 'pgDriverVersion' in node.attributes) {\n        delete node.attributes.pgDriverVersion\n      }\n    }\n  }\n  return { ...payload, schemaVersion: 2 }\n}\n\n// v2 → v3: Provenance enum shrinks to four values (ADR-068). Any edge whose\n// provenance still carries the pre-v0.3.5 'FRONTIER' literal is rewritten to\n// Provenance.OBSERVED on load. The target ref is unchanged — FrontierNodes\n// remain placeholders for unresolved peers; only how the edge was labelled\n// changes. If the edge id still carries the legacy provenance segment, it\n// is re-keyed to the OBSERVED wire format so consumers that parse the id\n// (traversal, divergence query, MCP) see the same shape downstream.\n//\n// The 'FRONTIER' string literal here is the only place in the codebase that\n// recognises the legacy value; the Rule 1 contract scan exempts persist.ts\n// for exactly this reason.\n// v3 → v4: ServiceNode identity gains an optional env discriminator\n// (ADR-074 §2). The v4 wire format reads as a superset of v3 — the\n// env-less `service:<name>` form is preserved as the env=`'unknown'`\n// node and the v3 → v4 migration is a version-only bump.\n//\n// Edges and node ids that pre-date the env discriminator remain valid v4\n// ids; no rewrite is needed. Idempotent — re-running on a v4 snapshot\n// produces an identical payload.\nfunction migrateV3ToV4(payload: PersistedGraph): PersistedGraph {\n  return { ...payload, schemaVersion: 4 }\n}\n\nfunction migrateV2ToV3(payload: PersistedGraph): PersistedGraph {\n  const edges = (payload.graph as {\n    edges?: Array<{\n      key?: string\n      attributes?: Record<string, unknown>\n    }>\n  }).edges\n  if (Array.isArray(edges)) {\n    for (const edge of edges) {\n      const attrs = edge.attributes\n      // 'FRONTIER' is the pre-v0.3.5 literal — read-only here, never written\n      // by current producers. The rewrite swaps to Provenance.OBSERVED.\n      if (!attrs || attrs.provenance !== 'FRONTIER') continue\n      attrs.provenance = Provenance.OBSERVED\n      const type = typeof attrs.type === 'string' ? attrs.type : undefined\n      const source = typeof attrs.source === 'string' ? attrs.source : undefined\n      const target = typeof attrs.target === 'string' ? attrs.target : undefined\n      if (type && source && target) {\n        const newId = observedEdgeId(source, target, type)\n        attrs.id = newId\n        if (edge.key) edge.key = newId\n      }\n    }\n  }\n  return { ...payload, schemaVersion: 3 }\n}\n\nasync function ensureDir(filePath: string): Promise<void> {\n  await fs.mkdir(path.dirname(filePath), { recursive: true })\n}\n\nexport async function saveGraphToDisk(graph: NeatGraph, outPath: string): Promise<void> {\n  await ensureDir(outPath)\n  const payload: PersistedGraph = {\n    schemaVersion: SCHEMA_VERSION,\n    exportedAt: new Date().toISOString(),\n    graph: graph.export(),\n  }\n  // Atomic write: drop into <name>.tmp first, then rename. A crash mid-write\n  // leaves the previous snapshot intact instead of a half-truncated file.\n  const tmp = `${outPath}.tmp`\n  await fs.writeFile(tmp, JSON.stringify(payload), 'utf8')\n  await fs.rename(tmp, outPath)\n}\n\nexport async function loadGraphFromDisk(graph: NeatGraph, outPath: string): Promise<void> {\n  let raw: string\n  try {\n    raw = await fs.readFile(outPath, 'utf8')\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return\n    throw err\n  }\n  let payload = JSON.parse(raw) as PersistedGraph\n  if (payload.schemaVersion === 1) {\n    payload = migrateV1ToV2(payload)\n  }\n  if (payload.schemaVersion === 2) {\n    payload = migrateV2ToV3(payload)\n  }\n  if (payload.schemaVersion === 3) {\n    payload = migrateV3ToV4(payload)\n  }\n  if (payload.schemaVersion !== SCHEMA_VERSION) {\n    throw new Error(\n      `persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`,\n    )\n  }\n  graph.clear()\n  graph.import(payload.graph)\n}\n\nexport interface PersistLoopOptions {\n  // How often the periodic background save fires. Defaults to 60s.\n  intervalMs?: number\n  // Whether a SIGTERM/SIGINT should flush the graph and then exit the process.\n  // Defaults to true, which is what the standalone owners (`neat serve`,\n  // `neat watch`) want: the persist loop is the only thing holding the process\n  // open, so it owns the shutdown — flush, then exit.\n  //\n  // The daemon owns its own orderly shutdown (it closes its listeners, flushes\n  // every slot, clears its `daemon.json` + the machine-wide discovery copy, and\n  // removes its pid file before exiting). It runs many persist loops, one per\n  // project, and an exiting signal handler inside any of them would end the\n  // process out from under that teardown — leaving the discovery copy and pid\n  // file behind. So the daemon passes `false`: each loop still saves on its\n  // interval and unhooks cleanly on teardown, but the daemon decides when the\n  // process exits.\n  exitOnSignal?: boolean\n}\n\n// Periodic save + (optionally) a best-effort save-and-exit on SIGTERM/SIGINT.\n// Returns a cleanup that clears the interval and unhooks any signal handlers —\n// important for tests so they don't keep the process alive, and for the daemon\n// so a torn-down slot's loop stops reacting to signals.\nexport function startPersistLoop(\n  graph: NeatGraph,\n  outPath: string,\n  opts: PersistLoopOptions = {},\n): () => void {\n  const intervalMs = opts.intervalMs ?? 60_000\n  const exitOnSignal = opts.exitOnSignal ?? true\n  let stopped = false\n\n  const tick = async (): Promise<void> => {\n    if (stopped) return\n    try {\n      await saveGraphToDisk(graph, outPath)\n    } catch (err) {\n      console.error('persist: periodic save failed', err)\n    }\n  }\n\n  const interval = setInterval(() => {\n    void tick()\n  }, intervalMs)\n\n  const onSignal = exitOnSignal\n    ? (signal: NodeJS.Signals): void => {\n        void (async () => {\n          try {\n            await saveGraphToDisk(graph, outPath)\n          } catch (err) {\n            console.error(`persist: ${signal} save failed`, err)\n          } finally {\n            process.exit(0)\n          }\n        })()\n      }\n    : null\n\n  if (onSignal) {\n    process.on('SIGTERM', onSignal)\n    process.on('SIGINT', onSignal)\n  }\n\n  return () => {\n    stopped = true\n    clearInterval(interval)\n    if (onSignal) {\n      process.off('SIGTERM', onSignal)\n      process.off('SIGINT', onSignal)\n    }\n  }\n}\n\n// Snapshot merge (ADR-074 §1) lives in ingest.ts — that's the mutation-\n// authority boundary per the lifecycle contract (Rule 3 / ADR-030). The\n// merge is a form of ingestion: an external snapshot lands on the live graph\n// the same way an OTel span does, preserving the EXTRACTED + OBSERVED\n// coexistence contract along the way.\n","// Project registry — owns the per-project state that lives alongside the\n// graph map: snapshot/error/stale paths, scan path, optional search index.\n//\n// Routes use it via `resolve(project)`. Server / watch construct it once at\n// boot and pass it to buildApi. Tests can mock it with a small literal.\n\nimport path from 'node:path'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT, getGraph } from './graph.js'\nimport type { SearchIndex } from './search.js'\n\nexport interface ProjectPaths {\n  snapshotPath: string\n  errorsPath: string\n  staleEventsPath: string\n  embeddingsCachePath: string\n  // Policy-violations log per ADR-041 § Append-only ndjson sidecars. Lives\n  // in the same neat-out directory as the other ndjson sidecars; daemons\n  // wire PolicyViolationsLog to it.\n  policyViolationsPath: string\n}\n\n// Default project keeps the legacy filenames so existing M5 / β / γ users\n// see no behaviour change. Named projects fan out by name (ADR-026).\nexport function pathsForProject(project: string, baseDir: string): ProjectPaths {\n  if (project === DEFAULT_PROJECT) {\n    return {\n      snapshotPath: path.join(baseDir, 'graph.json'),\n      errorsPath: path.join(baseDir, 'errors.ndjson'),\n      staleEventsPath: path.join(baseDir, 'stale-events.ndjson'),\n      embeddingsCachePath: path.join(baseDir, 'embeddings.json'),\n      policyViolationsPath: path.join(baseDir, 'policy-violations.ndjson'),\n    }\n  }\n  return {\n    snapshotPath: path.join(baseDir, `${project}.json`),\n    errorsPath: path.join(baseDir, `errors.${project}.ndjson`),\n    staleEventsPath: path.join(baseDir, `stale-events.${project}.ndjson`),\n    embeddingsCachePath: path.join(baseDir, `embeddings.${project}.json`),\n    policyViolationsPath: path.join(baseDir, `policy-violations.${project}.ndjson`),\n  }\n}\n\nexport interface ProjectContext {\n  name: string\n  graph: NeatGraph\n  scanPath?: string\n  paths: ProjectPaths\n  searchIndex?: SearchIndex\n}\n\nexport class Projects {\n  private contexts = new Map<string, ProjectContext>()\n\n  upsert(ctx: ProjectContext): void {\n    this.contexts.set(ctx.name, ctx)\n  }\n\n  set(\n    name: string,\n    init: Omit<ProjectContext, 'name' | 'graph'> & { graph?: NeatGraph },\n  ): ProjectContext {\n    const ctx: ProjectContext = {\n      name,\n      graph: init.graph ?? getGraph(name),\n      scanPath: init.scanPath,\n      paths: init.paths,\n      searchIndex: init.searchIndex,\n    }\n    this.contexts.set(name, ctx)\n    return ctx\n  }\n\n  get(name: string): ProjectContext | undefined {\n    return this.contexts.get(name)\n  }\n\n  has(name: string): boolean {\n    return this.contexts.has(name)\n  }\n\n  list(): string[] {\n    return [...this.contexts.keys()].sort()\n  }\n\n  attachSearchIndex(name: string, index: SearchIndex | undefined): void {\n    const ctx = this.contexts.get(name)\n    if (ctx) ctx.searchIndex = index\n  }\n}\n\n// Parses NEAT_PROJECTS=a,b,c; trims whitespace, drops empty entries.\n// `default` is implicit (always loaded), so callers usually filter it out\n// before iterating extra projects.\nexport function parseExtraProjects(raw: string | undefined): string[] {\n  if (!raw) return []\n  return raw\n    .split(',')\n    .map((p) => p.trim())\n    .filter((p) => p.length > 0 && p !== DEFAULT_PROJECT)\n}\n","/**\n * Machine-level project registry + machine-wide daemon discovery.\n *\n * This module owns two surfaces under `~/.neat/`:\n *\n *  1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One\n *     file, per-user, machine-local, not synced. Under the project-daemon\n *     contract (ADR-096) it is no longer the coordination point — it is read\n *     once for migration and otherwise left to the additive writes the daemon\n *     and orchestrator still perform. The read-modify-write helpers below keep\n *     their atomic-write + exclusive-lock machinery for that legacy surface.\n *\n *  2. The machine-wide daemon discovery directory at `~/.neat/daemons/`\n *     (ADR-096 §6). One file per running daemon — `<project>.json` — each owned\n *     solely by the daemon that wrote it (on start) and removes it (on graceful\n *     stop). Discovery is **append-only and lock-free**: a reader scans the\n *     directory and reconciles liveness; it never acquires a shared lock, so it\n *     can never deadlock against a daemon (#506). Losing or rebuilding the\n *     directory costs discovery convenience, not correctness — each project's\n *     own `neat-out/daemon.json` stays authoritative.\n *\n * `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`\n * verbs read discovery, falling back to the legacy registry where no daemon\n * file is present yet (the migration window before every daemon self-describes).\n *\n * The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we\n * mutate, and unlink on the way out. Crude but cross-platform; matches what\n * `proper-lockfile` does internally without pulling the dep in. It never sits\n * on the discovery path.\n */\n\nimport { promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport {\n  RegistryFileSchema,\n  type RegistryEntry,\n  type RegistryFile,\n  type RegistryStatus,\n} from '@neat.is/types'\n\nconst LOCK_TIMEOUT_MS = 5_000\nconst LOCK_RETRY_MS = 50\n\n// Resolve `~/.neat/` per call so tests can override `HOME` / `NEAT_HOME`\n// before each run without module-load order mattering.\nfunction neatHome(): string {\n  const override = process.env.NEAT_HOME\n  if (override && override.length > 0) return path.resolve(override)\n  return path.join(os.homedir(), '.neat')\n}\n\nexport function registryPath(): string {\n  return path.join(neatHome(), 'projects.json')\n}\n\nexport function registryLockPath(): string {\n  return path.join(neatHome(), 'projects.json.lock')\n}\n\n// The daemon writes its PID here on startup (daemon.ts). It's the authoritative\n// \"is a neat daemon running\" signal — more reliable than matching the lock's\n// own PID, since the daemon holds the registry lock only for brief read-modify-\n// write windows and an init that contends usually catches an orphaned lock\n// rather than the daemon mid-write.\nfunction daemonPidPath(): string {\n  return path.join(neatHome(), 'neatd.pid')\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// #366 / ADR-096 §6 — machine-wide daemon discovery, lock-free.\n//\n// Each running daemon drops a `<project>.json` file into `~/.neat/daemons/`\n// on start and removes it on graceful stop. The file is a copy of that\n// project's authoritative `neat-out/daemon.json` record. A daemon owns only\n// its own file — there is no shared file and no shared lock. Discovery reads\n// the directory and reconciles liveness; it never acquires a lock, so it can\n// never deadlock against a daemon mid-write the way the registry lock did\n// (#506). The shape mirrors what the daemon writes (keystone #508); we read it\n// as plain JSON rather than importing the daemon's writer so the two stay\n// decoupled, and validate defensively so a malformed file is skipped, not\n// fatal.\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface DaemonPorts {\n  rest: number\n  otlp: number\n  web: number\n}\n\nexport interface DaemonRecord {\n  project: string\n  // Project root whose `neat-out/` holds the authoritative record.\n  projectPath: string\n  pid: number\n  status: 'running' | 'stopped'\n  ports: DaemonPorts\n  // ISO8601.\n  startedAt: string\n  neatVersion: string\n}\n\n// A discovered daemon plus the liveness verdict the reader reconciled. `live`\n// is true only when the record claims `running` AND its pid is actually alive\n// — a daemon that crashed without clearing its file reads as `running` but\n// `live: false`, so `neat ps` never reports a ghost as up.\nexport interface DiscoveredDaemon {\n  record: DaemonRecord\n  live: boolean\n  // Where the discovery copy was read from. Useful for diagnostics and for the\n  // per-daemon verbs that clean a stale file.\n  source: string\n}\n\nexport function daemonsDir(): string {\n  return path.join(neatHome(), 'daemons')\n}\n\nfunction isFiniteInt(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v)\n}\n\n// Parse one discovery file's contents into a DaemonRecord, or undefined when\n// the shape doesn't hold. Defensive on purpose: discovery is convenience, so a\n// half-written or hand-edited file is skipped rather than crashing `neat ps`.\nfunction parseDaemonRecord(raw: string): DaemonRecord | undefined {\n  let obj: unknown\n  try {\n    obj = JSON.parse(raw)\n  } catch {\n    return undefined\n  }\n  if (typeof obj !== 'object' || obj === null) return undefined\n  const r = obj as Record<string, unknown>\n  const ports = r.ports as Record<string, unknown> | undefined\n  if (\n    typeof r.project !== 'string' ||\n    typeof r.projectPath !== 'string' ||\n    !isFiniteInt(r.pid) ||\n    (r.status !== 'running' && r.status !== 'stopped') ||\n    typeof r.startedAt !== 'string' ||\n    typeof r.neatVersion !== 'string' ||\n    !ports ||\n    !isFiniteInt(ports.rest) ||\n    !isFiniteInt(ports.otlp) ||\n    !isFiniteInt(ports.web)\n  ) {\n    return undefined\n  }\n  return {\n    project: r.project,\n    projectPath: r.projectPath,\n    pid: r.pid,\n    status: r.status,\n    ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },\n    startedAt: r.startedAt,\n    neatVersion: r.neatVersion,\n  }\n}\n\n// Liveness for a discovered daemon. Exported (via the default probe) so the\n// verbs and tests reconcile the same way: `running` claim AND pid alive.\nexport function isPidAlive(pid: number): boolean {\n  return isPidAliveDefault(pid)\n}\n\nexport interface DiscoveryProbe {\n  isPidAlive(pid: number): boolean\n}\n\nconst defaultDiscoveryProbe: DiscoveryProbe = { isPidAlive: isPidAliveDefault }\n\n/**\n * Scan `~/.neat/daemons/` and return every well-formed discovery record with\n * its reconciled liveness. Lock-free: a plain directory read, no rendezvous.\n *\n * A missing directory (no daemon has ever started under this model) yields an\n * empty list — first run, nothing to discover. Malformed or unreadable files\n * are skipped silently; discovery degrades to \"fewer entries,\" never an error.\n */\nexport async function discoverDaemons(\n  probe: DiscoveryProbe = defaultDiscoveryProbe,\n): Promise<DiscoveredDaemon[]> {\n  const dir = daemonsDir()\n  let names: string[]\n  try {\n    names = await fs.readdir(dir)\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n    throw err\n  }\n  const out: DiscoveredDaemon[] = []\n  for (const name of names) {\n    if (!name.endsWith('.json')) continue\n    const file = path.join(dir, name)\n    let raw: string\n    try {\n      raw = await fs.readFile(file, 'utf8')\n    } catch {\n      continue\n    }\n    const record = parseDaemonRecord(raw)\n    if (!record) continue\n    const live = record.status === 'running' && probe.isPidAlive(record.pid)\n    out.push({ record, live, source: file })\n  }\n  out.sort((a, b) => a.record.project.localeCompare(b.record.project))\n  return out\n}\n\n/**\n * Remove a daemon's discovery file. A daemon owns its own file, so this is for\n * the per-daemon verbs reconciling a record whose daemon is gone (a crashed\n * daemon that never cleared its own file) — never a rendezvous another process\n * coordinates through. Best-effort: an already-absent file is success.\n */\nexport async function removeDaemonRecord(source: string): Promise<void> {\n  await fs.unlink(source).catch(() => {})\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// #366 / ADR-096 §8 — migration off the global registry.\n//\n// Installs carrying `~/.neat/projects.json` map their registered projects onto\n// per-project daemons on first run under this model. The discovery directory is\n// authoritative for \"what's running\"; the registry is read once to surface\n// projects that were registered before any daemon self-described, so the\n// machine-wide verbs still see them during the migration window. The registry\n// is no longer the coordination surface — nothing here writes it back.\n// ─────────────────────────────────────────────────────────────────────────\n\n// A unified view for the machine-wide verbs: a discovered daemon when one is\n// present, otherwise a legacy registry entry projected into the same shape so\n// `neat ps` / `neat list` render one table regardless of which surface the\n// project lives on yet.\nexport interface MachineProject {\n  project: string\n  projectPath: string\n  // 'running' / 'stopped' come from a live discovery record; 'registered'\n  // marks a legacy registry entry with no daemon file yet (migration window).\n  state: 'running' | 'stopped' | 'registered'\n  // Present only when a discovery record backs this row.\n  ports?: DaemonPorts\n  // Legacy registry status when the row came from the registry; undefined for\n  // a discovery-backed row.\n  registryStatus?: RegistryStatus\n  pid?: number\n}\n\n/**\n * The machine-wide project view the CLI verbs render. Discovery wins: every\n * `~/.neat/daemons/` record becomes a row (running or stopped per liveness).\n * Legacy registry entries whose path isn't already covered by a discovery\n * record are folded in as `registered` rows so a pre-#508 install still lists\n * its projects. Keyed on resolved path so a registry entry and its daemon\n * record collapse to one row.\n *\n * Read-only on both surfaces. The registry is read once for migration; nothing\n * here writes it back, in keeping with ADR-096 §8.\n */\nexport async function listMachineProjects(\n  probe: DiscoveryProbe = defaultDiscoveryProbe,\n): Promise<MachineProject[]> {\n  const discovered = await discoverDaemons(probe)\n  const byPath = new Map<string, MachineProject>()\n  for (const d of discovered) {\n    const key = await normalizeProjectPath(d.record.projectPath)\n    byPath.set(key, {\n      project: d.record.project,\n      projectPath: d.record.projectPath,\n      state: d.live ? 'running' : 'stopped',\n      ports: d.record.ports,\n      pid: d.record.pid,\n    })\n  }\n\n  // Migration read: legacy registry entries not already covered by a daemon\n  // record. Read once, never written back.\n  let legacy: RegistryEntry[] = []\n  try {\n    legacy = (await readRegistry()).projects\n  } catch {\n    // A corrupt legacy file shouldn't sink discovery — the daemons we found are\n    // still valid. Skip the legacy fold.\n    legacy = []\n  }\n  for (const entry of legacy) {\n    const key = await normalizeProjectPath(entry.path)\n    if (byPath.has(key)) continue\n    byPath.set(key, {\n      project: entry.name,\n      projectPath: entry.path,\n      state: 'registered',\n      registryStatus: entry.status,\n    })\n  }\n\n  return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project))\n}\n\n// Find a discovery record by project name, with its reconciled liveness. The\n// per-daemon verbs key on the project name the operator types; discovery is the\n// source of truth for whether a daemon is running and which pid to signal.\nexport async function findDaemonByProject(\n  name: string,\n  probe: DiscoveryProbe = defaultDiscoveryProbe,\n): Promise<DiscoveredDaemon | undefined> {\n  const discovered = await discoverDaemons(probe)\n  return discovered.find((d) => d.record.project === name)\n}\n\n// Signal a running daemon to shut down via its discovery-recorded pid. SIGTERM\n// is the graceful-stop signal every daemon already wires (neatd.ts) — the\n// daemon clears its own discovery file on the way out. Returns true when the\n// signal was delivered, false when the pid was already gone (nothing to stop).\nexport function signalDaemonStop(pid: number): boolean {\n  try {\n    process.kill(pid, 'SIGTERM')\n    return true\n  } catch (err) {\n    // ESRCH → already gone; treat as a no-op success of intent. EPERM and the\n    // rest surface as \"couldn't signal\" so the verb can report honestly.\n    if ((err as NodeJS.ErrnoException).code === 'ESRCH') return false\n    throw err\n  }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// #432 — distinguish a live daemon from a genuinely stale lock.\n//\n// `neat init` used to hang the full timeout on any contended lock and then\n// print one remediation: \"remove the file by hand.\" That advice is actively\n// dangerous when a neat daemon is alive — the daemon grabs the lock for its\n// own registry writes, so hand-removing it races the daemon and corrupts the\n// registry for the live process. The lock now carries the holder PID, and the\n// timeout (or first contention with a live daemon) resolves to a message that\n// names who holds it and what's safe to do.\n// ─────────────────────────────────────────────────────────────────────────\n\nexport type LockHolder =\n  | { kind: 'daemon'; pid: number }\n  | { kind: 'command'; pid: number }\n  | { kind: 'stale' }\n\n// Probes the holder-resolution logic depends on, broken out so tests can drive\n// each branch without a real daemon or live PIDs.\nexport interface LockHolderProbe {\n  // POSIX liveness via `process.kill(pid, 0)`: true if the process exists\n  // (including EPERM — alive but owned by another user), false if it's gone.\n  isPidAlive(pid: number): boolean\n  // PID recorded in `~/.neat/neatd.pid`, or undefined if there's no pidfile.\n  daemonPidFromFile(): Promise<number | undefined>\n  // Whether the daemon's always-open `/health` endpoint answers. Confirms a\n  // live neatd.pid is actually our daemon and not a reused PID.\n  daemonResponds(): Promise<boolean>\n}\n\nfunction isPidAliveDefault(pid: number): boolean {\n  try {\n    process.kill(pid, 0)\n    return true\n  } catch (err) {\n    // ESRCH → no such process (dead). EPERM → exists but owned by another user\n    // (alive). Treat anything else as not-alive: we'd rather under-claim a live\n    // holder than wrongly block on a lock we can't verify.\n    return (err as NodeJS.ErrnoException).code === 'EPERM'\n  }\n}\n\nasync function readPidFile(file: string): Promise<number | undefined> {\n  try {\n    const raw = await fs.readFile(file, 'utf8')\n    const pid = Number.parseInt(raw.trim(), 10)\n    return Number.isInteger(pid) && pid > 0 ? pid : undefined\n  } catch {\n    return undefined\n  }\n}\n\nconst defaultLockHolderProbe: LockHolderProbe = {\n  isPidAlive: isPidAliveDefault,\n  daemonPidFromFile: () => readPidFile(daemonPidPath()),\n  async daemonResponds(): Promise<boolean> {\n    const base = process.env.NEAT_API_URL ?? 'http://localhost:8080'\n    try {\n      // `/health` stays unauthenticated in every mode (auth.ts), so a reachable\n      // daemon answers it even with a bearer token set. Any HTTP response — not\n      // just 2xx — means a daemon owns the port. A short timeout keeps the\n      // fail-fast path well under a second.\n      await fetch(`${base}/health`, { signal: AbortSignal.timeout(750) })\n      return true\n    } catch {\n      return false\n    }\n  },\n}\n\n// Read the PID a lock holder recorded when it created the lock. Undefined for a\n// legacy empty lock, an unreadable file, or a lock unlinked out from under us.\nasync function readLockPid(lockPath: string): Promise<number | undefined> {\n  return readPidFile(lockPath)\n}\n\n// Decide who holds (or orphaned) the lock. A live daemon dominates: while neatd\n// is alive, hand-removing the lock is never safe, so we surface the daemon\n// message even when the lock itself is an empty orphan. We never classify our\n// own process as the blocking daemon — a daemon serializing two of its own\n// registry writes contends with itself briefly and should just retry.\nexport async function classifyLockHolder(\n  lockPath: string,\n  probe: LockHolderProbe = defaultLockHolderProbe,\n): Promise<LockHolder> {\n  const lockPid = await readLockPid(lockPath)\n  const daemonPid = await probe.daemonPidFromFile()\n  if (\n    daemonPid !== undefined &&\n    daemonPid !== process.pid &&\n    probe.isPidAlive(daemonPid) &&\n    // The lock already names the daemon, or the daemon answers on its port.\n    // Either confirms a live daemon is in the picture (the second guards\n    // against a stale pidfile whose PID got reused).\n    (daemonPid === lockPid || (await probe.daemonResponds()))\n  ) {\n    return { kind: 'daemon', pid: daemonPid }\n  }\n  if (lockPid !== undefined && lockPid !== process.pid && probe.isPidAlive(lockPid)) {\n    return { kind: 'command', pid: lockPid }\n  }\n  return { kind: 'stale' }\n}\n\nexport function lockHolderMessage(holder: LockHolder, lockPath: string, timeoutMs: number): string {\n  switch (holder.kind) {\n    case 'daemon':\n      return (\n        `The neat daemon (pid ${holder.pid}) is holding the registry lock. ` +\n        'Register this project through the daemon, or stop neatd and re-run `neat init`.'\n      )\n    case 'command':\n      return (\n        `Another neat command (pid ${holder.pid}) is holding the registry lock. ` +\n        \"Wait for it to finish, or check `ps` if you're not sure what's running.\"\n      )\n    case 'stale':\n      return (\n        `neat registry: timed out after ${timeoutMs}ms waiting for ${lockPath}. ` +\n        'Another neat process is holding the lock; if no such process exists, remove the file by hand.'\n      )\n  }\n}\n\n/**\n * Path normalisation per ADR-048 #7. Two `init` calls from different relative\n * paths to the same dir must collapse to one entry. `path.resolve` handles\n * relative-to-cwd; we pass it through `fs.realpath` when the dir exists so\n * symlinked paths land on the same canonical entry too.\n */\nexport async function normalizeProjectPath(input: string): Promise<string> {\n  const resolved = path.resolve(input)\n  try {\n    return await fs.realpath(resolved)\n  } catch {\n    return resolved\n  }\n}\n\n/**\n * tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on\n * disk before rename swaps the inode; rename itself is atomic on POSIX.\n *\n * Exported so the init flow and test harnesses can use the same helper.\n */\nexport async function writeAtomically(target: string, contents: string): Promise<void> {\n  await fs.mkdir(path.dirname(target), { recursive: true })\n  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`\n  const fd = await fs.open(tmp, 'w')\n  try {\n    await fd.writeFile(contents, 'utf8')\n    await fd.sync()\n  } finally {\n    await fd.close()\n  }\n  await fs.rename(tmp, target)\n}\n\nasync function acquireLock(\n  lockPath: string,\n  timeoutMs: number = LOCK_TIMEOUT_MS,\n  probe: LockHolderProbe = defaultLockHolderProbe,\n): Promise<void> {\n  const deadline = Date.now() + timeoutMs\n  await fs.mkdir(path.dirname(lockPath), { recursive: true })\n  let probedHolder = false\n  while (true) {\n    try {\n      const fd = await fs.open(lockPath, 'wx')\n      try {\n        // Stamp the lock with our PID so a contender can name who holds it and\n        // tell a live holder apart from a stale file. Best-effort: an empty\n        // lock still excludes correctly, it just can't be diagnosed.\n        await fd.writeFile(`${process.pid}\\n`, 'utf8')\n      } finally {\n        await fd.close()\n      }\n      return\n    } catch (err) {\n      const code = (err as NodeJS.ErrnoException).code\n      if (code !== 'EEXIST') throw err\n      // A live daemon holds the registry continuously enough that spinning the\n      // full timeout is pointless — surface the routed remediation on the first\n      // contention. Peer commands and stale locks fall through to the retry:\n      // peers clear on their own, and a stale lock wants the timeout's guidance.\n      if (!probedHolder) {\n        probedHolder = true\n        const holder = await classifyLockHolder(lockPath, probe)\n        if (holder.kind === 'daemon') throw new Error(lockHolderMessage(holder, lockPath, timeoutMs))\n      }\n      if (Date.now() >= deadline) {\n        const holder = await classifyLockHolder(lockPath, probe)\n        throw new Error(lockHolderMessage(holder, lockPath, timeoutMs))\n      }\n      await new Promise((r) => setTimeout(r, LOCK_RETRY_MS))\n    }\n  }\n}\n\nasync function releaseLock(lockPath: string): Promise<void> {\n  await fs.unlink(lockPath).catch(() => {})\n}\n\nasync function withLock<T>(fn: () => Promise<T>): Promise<T> {\n  const lock = registryLockPath()\n  await acquireLock(lock)\n  try {\n    return await fn()\n  } finally {\n    await releaseLock(lock)\n  }\n}\n\n/**\n * Read the registry from disk. Returns an empty registry if the file does\n * not exist yet — first run, never registered anything.\n *\n * Throws on parse / schema errors. The contract is single-source-of-truth;\n * a corrupt file is louder than a silent reset.\n */\nexport async function readRegistry(): Promise<RegistryFile> {\n  const file = registryPath()\n  let raw: string\n  try {\n    raw = await fs.readFile(file, 'utf8')\n  } catch (err) {\n    if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n      return { version: 1, projects: [] }\n    }\n    throw err\n  }\n  const parsed = JSON.parse(raw)\n  return RegistryFileSchema.parse(parsed)\n}\n\nasync function writeRegistry(reg: RegistryFile): Promise<void> {\n  // Re-parse before writing to surface schema drift introduced by callers\n  // mutating the in-memory object directly.\n  const validated = RegistryFileSchema.parse(reg)\n  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + '\\n')\n}\n\nexport interface AddProjectOptions {\n  name: string\n  path: string\n  languages?: string[]\n  status?: RegistryStatus\n}\n\nexport class ProjectNameCollisionError extends Error {\n  readonly projectName: string\n  constructor(name: string) {\n    super(`neat registry: a project named \"${name}\" is already registered`)\n    this.name = 'ProjectNameCollisionError'\n    this.projectName = name\n  }\n}\n\n/**\n * Register a project, or update its `lastSeenAt` if the same path is already\n * registered under the same name (idempotent re-init).\n *\n * Hard error on name collision against a different path — ADR-046 #7. The\n * caller can recover by passing `--project <new-name>`.\n */\nexport async function addProject(opts: AddProjectOptions): Promise<RegistryEntry> {\n  const resolvedPath = await normalizeProjectPath(opts.path)\n  return withLock(async () => {\n    const reg = await readRegistry()\n    const byName = reg.projects.find((p) => p.name === opts.name)\n    const byPath = reg.projects.find((p) => p.path === resolvedPath)\n\n    if (byName && byName.path !== resolvedPath) {\n      throw new ProjectNameCollisionError(opts.name)\n    }\n\n    const now = new Date().toISOString()\n\n    if (byName && byName.path === resolvedPath) {\n      // Idempotent re-register: same name, same path. Refresh languages /\n      // status if the caller passed new ones.\n      byName.lastSeenAt = now\n      if (opts.languages) byName.languages = opts.languages\n      if (opts.status) byName.status = opts.status\n      await writeRegistry(reg)\n      return byName\n    }\n\n    if (byPath && byPath.name !== opts.name) {\n      // Same dir already registered under a different name. Treat as a\n      // collision so the user is forced to decide which name wins.\n      throw new ProjectNameCollisionError(byPath.name)\n    }\n\n    const entry: RegistryEntry = {\n      name: opts.name,\n      path: resolvedPath,\n      registeredAt: now,\n      languages: opts.languages ?? [],\n      status: opts.status ?? 'active',\n    }\n    reg.projects.push(entry)\n    await writeRegistry(reg)\n    return entry\n  })\n}\n\nexport async function getProject(name: string): Promise<RegistryEntry | undefined> {\n  const reg = await readRegistry()\n  return reg.projects.find((p) => p.name === name)\n}\n\nexport async function listProjects(): Promise<RegistryEntry[]> {\n  const reg = await readRegistry()\n  return reg.projects\n}\n\nexport async function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry> {\n  return withLock(async () => {\n    const reg = await readRegistry()\n    const entry = reg.projects.find((p) => p.name === name)\n    if (!entry) throw new Error(`neat registry: no project named \"${name}\"`)\n    entry.status = status\n    await writeRegistry(reg)\n    return entry\n  })\n}\n\nexport async function touchLastSeen(name: string, at: string = new Date().toISOString()): Promise<void> {\n  await withLock(async () => {\n    const reg = await readRegistry()\n    const entry = reg.projects.find((p) => p.name === name)\n    if (!entry) return\n    entry.lastSeenAt = at\n    await writeRegistry(reg)\n  })\n}\n\n/**\n * Remove the registry entry for `name`. Per ADR-048 #6: this only removes the\n * registry row. It does **not** touch `neat-out/`, `policy.json`, or any user\n * file in the project directory. SDK-install rollback is a separate flow\n * (`neat-rollback.patch`) that the caller opts in to.\n */\nexport async function removeProject(name: string): Promise<RegistryEntry | undefined> {\n  return withLock(async () => {\n    const reg = await readRegistry()\n    const idx = reg.projects.findIndex((p) => p.name === name)\n    if (idx < 0) return undefined\n    const [removed] = reg.projects.splice(idx, 1)\n    await writeRegistry(reg)\n    return removed\n  })\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// #463 — prune dead-path entries so the registry doesn't accumulate zombies.\n//\n// Ephemeral tmp-dir projects (smoke runs, throwaway demos) leave entries whose\n// `path` is gone from disk. Without pruning the daemon logs an ENOENT warning\n// for each on every startup forever and `/api/health` lists them as permanent\n// `broken` rows. Pruning removes user-facing state, so it's deliberately\n// conservative: we only ever drop an entry on a *definite* ENOENT (the path is\n// genuinely gone). A transient stat failure — EACCES, EBUSY, an unmounted\n// drive — leaves the entry untouched; it isn't dead, just unreachable.\n//\n// Two modes:\n//  - Auto-prune (daemon bootstrap / reload) gates removal on a staleness TTL.\n//    An ENOENT entry is only dropped if its `lastSeenAt` is older than the TTL,\n//    so a recently-active project whose path is temporarily unavailable stays.\n//  - `neat prune` is explicit user intent, so it drops any ENOENT entry\n//    immediately regardless of age.\n// ─────────────────────────────────────────────────────────────────────────\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n// Default 7 days, mirroring NEAT_STALE_THRESHOLDS' env-override shape.\nexport const DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS\n\nexport function pruneTtlMs(): number {\n  const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS\n  if (!raw) return DEFAULT_PRUNE_TTL_MS\n  const n = Number.parseInt(raw, 10)\n  if (Number.isFinite(n) && n >= 0) return n\n  console.warn(\n    `[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`,\n  )\n  return DEFAULT_PRUNE_TTL_MS\n}\n\n// Classify a single path: 'gone' (definite ENOENT), 'present' (stat succeeded,\n// directory exists), or 'unknown' (a transient/ambiguous error — never prune).\n// Exported so the daemon and tests can probe the same logic.\nexport type PathStatus = 'gone' | 'present' | 'unknown'\n\nasync function statPathStatus(p: string): Promise<PathStatus> {\n  try {\n    const stat = await fs.stat(p)\n    return stat.isDirectory() ? 'present' : 'unknown'\n  } catch (err) {\n    return (err as NodeJS.ErrnoException).code === 'ENOENT' ? 'gone' : 'unknown'\n  }\n}\n\nexport interface PruneOptions {\n  // Override the staleness gate. Auto-prune passes a TTL; `neat prune` passes\n  // `ttlMs: 0` to drop any ENOENT entry immediately. Default is the env-driven\n  // TTL.\n  ttlMs?: number\n  // Override the path probe (tests drive ENOENT / EACCES / present branches\n  // without a real filesystem). Defaults to a real stat.\n  statPath?: (p: string) => Promise<PathStatus>\n  // Clock injection for the staleness comparison; defaults to Date.now().\n  now?: () => number\n}\n\n/**\n * Remove registry entries whose `path` is definitely gone (ENOENT). Goes\n * through the same lock + atomic-write path as every other mutation — never a\n * raw rewrite — so the contract's atomicity invariant holds.\n *\n * Staleness gate: an ENOENT entry is dropped only when `ttlMs` is 0 (explicit\n * `neat prune`) or its `lastSeenAt` (falling back to `registeredAt`) is older\n * than `ttlMs`. A fresh ENOENT entry under a non-zero TTL is left in place — the\n * daemon still marks it `broken`, the TTL is the safety margin before removal.\n *\n * Returns the entries that were removed.\n */\nexport async function pruneRegistry(opts: PruneOptions = {}): Promise<RegistryEntry[]> {\n  const ttlMs = opts.ttlMs ?? pruneTtlMs()\n  const statPath = opts.statPath ?? statPathStatus\n  const now = opts.now ?? Date.now\n\n  return withLock(async () => {\n    const reg = await readRegistry()\n    const removed: RegistryEntry[] = []\n    const kept: RegistryEntry[] = []\n    for (const entry of reg.projects) {\n      const status = await statPath(entry.path)\n      if (status !== 'gone') {\n        // Present, or a transient/ambiguous error — keep it. We only prune on a\n        // definite ENOENT.\n        kept.push(entry)\n        continue\n      }\n      // Path is gone. Drop it immediately when there's no TTL gate, otherwise\n      // only once it's been quiet longer than the TTL.\n      if (ttlMs <= 0) {\n        removed.push(entry)\n        continue\n      }\n      const lastSeen = Date.parse(entry.lastSeenAt ?? entry.registeredAt)\n      const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0)\n      if (age > ttlMs) {\n        removed.push(entry)\n      } else {\n        kept.push(entry)\n      }\n    }\n    if (removed.length === 0) return []\n    reg.projects = kept\n    await writeRegistry(reg)\n    return removed\n  })\n}\n\n","// SSE handler for the frontend-facing event stream (ADR-051 #1).\n// Subscribes to the bus in events.ts, filters by project, writes\n// `event: <type>\\ndata: <json>\\n\\n` frames to the client. An initial\n// `:open\\n\\n` comment goes out at the handshake so EventSource opens\n// right away instead of waiting on the first event or heartbeat.\n//\n// Backpressure: per-connection queue cap of 1000 outstanding writes; once\n// hit, the connection is dropped with `event: error data: { reason:\n// 'backpressure' }` per ADR-051 #8. Heartbeat: comment line every 30s\n// keeps proxies from idle-timing out (ADR-051 #3).\n\nimport type { FastifyReply, FastifyRequest } from 'fastify'\nimport {\n  EVENT_BUS_CHANNEL,\n  eventBus,\n  type NeatEventEnvelope,\n} from './events.js'\n\nexport const SSE_HEARTBEAT_MS = 30_000\nexport const SSE_BACKPRESSURE_CAP = 1000\n\nexport interface HandleSseOptions {\n  project: string\n  heartbeatMs?: number\n  backpressureCap?: number\n}\n\nexport function handleSse(\n  req: FastifyRequest,\n  reply: FastifyReply,\n  opts: HandleSseOptions,\n): void {\n  const heartbeatMs = opts.heartbeatMs ?? SSE_HEARTBEAT_MS\n  const backpressureCap = opts.backpressureCap ?? SSE_BACKPRESSURE_CAP\n\n  reply.raw.setHeader('Content-Type', 'text/event-stream')\n  reply.raw.setHeader('Cache-Control', 'no-cache, no-transform')\n  reply.raw.setHeader('Connection', 'keep-alive')\n  reply.raw.setHeader('X-Accel-Buffering', 'no')\n  reply.raw.flushHeaders?.()\n\n  // Flushing headers leaves the response body empty, so the browser's\n  // EventSource stays in CONNECTING (readyState 0) until the first body byte\n  // lands — which, on a quiet graph, is the first real event or the 30s\n  // heartbeat, whichever comes first. Write a comment line right away so the\n  // stream opens at the handshake and EventSource fires onopen immediately.\n  // A colon-prefixed comment is a no-op per the SSE spec (clients ignore it,\n  // same as the heartbeat), so it sits outside the locked ADR-051 taxonomy.\n  // Written raw, bypassing the backpressure accounting below, exactly like\n  // the heartbeat does.\n  reply.raw.write(':open\\n\\n')\n\n  let pending = 0\n  let dropped = false\n\n  const closeConnection = (): void => {\n    if (dropped) return\n    dropped = true\n    eventBus.off(EVENT_BUS_CHANNEL, listener)\n    clearInterval(heartbeat)\n    if (!reply.raw.writableEnded) reply.raw.end()\n  }\n\n  const writeFrame = (frame: string): void => {\n    if (dropped) return\n    if (pending >= backpressureCap) {\n      // Past the cap — emit one final error frame and drop. Don't try to\n      // gracefully drain; a slow consumer that's already 1000 frames behind\n      // is not going to catch up.\n      const errFrame = `event: error\\ndata: ${JSON.stringify({ reason: 'backpressure' })}\\n\\n`\n      reply.raw.write(errFrame)\n      closeConnection()\n      return\n    }\n    pending++\n    reply.raw.write(frame, () => {\n      pending = Math.max(0, pending - 1)\n    })\n  }\n\n  const listener = (envelope: NeatEventEnvelope): void => {\n    if (envelope.project !== opts.project) return\n    writeFrame(`event: ${envelope.type}\\ndata: ${JSON.stringify(envelope.payload)}\\n\\n`)\n  }\n\n  eventBus.on(EVENT_BUS_CHANNEL, listener)\n\n  const heartbeat = setInterval(() => {\n    if (dropped) return\n    reply.raw.write(':heartbeat\\n\\n')\n  }, heartbeatMs)\n  if (typeof heartbeat.unref === 'function') heartbeat.unref()\n\n  req.raw.on('close', closeConnection)\n  reply.raw.on('close', closeConnection)\n  reply.raw.on('error', closeConnection)\n}\n","// semantic_search — embedding-based node retrieval with a three-tier\n// fallback chain. The chain is settled in ADR-025; this file is the\n// implementation. Public API:\n//\n//   buildSearchIndex(graph, opts) → SearchIndex\n//   SearchIndex.search(query, limit) → { provider, matches }\n//   SearchIndex.refresh(graph)      → re-embeds new/changed nodes,\n//                                     drops vanished ones\n//\n// The `/search` route in api.ts holds a single SearchIndex, refreshing it\n// after any extraction. MCP's `semantic_search` tool reads the same shape.\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { createHash } from 'node:crypto'\nimport type { GraphNode } from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\n\nexport interface ScoredNode {\n  node: GraphNode\n  score: number\n}\n\nexport interface SearchResponse {\n  query: string\n  provider: 'ollama' | 'transformers' | 'substring'\n  matches: ScoredNode[]\n}\n\nexport interface SearchIndex {\n  readonly provider: SearchResponse['provider']\n  search(query: string, limit?: number): Promise<SearchResponse>\n  refresh(graph: NeatGraph): Promise<void>\n}\n\ninterface Embedder {\n  provider: 'ollama' | 'transformers'\n  model: string\n  dim: number\n  embed(texts: string[]): Promise<Float32Array[]>\n}\n\nconst DEFAULT_LIMIT = 10\nconst NOMIC_DIM = 768\nconst MINI_LM_DIM = 384\n\n// FrontierNodes are noise by design (placeholders that should disappear).\n// Embedding them would just clutter results.\nfunction shouldEmbed(node: GraphNode): boolean {\n  return node.type !== 'FrontierNode'\n}\n\n// Deterministic per-node text. Stable keys let the cache hit across\n// extractions when nothing material changed.\nexport function embedText(node: GraphNode): string {\n  const parts: string[] = [node.id]\n  const name = (node as { name?: string }).name\n  if (name) parts.push(name)\n  switch (node.type) {\n    case 'ServiceNode': {\n      const lang = (node as { language?: string }).language\n      if (lang) parts.push(`language=${lang}`)\n      break\n    }\n    case 'DatabaseNode': {\n      const eng = (node as { engine?: string }).engine\n      const ver = (node as { engineVersion?: string }).engineVersion\n      if (eng) parts.push(`engine=${eng}`)\n      if (ver) parts.push(`engineVersion=${ver}`)\n      break\n    }\n    case 'InfraNode': {\n      const kind = (node as { kind?: string }).kind\n      if (kind) parts.push(`kind=${kind}`)\n      break\n    }\n    case 'ConfigNode': {\n      const filePath = (node as { path?: string }).path\n      if (filePath) parts.push(`path=${filePath}`)\n      break\n    }\n    case 'RouteNode': {\n      const method = (node as { method?: string }).method\n      const tmpl = (node as { pathTemplate?: string }).pathTemplate\n      if (method) parts.push(`method=${method}`)\n      if (tmpl) parts.push(`path=${tmpl}`)\n      break\n    }\n    case 'GraphQLOperationNode': {\n      const opType = (node as { operationType?: string }).operationType\n      if (opType) parts.push(`operationType=${opType}`)\n      break\n    }\n    case 'GrpcMethodNode': {\n      const rpcService = (node as { rpcService?: string }).rpcService\n      const rpcMethod = (node as { rpcMethod?: string }).rpcMethod\n      if (rpcService) parts.push(`rpcService=${rpcService}`)\n      if (rpcMethod) parts.push(`rpcMethod=${rpcMethod}`)\n      break\n    }\n    case 'WebSocketChannelNode': {\n      const channel = (node as { channel?: string }).channel\n      if (channel) parts.push(`channel=${channel}`)\n      break\n    }\n    default:\n      break\n  }\n  return parts.join(' ')\n}\n\nfunction attrsHash(node: GraphNode): string {\n  return createHash('sha1').update(embedText(node)).digest('hex').slice(0, 16)\n}\n\nexport function cosine(a: Float32Array, b: Float32Array): number {\n  if (a.length !== b.length) return 0\n  let dot = 0\n  let na = 0\n  let nb = 0\n  for (let i = 0; i < a.length; i++) {\n    const ai = a[i] ?? 0\n    const bi = b[i] ?? 0\n    dot += ai * bi\n    na += ai * ai\n    nb += bi * bi\n  }\n  if (na === 0 || nb === 0) return 0\n  return dot / (Math.sqrt(na) * Math.sqrt(nb))\n}\n\n// ---------------------------------------------------------------- Embedders\n\nfunction ollamaHost(): string | null {\n  return process.env.OLLAMA_HOST ?? null\n}\n\nasync function ollamaReachable(host: string): Promise<boolean> {\n  try {\n    const res = await fetch(`${host.replace(/\\/$/, '')}/api/tags`, {\n      signal: AbortSignal.timeout(500),\n    })\n    return res.ok\n  } catch {\n    return false\n  }\n}\n\nfunction makeOllamaEmbedder(host: string, model = 'nomic-embed-text'): Embedder {\n  const root = host.replace(/\\/$/, '')\n  return {\n    provider: 'ollama',\n    model,\n    dim: NOMIC_DIM,\n    async embed(texts: string[]): Promise<Float32Array[]> {\n      const out: Float32Array[] = []\n      // Ollama's /api/embeddings is one-text-per-request. ≤10K nodes × ~30ms\n      // each is fine for a one-shot index build; if it ever isn't, the API\n      // also accepts batched input on /api/embed (newer routes).\n      for (const text of texts) {\n        const res = await fetch(`${root}/api/embeddings`, {\n          method: 'POST',\n          headers: { 'content-type': 'application/json' },\n          body: JSON.stringify({ model, prompt: text }),\n        })\n        if (!res.ok) {\n          throw new Error(`ollama embeddings: ${res.status} ${res.statusText}`)\n        }\n        const data = (await res.json()) as { embedding: number[] }\n        out.push(Float32Array.from(data.embedding))\n      }\n      return out\n    },\n  }\n}\n\ninterface XenovaPipeline {\n  (text: string | string[], options?: { pooling?: string; normalize?: boolean }): Promise<{\n    data: Float32Array\n  }>\n}\n\nasync function makeTransformersEmbedder(): Promise<Embedder | null> {\n  let pipelineFn: ((task: string, model: string) => Promise<XenovaPipeline>) | null = null\n  try {\n    // Lazy require so server.ts boot doesn't pay the WASM init cost when\n    // Ollama is available. The package is heavy — only load it on demand.\n    // The package is optional so its types may not be installed in every\n    // environment. Use a dynamic specifier so tsc keeps the import dynamic\n    // and doesn't try to resolve types at build time.\n    const specifier = '@xenova/transformers'\n    const mod = (await import(specifier)) as unknown as {\n      pipeline: (task: string, model: string) => Promise<XenovaPipeline>\n    }\n    pipelineFn = mod.pipeline\n  } catch {\n    return null\n  }\n  if (!pipelineFn) return null\n  const model = 'Xenova/all-MiniLM-L6-v2'\n  const extractor = await pipelineFn('feature-extraction', model)\n  return {\n    provider: 'transformers',\n    model,\n    dim: MINI_LM_DIM,\n    async embed(texts: string[]): Promise<Float32Array[]> {\n      const out: Float32Array[] = []\n      for (const text of texts) {\n        // Mean-pooled, L2-normalized → cosine reduces to dot product but\n        // we keep the explicit cosine() for clarity.\n        const result = await extractor(text, { pooling: 'mean', normalize: true })\n        out.push(Float32Array.from(result.data))\n      }\n      return out\n    },\n  }\n}\n\n// Picks the highest-tier embedder available. Returns null when only\n// substring is available (caller decides what to build).\nexport async function pickEmbedder(): Promise<Embedder | null> {\n  const host = ollamaHost()\n  if (host && (await ollamaReachable(host))) {\n    return makeOllamaEmbedder(host)\n  }\n  return makeTransformersEmbedder()\n}\n\n// ------------------------------------------------------------------ Cache\n\ninterface CacheEntry {\n  nodeId: string\n  attrsHash: string\n  vector: number[]\n}\n\ninterface CacheFile {\n  version: 1\n  provider: 'ollama' | 'transformers'\n  model: string\n  dim: number\n  entries: CacheEntry[]\n}\n\nasync function readCache(cachePath: string): Promise<CacheFile | null> {\n  try {\n    const raw = await fs.readFile(cachePath, 'utf8')\n    const parsed = JSON.parse(raw) as CacheFile\n    if (parsed.version !== 1) return null\n    return parsed\n  } catch {\n    return null\n  }\n}\n\nasync function writeCache(cachePath: string, cache: CacheFile): Promise<void> {\n  await fs.mkdir(path.dirname(cachePath), { recursive: true })\n  await fs.writeFile(cachePath, JSON.stringify(cache))\n}\n\n// ----------------------------------------------------------------- Indexes\n\nclass VectorIndex implements SearchIndex {\n  readonly provider: 'ollama' | 'transformers'\n  private vectors = new Map<string, { node: GraphNode; vector: Float32Array; hash: string }>()\n\n  constructor(\n    private embedder: Embedder,\n    private cachePath: string | null,\n  ) {\n    this.provider = embedder.provider\n  }\n\n  async search(query: string, limit = DEFAULT_LIMIT): Promise<SearchResponse> {\n    const trimmed = query.trim()\n    if (!trimmed || this.vectors.size === 0) {\n      return { query: trimmed, provider: this.provider, matches: [] }\n    }\n    const embedded = await this.embedder.embed([trimmed])\n    const qv = embedded[0]\n    if (!qv) {\n      return { query: trimmed, provider: this.provider, matches: [] }\n    }\n    const scored: ScoredNode[] = []\n    for (const { node, vector } of this.vectors.values()) {\n      const score = cosine(qv, vector)\n      scored.push({ node, score })\n    }\n    scored.sort((a, b) => b.score - a.score)\n    return { query: trimmed, provider: this.provider, matches: scored.slice(0, limit) }\n  }\n\n  async refresh(graph: NeatGraph): Promise<void> {\n    const present = new Set<string>()\n    const toEmbed: { id: string; node: GraphNode; hash: string; text: string }[] = []\n\n    graph.forEachNode((id, attrs) => {\n      const node = attrs as GraphNode\n      if (!shouldEmbed(node)) return\n      present.add(id)\n      const hash = attrsHash(node)\n      const cached = this.vectors.get(id)\n      if (cached && cached.hash === hash) {\n        cached.node = node\n        return\n      }\n      toEmbed.push({ id, node, hash, text: embedText(node) })\n    })\n\n    // Drop vanished nodes\n    for (const id of [...this.vectors.keys()]) {\n      if (!present.has(id)) this.vectors.delete(id)\n    }\n\n    if (toEmbed.length > 0) {\n      const vectors = await this.embedder.embed(toEmbed.map((e) => e.text))\n      toEmbed.forEach((entry, i) => {\n        const v = vectors[i]\n        if (!v) return\n        this.vectors.set(entry.id, { node: entry.node, vector: v, hash: entry.hash })\n      })\n    }\n\n    if (this.cachePath) {\n      const entries: CacheEntry[] = []\n      for (const [id, { vector, hash }] of this.vectors) {\n        entries.push({ nodeId: id, attrsHash: hash, vector: Array.from(vector) })\n      }\n      await writeCache(this.cachePath, {\n        version: 1,\n        provider: this.embedder.provider,\n        model: this.embedder.model,\n        dim: this.embedder.dim,\n        entries,\n      })\n    }\n  }\n\n  // Hydrate the in-memory map from a previously-written cache. Validates\n  // shape against the current embedder; mismatch → empty start.\n  loadFromCache(cache: CacheFile, graph: NeatGraph): void {\n    if (\n      cache.provider !== this.embedder.provider ||\n      cache.model !== this.embedder.model ||\n      cache.dim !== this.embedder.dim\n    ) {\n      return\n    }\n    const present = new Map<string, GraphNode>()\n    graph.forEachNode((id, attrs) => {\n      const node = attrs as GraphNode\n      if (shouldEmbed(node)) present.set(id, node)\n    })\n    for (const entry of cache.entries) {\n      const node = present.get(entry.nodeId)\n      if (!node) continue\n      // Skip cache entries whose attrs no longer match — they'll be\n      // re-embedded by the next refresh().\n      if (attrsHash(node) !== entry.attrsHash) continue\n      if (entry.vector.length !== this.embedder.dim) continue\n      this.vectors.set(entry.nodeId, {\n        node,\n        hash: entry.attrsHash,\n        vector: Float32Array.from(entry.vector),\n      })\n    }\n  }\n}\n\nclass SubstringIndex implements SearchIndex {\n  readonly provider = 'substring' as const\n  private graph: NeatGraph | null = null\n\n  async search(query: string, limit = DEFAULT_LIMIT): Promise<SearchResponse> {\n    const q = query.trim().toLowerCase()\n    const out: ScoredNode[] = []\n    if (!q || !this.graph) {\n      return { query: q, provider: 'substring', matches: [] }\n    }\n    this.graph.forEachNode((id, attrs) => {\n      const node = attrs as GraphNode\n      const name = (node as { name?: string }).name ?? ''\n      if (id.toLowerCase().includes(q) || name.toLowerCase().includes(q)) {\n        out.push({ node, score: 1 })\n      }\n    })\n    return { query: q, provider: 'substring', matches: out.slice(0, limit) }\n  }\n\n  async refresh(graph: NeatGraph): Promise<void> {\n    this.graph = graph\n  }\n}\n\n// ------------------------------------------------------------ Public factory\n\nexport interface BuildSearchIndexOptions {\n  // Where to read/write the embedding cache. Falls back to in-memory only\n  // if not provided. Pass `null` to explicitly disable caching.\n  cachePath?: string | null\n  // Override the embedder selection. Useful for tests (substring-only mode\n  // skips the Ollama probe + the Transformers.js download).\n  forceProvider?: 'ollama' | 'transformers' | 'substring'\n  // Pre-built embedder (test injection). Wins over forceProvider.\n  embedder?: Embedder\n}\n\nexport async function buildSearchIndex(\n  graph: NeatGraph,\n  options: BuildSearchIndexOptions = {},\n): Promise<SearchIndex> {\n  let embedder: Embedder | null = null\n  if (options.embedder) {\n    embedder = options.embedder\n  } else if (options.forceProvider !== 'substring') {\n    embedder = await pickEmbedder()\n    if (options.forceProvider === 'ollama' && embedder?.provider !== 'ollama') {\n      embedder = null\n    }\n    if (options.forceProvider === 'transformers' && embedder?.provider !== 'transformers') {\n      embedder = null\n    }\n  }\n\n  if (!embedder) {\n    const idx = new SubstringIndex()\n    await idx.refresh(graph)\n    return idx\n  }\n\n  const cachePath = options.cachePath === undefined ? null : options.cachePath\n  const idx = new VectorIndex(embedder, cachePath)\n  if (cachePath) {\n    const cache = await readCache(cachePath)\n    if (cache) idx.loadFromCache(cache, graph)\n  }\n  await idx.refresh(graph)\n  return idx\n}\n"],"mappings":"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","names":["isLoopbackHost","path","path","import_node_path","import_node_crypto","path","protobuf","reshapeGrpcRequest","Fastify","import_node_path","import_node_url","import_fastify","import_node_path","GraphDefault","import_types","import_node_fs","import_node_path","fs","path","fs","path","os","snippet","registryResolve","registryList","import_types","import_node_fs","import_node_os","import_node_path","path","os","semver","fs","path","walk","serviceId","import_node_fs","import_node_path","import_types","frontierId","fs","path","import_node_fs","import_node_path","import_types","status","grpc","path","fs","path","status","frontierId","serviceId","fs","path","import_node_fs","import_node_path","import_minimatch","import_types","import_node_fs","import_node_path","import_types","fs","path","parseYaml","exists","import_node_fs","import_node_path","path","exists","fs","parseToml","import_node_fs","import_node_path","path","exists","fs","import_node_fs","import_node_path","fs","path","fs","path","exists","path","fs","ignore","import_node_path","import_node_fs","import_yaml","import_types","serviceId","path","exists","fs","import_node_fs","import_node_path","import_types","path","walk","fs","toPosix","import_node_path","toPosix","path","import_node_path","import_node_fs","import_types","Parser","JavaScript","Python","fileExists","fs","path","toPosix","snippet","import_node_path","import_types","import_node_path","path","exists","import_node_fs","import_node_path","import_node_fs","import_node_path","fs","path","parse","fs","path","import_node_path","parse","path","parse","parse","import_node_path","parse","path","exists","parse","import_node_path","parse","path","exists","import_node_path","parse","path","exists","toPosix","path","import_node_fs","import_node_path","import_types","walk","fs","path","toPosix","import_node_path","import_tree_sitter","import_tree_sitter_javascript","import_types","PARSE_CHUNK","parseSource","makeJsParser","Parser","JavaScript","toPosix","path","import_node_fs","import_node_path","import_types","walk","fs","path","toPosix","import_types","import_node_path","import_tree_sitter","import_tree_sitter_javascript","import_tree_sitter_python","import_types","PARSE_CHUNK","parseSource","makeJsParser","Parser","JavaScript","makePyParser","Python","path","toPosix","import_node_path","import_tree_sitter","import_tree_sitter_javascript","import_types","PARSE_CHUNK","parseSource","makeJsParser","Parser","JavaScript","walk","path","toPosix","import_node_path","import_types","path","import_node_path","import_types","path","import_node_path","import_types","findAll","path","import_node_path","import_types","path","import_node_path","import_types","readImports","path","toPosix","import_node_path","import_types","import_types","path","exists","import_node_path","import_node_fs","import_types","path","exists","fs","toPosix","import_node_fs","import_node_path","import_types","fs","path","lineAt","toPosix","import_node_fs","import_node_path","import_yaml","walkYamlFiles","fs","path","import_node_path","import_node_fs","import_node_path","import_types","path","path","import_node_fs","fs","import_node_fs","import_node_path","import_types","fs","path","import_node_path","path","import_node_fs","import_node_os","import_node_path","import_types","path","os","fs","DAY_MS","violations","blocking","Fastify","cors","import_node_fs","import_node_path","import_node_crypto","fs","path","idx","path"]}