/** * MCP handler: certify_public_surface (change: add-public-api-surface-contract). * * Two conclusion modes over a package/module's exported public surface: * - No base ref → return the PUBLIC SURFACE: the exported symbols and their signatures. * - A base ref → return the BREAKING-CHANGE VERDICT for the current diff: each changed * public symbol classified `breaking | non-breaking | potentially-breaking`, each * breaking one paired with the consumers it breaks (in-repo, plus indexed sibling repos * under `federation`) and split into `breaking-consumed` / `breaking-unconsumed-in-index`, * plus an overall summary. Breaking findings accepted in the checked-in baseline are * listed as accepted instead of as findings (change: add-public-surface-acceptance-baseline). * * Deterministic, no LLM, no type checker, no build. Conservative by construction: a * change that cannot be proven compatible from the available signatures is * `potentially-breaking`, never silently `non-breaking`. Renamed exports are reported * as renames (not remove+add) via the symbol-identity continuity map (change: * add-symbol-identity-continuity). External/unindexed consumers are disclosed as a * known-unknowable boundary rather than implied to be absent. */ import { type SurfaceChange, type ChangeClass, type SuggestedBump } from '../../analyzer/public-surface.js'; import { type GovernanceFinding } from './enforcement-policy.js'; export interface CertifyPublicSurfaceInput { directory: string; /** Diff the working tree's public surface against this ref. Omit to return the surface itself. */ baseRef?: string; /** Cap the surface listing (surface mode). */ maxResults?: number; /** * Certification is fatal on an unresolvable base by default: a verdict computed * against a base the caller did not ask for is not a certificate. Set this to accept * the disclosed main → master → HEAD~1 fallback instead (fix-cli-conclusion-honesty). */ allowBaseFallback?: boolean; /** Opt-in: count consumers in indexed sibling repos (`.openlore/federation.json`) too. */ federation?: boolean; /** Limit the federation census to these registry repo names (default: all). */ federationRepos?: string[]; } interface Consumer { id: string; name: string; file: string; /** * How the consumer binds the symbol: a resolved `call`; an `unresolved-call` by name from a file * that imports it (the index was built after the symbol went away); an `import` of the symbol by * name (directly or through a re-exporting module) by a file with no call the index could * attribute (a const, class, or type is never a call target); or a `module-import` — a default, * namespace, or whole-module import of the defining module, which MAY use the symbol. */ via: 'call' | 'unresolved-call' | 'import' | 'module-import'; } interface EdgeStoreLike { getCallers(nodeId: string): Array<{ callerId: string; calleeName?: string; }>; /** Unresolved (`external`) call sites to this exact name. */ getExternalConsumers?(symbolName: string): Array<{ callerId: string; }>; } /** Files that import `name` from `file` (by name, or as a whole module), from the dependency graph. */ export type ImporterLookup = (file: string, name: string) => ReadonlyArray<{ file: string; via: 'import' | 'module-import'; }>; /** * The consumer-weighted split of a breaking change (change: add-public-surface-acceptance-baseline): * `breaking-consumed` when at least one indexed consumer binds the symbol, else * `breaking-unconsumed-in-index`. The consumer list is the evidence; there is no score. Zero indexed * consumers is never "safe" — the external-consumer boundary is disclosed on both. */ export type BreakingWeight = 'breaking-consumed' | 'breaking-unconsumed-in-index'; interface CrossRepoConsumerOut { repo: string; name: string; file: string; } export type WeightedBreakingChange = SurfaceChange & { consumers: Consumer[]; consumersTruncated: number; /** Consumers in indexed sibling repos (federation scope only), matched by symbol name. */ crossRepoConsumers?: CrossRepoConsumerOut[]; /** Cross-repo consumers found but not listed (a cap). */ crossRepoConsumersTruncated?: number; /** In-repo plus cross-repo consumers, including any dropped by a cap. */ consumerCount: number; breakingClass: BreakingWeight; }; /** * The pure breaking-change core: classify the public-surface delta between two sets of * file contents. No git, no readCachedContext, no clock — git I/O and the * confidence-boundary live in `diffSurface`. Exposed so the classification can be * unit-tested in CI from in-memory contents with a stub edge store. Deterministic. */ export declare function assembleSurfaceDiff(baseFiles: Array<{ path: string; content: string; language: string; }>, headFiles: Array<{ path: string; content: string; language: string; }>, headPathOf: Map, edgeStore?: EdgeStoreLike, /** Changed code files in a language whose signatures are not classified (they never reach this core). */ unassessedCodeFiles?: number, /** Files importing a symbol, for the consumer census (see {@link resolveConsumers}). */ importersOf?: ImporterLookup): Promise<{ overall: ChangeClass; summary: { breaking: number; potentiallyBreaking: number; nonBreaking: number; breakingConsumed: number; breakingUnconsumedInIndex: number; }; changes: SurfaceChange[]; breaking: WeightedBreakingChange[]; suggestedBump: SuggestedBump | null; /** Why the bump is withheld, when `suggestedBump` is null. */ suggestedBumpWithheld?: string; findings: GovernanceFinding[]; soundness: { posture: string; languages: string; }; extraCrossings: Array<{ kind: 'unindexed-repo'; count: number; detail: string; }>; }>; /** * What exactly broke, so an acceptance of one break never covers a different break of the same * rule on the same symbol (change: add-public-surface-acceptance-baseline): the rename target for a * rename; the canonical before → after contract for a signature change; the removed contract (or, * for a const, class, or type, its base declaration line) for a removal or a visibility reduction — * a symbol that comes back and is removed again with a different contract is a new break. */ export declare function breakDiscriminator(change: SurfaceChange): string | undefined; /** * Governance findings for a surface diff, one per rule code per changed symbol, so an * `enforcement.policy` can gate an individual rule (for example block `export-removed` but not * `param-type-narrowed`). Breaking-classed codes are severity `error`; `signature-unprovable` is a * `warning` a caller can choose to gate, so removing a type annotation cannot hide a narrowing from * a policy. `export-added` is not a finding. Deterministic order (the changes are already sorted). */ export declare function publicSurfaceFindings(changes: readonly SurfaceChange[]): GovernanceFinding[]; export declare function computeCertifyPublicSurface(input: CertifyPublicSurfaceInput): Promise; export declare function handleCertifyPublicSurface(input: CertifyPublicSurfaceInput): Promise; export {}; //# sourceMappingURL=public-surface.d.ts.map