import type { ResolvedConfig } from "../types.js"; /** One of lookout's instruction files, as it stands in this project. */ export interface SkillState { name: string; description: string; version: number; /** Absolute path of this project's layer, when it has written one. */ amendmentPath: string | null; /** An amendment nothing could grade, waiting for a person to read it. */ proposalPath: string | null; } /** One line of `.lookout/skills/history.jsonl`, as the page reads it. */ export interface LearningEntry { at: string; skill: string; action: "applied" | "rolled-back" | "proposed" | "no-change"; summary: string; version?: number; evidence?: string[]; /** What the frozen set caught, on a rollback. */ violations?: { kind: string; shotId: string; category: string; why: string; }[]; } /** A failure lookout keeps hitting, with the occurrences folded together. */ export interface IncidentGroup { kind: string; message: string; /** Occurrences in the window, after the latest heal when one exists. */ count: number; latestAt: string; verb: string | null; detail: string | null; /** Healed before, and it came back: the loudest state there is. */ recurred: boolean; /** Two reverted heal attempts on record; self-heal skips it. */ needsPerson: boolean; } /** A heal that was written, failed a gate, and was reverted. */ export interface HealAttempt { at: string; /** Where the diff and the gate output were kept. */ dir: string; summary: string | null; cause: string | null; failedGates: string[]; } /** A heal that passed every gate and is now in the history of the checkout. */ export interface HealCommit { sha: string; at: string; subject: string; } export interface Learning { /** What is happening this second, from the locks the two verbs hold. */ running: { improve: boolean; heal: boolean; }; /** The instructions track: this project's judge, and how it has moved. */ instructions: { skills: SkillState[]; history: LearningEntry[]; /** The evidence that gates an amendment, or null when nothing is frozen. */ frozen: { cases: number; claims: number; frozenAt: string; } | null; /** NEW signals no improve pass has seen, which is what the trigger counts. */ pending: { total: number; bySkill: { skill: string; count: number; }[]; lastImproveAt: string | null; threshold: number; }; }; /** The source track: lookout's own code, and what it did wrong here. */ code: { /** lookout's checkout, or null when this is an installed package. */ checkout: string | null; incidents: IncidentGroup[]; attempts: HealAttempt[]; commits: HealCommit[]; }; } /** * A key that moves when anything this reads moves. * * The page polls, and assembling this touches a dozen files plus a git log, so * the answer is held until one of them changes. The lock files are part of the * key because a run starting or finishing is precisely the moment the page has * to repaint, and neither writes anything else while it is going. */ export declare function learningKey(resolved: ResolvedConfig): string; /** * Everything lookout has done to itself, for this project and this machine. * * Nothing here throws. The page that renders it is a viewer over whatever * happens to be on disk, and a half-written history file or an unreadable * incident log must degrade to a shorter answer rather than to a broken page. */ export declare function buildLearning(resolved: ResolvedConfig): Promise; /** * The one-line version, for the sidebar. * * The badge exists so the icon can say "something is here" without the page * fetching the whole record on every poll: whether either verb is running, how * many amendments stuck, how many proposals are waiting to be read, and how * many failures are still recurring. */ export declare function learningBadge(l: Learning): { running: boolean; applied: number; proposed: number; incidents: number; /** New signals waiting for the trigger; can go down, which earns its spot. */ pendingNew: number; /** Active groups loud enough to be worth a manual self-heal. */ hot: number; };