/** * The telemetry contract: the complete, machine-enforced list of what graft may * send. `TELEMETRY.md` is the same list in prose, and the two are kept in * lockstep — an event or property that is not in this file cannot be sent, and * one that is not in `TELEMETRY.md` must not be added here. * * Why an allowlist rather than a review rule: every telemetry incident in the * tools we studied came from a call site quietly adding a property nobody * audited. `track()` (track.ts) drops unknown events and unknown keys instead of * trusting its callers, so a future `track('query', { path })` sends `query` * with no `path` rather than leaking one. The blast radius of a careless call * site is "we lose a metric", never "we received someone's source tree". * * The other half of that guarantee is the value side: every number crosses as a * BUCKET and every string as a member of a fixed union. A raw file count plus a * language set starts to fingerprint a specific repo; `"100-500"` does not. A * raw error message is a path, a symbol, and sometimes a snippet of code; an * enum member is not. * * This module is pure — no I/O, no config, no clock. It is the file a reviewer * (or a suspicious user, since the repo is public) reads to verify the claim. */ /** * The contract itself. Common properties (see {@link COMMON_KEYS}) are stamped * centrally by `track()` and are deliberately not repeated per event. */ export declare const EVENTS: Record>; /** Stamped on every event by `track()`; not listed per event above. */ export declare const COMMON_KEYS: readonly ["app_version", "os", "arch", "node_major", "ci", "agent_host", "repo_id"]; /** Which surface issued a query. `hook` is Claude Code's prompt hook, which * queries on the user's behalf without them typing a command. */ export type Surface = 'cli' | 'mcp' | 'hook'; /** Which editor/agent graft is running under. Derived from the surface and the * wiring on disk — never from a hostname, a username, or an env var's value. */ export type AgentHost = 'claude-code' | 'cursor' | 'mcp' | 'cli'; /** * The commands worth counting. A command absent here is simply not reported — * `track()` drops the event rather than sending a free-form string, which is * what keeps a future `graft ` from becoming a data leak. */ export declare const TRACKED_COMMANDS: readonly ["ask", "grep", "callers", "skeleton", "map", "check", "blast", "viz"]; export type TrackedCommand = (typeof TRACKED_COMMANDS)[number]; export declare function isTrackedCommand(name: string): name is TrackedCommand; /** Where a failing build died. Coarse on purpose: enough to route a bug, not * enough to describe anyone's repo. */ export declare const BUILD_STAGES: readonly ["walk", "extract", "graph", "summarize", "synthesize", "write"]; export type BuildStage = (typeof BUILD_STAGES)[number]; /** * The failure taxonomy. Anything unrecognised becomes `E_UNKNOWN` — the point of * a closed set is that an unclassified error contributes a count and nothing * else, so no message text can ride along inside a "code". */ export declare const ERROR_CODES: readonly ["E_UNKNOWN", "E_PARSE", "E_TIMEOUT", "E_LLM", "E_AUTH", "E_RATE_LIMIT", "E_NETWORK", "E_DISK", "E_PERMISSION", "E_OOM", "E_LOCK"]; export type ErrorCode = (typeof ERROR_CODES)[number]; /** * Map a thrown value to a code without letting its text through. Only fixed * substrings are matched, and only a member of {@link ERROR_CODES} is returned — * the error's own message is never the return value, not even in part. */ export declare function errorCode(err: unknown): ErrorCode; /** Repo scale. Exact counts fingerprint a repo; these five labels answer the * only question we have ("toy, service, or monorepo?"). */ export declare function filesBucket(n: number): string; /** Wall-clock for a build. Coarse enough that it says "fast/slow", not "this * exact machine on this exact tree". */ export declare function durationBucket(ms: number): string; /** Generic small-count bucket, for the per-session read counters. */ export declare function countBucket(n: number): string; /** Estimated tokens saved in one session — the README's central claim, in the * only resolution we need to defend it. */ export declare function savedTokensBucket(n: number): string; /** * Languages, normalised, filtered to a safe shape, and capped. * * Sorted so `["go","ts"]` and `["ts","go"]` aggregate as one value, deduped, and * cut to eight — a repo with a very long tail of languages is itself a * distinguishing fact, and the tail answers no question we have. * * The character filter is the part that matters. This is the only property in * the contract that is not drawn from a closed set defined in this file: the * values come from the parser registry, several modules away, via * `languageLabelOf() ?? container.name ?? generic.name ?? "unknown"`. That is * safe today, but it means the no-free-text guarantee would rest on a promise * made elsewhere — and a future generic extractor naming itself after the * extension it matched would quietly turn this into a channel for file * metadata. Anything outside `[a-z0-9+#._-]`, or longer than 24 characters, is * therefore dropped here rather than trusted. */ export declare function langsValue(langs: readonly string[]): string; //# sourceMappingURL=contract.d.ts.map