/** * `analyzeAbandonedMemory(beforePath, afterPath)` * * Diff two `.memgraph` snapshots on the reference-tree class counts (not the * cycle list) and classify the GROWTH shape per class. Surfaces the family * of bugs that the standard `diffMemgraphs` (cycle-focused) misses: * orphaned KVO observers, never-removed NotificationCenter handlers, caches * that never evict, singletons that retain payloads, and the long tail of * "unknown growth" that warrants further inspection. * * The tool is the natural pair for the v1.8 verify-fix loop: capture a * `before.memgraph`, ship the fix, capture an `after.memgraph`, then run * this to confirm the suspect class went from N to <= 1. Validated end * to end on the notelet investigation 2026-05-12 where AVPlayerItem went * 342 to 0 across a fix that was invisible in `leaks` output but obvious * in the reference tree. * * The classifier is pattern-catalog driven, same shape as `classifyCycle`: * each grown class is matched against a small set of heuristics and tagged * with a stable `classification` id + confidence tier. The agent can chain * the result into `swiftSearchPattern` with the class name to locate the * source. */ import { z } from "zod"; import { type ReferenceTreeEntry } from "../parsers/referenceTree.js"; import type { NextCallSuggestion } from "../types.js"; export declare const analyzeAbandonedMemoryShape: { readonly beforePath: z.ZodString; readonly afterPath: z.ZodString; readonly topN: z.ZodDefault; readonly classFilter: z.ZodOptional; readonly outputFormat: z.ZodOptional>; }; export declare const analyzeAbandonedMemorySchema: z.ZodObject<{ readonly beforePath: z.ZodString; readonly afterPath: z.ZodString; readonly topN: z.ZodDefault; readonly classFilter: z.ZodOptional; readonly outputFormat: z.ZodOptional>; }, "strip", z.ZodTypeAny, { topN: number; beforePath: string; afterPath: string; outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined; classFilter?: string | undefined; }, { beforePath: string; afterPath: string; outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined; topN?: number | undefined; classFilter?: string | undefined; }>; export type AnalyzeAbandonedMemoryInput = z.infer; export type AbandonedMemoryClassification = "kvo-observer-orphaned" | "notificationcenter-observer-leaked" | "cache-too-aggressive" | "singleton-retains-payload" | "unknown-growth"; export interface AbandonedMemoryEntry { className: string; beforeCount: number; afterCount: number; delta: number; beforeBytes: number; afterBytes: number; bytesDelta: number; classification: AbandonedMemoryClassification; confidence: "high" | "medium" | "low"; hint?: string; } export interface AnalyzeAbandonedMemoryResult { ok: boolean; beforePath: string; afterPath: string; totals: { classesGrown: number; classesShrunk: number; classesUnchanged: number; netInstancesDelta: number; netBytesDelta: number; }; /** * Classes that grew between before and after, ranked by absolute delta * descending. Each entry carries a `classification` from the catalog plus * a `confidence` tier. The agent can branch on `classification` to choose * the right `swiftSearchPattern` / fix template. * * **Raw view.** Includes framework noise (NSMutableDictionary, CFString, * libMainThreadChecker bss, etc.). Useful for cache-bloat investigations. */ growthByClass: AbandonedMemoryEntry[]; /** * Classes that shrunk between before and after. Surfaced so the caller * can confirm the fix freed the suspect class (e.g. AVPlayerItem in the * notelet case went from 342 to 0). Sorted by absolute delta desc. * * **Raw view.** See `actionableShrinkage` for the filtered "what fix * verifiably freed" view. */ shrinkageByClass: AbandonedMemoryEntry[]; /** * `growthByClass` with framework noise filtered out (Foundation collection * types, ObjC metadata, __DATA sections, allocator stacks, etc.). The * remaining entries are user-actionable classes. New in v1.10. * * Use this when answering "what new bug just appeared?". Use the raw * `growthByClass` when answering "what does the heap look like now?". */ actionableGrowth: AbandonedMemoryEntry[]; /** * `shrinkageByClass` with framework noise filtered out. Use this in the * verify-fix loop to confirm which app-level classes the fix actually * freed. AVPlayerItem dropping from 342 to 0 shows up here at the top. * New in v1.10. */ actionableShrinkage: AbandonedMemoryEntry[]; /** Plain-English diagnosis tying the highest-confidence growth to a fix hint. */ diagnosis: string; /** Pipeline hints: chain into `swiftSearchPattern` against the top growth class. */ suggestedNextCalls?: NextCallSuggestion[]; } /** * Pure: diff two reference-tree entry lists by class name, classify each * class with a delta != 0, and return the structured result minus the * filesystem header fields. * * Exposed so tests can drive it without subprocess spawning. The async * wrapper around it handles the leaks invocations. */ export declare function buildAbandonedMemoryDiff(before: ReferenceTreeEntry[], after: ReferenceTreeEntry[], options: { topN: number; classFilter?: string; }): Omit; /** * Pure: classify a single class's growth shape based on its name + the * presence of co-occurring NSKeyValueObservance growth. * * Heuristics (highest specificity first): * * - NSKeyValueObservance / NSKeyValueObservationInfo growth: high-confidence * `kvo-observer-orphaned`. The KVO subsystem only allocates these tokens * when `obj.observe(\.x) { ... }` is called; growth here means tokens * never invalidated. * * - When KVO observation infrastructure grew, escalate any other class * with delta >= 5 to `kvo-observer-orphaned` (medium confidence). These * are typically the observed types being retained by orphaned observers * (AVPlayerItem in the notelet case). * * - NSCache / NSCountedSet / NSMapTable / NSMutable{Array,Dictionary,Set} * growth: medium-confidence `cache-too-aggressive`. Collection classes * that grow across a workflow typically indicate missing eviction. * * - NotificationCenter observer block growth (NSConcreteNotification, * __NSObserver, and similar): medium-confidence * `notificationcenter-observer-leaked`. * * - Everything else: low-confidence `unknown-growth`. The agent should * chain into `swiftSearchPattern` with the class name to confirm. */ export declare function classifyGrowth(className: string, delta: number, hasKvoCoOccurrence: boolean, kvoObservanceDelta: number): { classification: AbandonedMemoryClassification; confidence: "high" | "medium" | "low"; hint?: string; }; export declare function analyzeAbandonedMemory(input: AnalyzeAbandonedMemoryInput): Promise;