/** * Per-directory memory of what `/learn` has already shown you. * * The extractor computes a sliding-window snapshot — "in your last N sessions * you said this M times" — which is stateless by design and correct to * recompute from scratch every run. What it cannot know on its own is whether * you have already *seen* a given item and made a call on it. Without that, a * rule you accepted last week comes back forever (its occurrences are still * inside the window), and worse, it comes back flagged `restated` — accusing a * rule that is working of not working. An item you declined comes back too, * unchanged, every single run. * * So one bookmark per directory: what was surfaced, and when. An item is shown * again only once something new has happened — you said it again *after* it was * last put in front of you. That single rule fixes both cases, and it keeps the * `restated` label honest, because the only way to earn it is to repeat yourself * after the rule already existed. * * This is a bookmark, not an index. No cards, no retrieval, no embeddings — the * transcripts remain the source of truth and this file can be deleted at any * time with no loss beyond re-proposing things once. */ export interface SurfacedItem { /** When this item was last put in front of the user, ISO. */ surfacedAt: string; /** Newest occurrence the run knew about at that point, ISO. */ lastOccurrence: string; /** * Whether the item was covered by a context file the last time it was * surfaced. Comparing that against coverage now is how an adopted proposal is * told from a declined one, without asking. */ coveredWhenSurfaced: boolean; /** * Representative wording, kept so `/learn stats` can ask about coverage using * what was actually said. The key alone is a slug, and judging "is this * written down?" from a slug is a much weaker question than judging it from * the sentence the slug stands for. */ text?: string; } export interface LearnState { version: number; lastRun?: string; surfaced: Record; } /** * State file for a directory. * * Keyed off the *name* of the cwd's session directory, which encodes the cwd — * so the bookmark is per working directory, matching what the digest describes. * Callers pass the cwd-derived path rather than whatever directory the live * session happens to be reading: a session with no directory of its own would * otherwise land every project on one nameless state file, and projects sharing * a custom `sessionDir` would share one bookmark between them. */ export declare function getLearnStatePath(agentDir: string, sessionDir: string): string; /** Read state, treating any unreadable or unknown-version file as empty. */ export declare function readLearnState(path: string): LearnState; /** Persist state, pruning entries nothing has referenced in a long time. */ export declare function writeLearnState(path: string, state: LearnState, now?: Date): void; /** What the caller needs to decide whether an item is worth showing again. */ export interface SuppressionInput { key: string; /** Newest occurrence of this item in the current window, ISO. */ lastSeen: string; /** Whether a context file covers it right now. */ covered: boolean; } export interface SuppressionVerdict { /** Drop it: already surfaced, and nothing new has happened since. */ suppressed: boolean; /** * Surfaced before, still not covered by any context file — you saw it and * chose not to write it down. Worth telling the model so it proposes again * tentatively instead of pressing the same case twice. */ previouslyDeclined: boolean; } /** * Decide whether an item should be shown again. * * An item is suppressed when it was surfaced before and has not recurred since: * `lastSeen <= surfacedAt` means every occurrence backing it was already on * screen when you made your call. Say it again and `lastSeen` moves past * `surfacedAt`, and it returns — which is exactly when it is worth returning. */ export declare function judge(state: LearnState, input: SuppressionInput): SuppressionVerdict; export interface LearnStats { /** Proposals on record, across all categories. */ total: number; directives: number; fixes: number; requests: number; earliest?: string; latest?: string; lastRun?: string; } /** * Count what has been proposed, and when. * * Deliberately no adoption rate. There used to be one, derived by re-judging * coverage and calling the delta "adopted", and it was wrong in both * directions: a failed judge at either end moved the number, and a proposal * correctly rejected as junk was indistinguishable from one ignored. It shipped * with two disclaimers explaining how not to misread it, which is the clearest * possible sign that it should not have shipped. What the reader actually wants * — is the always-loaded surface growing or shrinking — is measurable exactly, * from the context files themselves, and is reported instead. */ export declare function summarizeLearnState(state: LearnState): LearnStats; /** Record everything this run put on screen, so the next run can suppress it. */ export declare function recordSurfaced(state: LearnState, items: Array<{ key: string; lastSeen: string; covered: boolean; text?: string; }>, now?: Date): LearnState; //# sourceMappingURL=state.d.ts.map