Skip to content

Confidence and provenance

Real applications rarely call an SDK only in the file that imports it. A client may be constructed once, exported, renamed, returned from a factory, passed into a function, or stored on an object. Emendant follows those statically visible paths.

Consider this shape:

client.ts
import OpenAI from "openai";
export const client = new OpenAI();
chat.ts
import { client as ai } from "./client";
await ai.beta.chat.completions.parse(input);

The affected call site never names openai. Emendant records a provenance chain from the package import, through the constructed and exported value, to the local import and final call.

Supported static paths include:

  • renamed local imports and re-exports;
  • exported SDK instances;
  • direct local factories with attributable return values;
  • direct function arguments; and
  • statically named object properties.

If two possible sources disagree, a value is overwritten, or the path depends on runtime behaviour, Emendant declines instead of guessing.

Normal scan output summarizes an indirect chain in one line. Add --verbose to print each hop:

Terminal window
npx emendant scan --verbose

You can also pass a finding ID to explain:

Terminal window
npx emendant explain 'candidate-key@src/chat.ts:4:7'

A change ID explains the feed entry. A finding ID names one call site and adds the chain that led to it.

A complete chain has every step traced and assumes nothing. Its length does not reduce confidence: a ten-hop static proof is still a proof.

An instanceof guard can attribute a value without showing where it was created. Such a chain is marked incomplete. It can explain a reported use, but it is not eligible for an automatic fix.

Confidence and provenance therefore answer different questions:

  • Confidence: how reliably does this curated matcher identify affected code?
  • Provenance: why does this value at this call site belong to the package?