/** * The reduce half of `/learn`: turn per-session observations into counts. * * The split with `mine.ts` is the whole design. Deciding that "we're on bun * now" and "stop using npm" mean the same thing is semantics, and the model is * better at it than any normalizer — so the model does it, by emitting a shared * `label`. Deciding that the shared label occurred nine times across five * sessions is arithmetic, and arithmetic stays here, because a model asked to * count over a long context will be approximately right, and an approximately * right number is worse than none when the number is the thing the reader acts * on. * * Nothing in this file filters on content. The only gate is the repeat * threshold, which is a dial the user sets and the digest reports, not a * whitelist they cannot see. */ import type { LabelledCandidate } from "./mine.js"; /** Where a repeated directive already lives, if anywhere. */ export type DirectiveStatus = "new" | "restated" | "has-skill"; /** Fields every proposable item shares, so suppression can be applied uniformly. */ export interface Proposable { /** Stable identity across runs — what the state file remembers. */ key: string; /** Newest occurrence in the window, ISO. */ lastSeen: string; } export interface DirectiveCluster extends Proposable { /** The model's canonical name for what was meant. The clustering key. */ label: string; /** Representative verbatim quote, the longest seen in the cluster. */ text: string; /** Why it is durable, in the model's words. */ rationale?: string; /** Total times said. */ count: number; /** Distinct sessions it was said in — the stronger of the two counts. */ sessions: number; status: DirectiveStatus; /** The existing rule line matched, when status is `restated`. */ existingRule?: string; /** The skill that already covers this, when status is `has-skill`. */ existingSkill?: string; /** * Shown before and still not written down anywhere — neither as a rule nor as * a skill — so the reader saw this proposal and passed on it. */ previouslyDeclined: boolean; } export interface FixCandidate extends Proposable { label: string; /** The failing command. */ command: string; /** Short excerpt of the real error text. */ errorExcerpt: string; /** Commands run between the failure and the pass. */ interveningCommands: string[]; /** Files edited between the failure and the pass. */ editedFiles: string[]; count: number; sessions: number; } /** * A piece of work the user keeps asking for by name. * * This is the slash-command signal, and it was being thrown away: the miner * used to be told not to report task requests at all. But a request repeated * across sessions is exactly what a slash command is for, and the evidence for * it is stronger than for most rules — the same job, asked for again, in * someone's own words. */ export interface RequestCandidate extends Proposable { label: string; /** How the user phrased it, so the proposal can quote rather than invent. */ text: string; count: number; sessions: number; } /** One session's mining output, named and tagged with the identity the counts need. */ export interface MinedSession { sessionId: string; /** When the session was last written to — the clock suppression runs on. */ lastActivity: string; /** Named by the global clustering pass; grouping here is exact-label arithmetic. */ candidates: LabelledCandidate[]; } export declare function reduceDirectives(sessions: MinedSession[], minRepeats: number): DirectiveCluster[]; export declare function reduceFixes(sessions: MinedSession[], minRepeats: number): FixCandidate[]; export declare function reduceRequests(sessions: MinedSession[], minRepeats: number): RequestCandidate[]; //# sourceMappingURL=reduce.d.ts.map