/** * What this project has already taught KONECK, and whether it is still true. * * There is a memory file already — `.koneck/MEMORY.md` — and it has two properties that between * them explain why sessions keep relearning the same things. It is written only when the model * decides to call the remember tool, which is rarely, because the model is busy doing the task. And * it is loaded whole into every prompt, so it cannot grow without eating the context window. Memory * that must stay small is memory that must forget. * * This is the other half. Three differences, and each is the point: * * **Findings are derived, not volunteered.** The knowledge worth keeping is the knowledge that cost * a failure to obtain: the command that failed until it was run a different way, the file that had * to be edited four times, the test that went red and then green. Those are already in the * trajectory. Reading them out is mechanical, so it happens every time rather than when the model * happens to be reflective. * * **Every finding is anchored to a commit and expires.** A note about code that has since changed is * not a memory, it is misinformation with a timestamp. Each entry records the sha it was learned at * and the paths it concerns; once those paths have moved on it is reported as stale, or withheld. * This is the part that makes the whole idea safe rather than dangerous — a shared store with no * expiry is a machine for propagating one session's wrong belief into every session that follows. * That is not hypothetical: a test run here once wrote a fiction into the live model-status file and * the running install believed it for days. * * **Retrieval is keyed, not wholesale.** Nothing goes into the prompt by default. When a turn is * about to write a particular file or run a particular command, the findings filed against that * path or that command prefix are surfaced — two lines, at the moment they are useful. That is what * lets the ledger grow to thousands of entries at no cost in context, and why there is no embedding * index here: an exact key is cheaper, inspectable, and cannot be confidently wrong. */ /** Where a project's findings live. Beside MEMORY.md, which is the same idea done by hand. */ export declare const LEDGER_FILE = ".koneck/ledger.jsonl"; export type FindingKind = /** How to run something here: what failed, and what worked instead. */ 'command' /** Something about a particular file that cost effort to discover. */ | 'file' /** How long something takes, so nobody plans around a guess. */ | 'timing' /** A rule this workspace enforces — a hook, a policy, a convention. */ | 'rule'; export interface Finding { kind: FindingKind; /** One sentence, in the words a reader needs. Never a paragraph. */ text: string; /** Paths this concerns. Staleness is judged against these. */ paths?: readonly string[]; /** Commands this concerns. Timing notes use the complete command; other notes use a prefix. */ commands?: readonly string[]; /** The commit it was learned at, when the workspace is a repository. */ sha?: string; /** ISO. */ at: string; /** Which session learned it, so a reader can go and look. */ session?: string; } /** A finding plus what is now known about whether it still holds. */ export interface RecalledFinding extends Finding { /** True when a path this finding depends on has changed since it was learned. */ stale: boolean; } /** * Appends findings, skipping anything already recorded in the same words. * * Duplicates are the failure mode of automatic capture: the same command fails the same way in * twenty sessions, and twenty identical lines make the useful ones unreadable. Matched on text * rather than on time, so a finding relearned after the code changed still replaces nothing — it * arrives with a newer sha, which is what makes it worth having again. */ export declare function record(cwd: string, findings: readonly Finding[]): Promise; /** Findings that were not already saved, including duplicates within this batch. */ export declare function freshFindings(findings: readonly Finding[], existing: readonly Finding[]): Finding[]; /** Every finding recorded for this workspace, oldest first. Absent or damaged lines are skipped. */ export declare function readAll(cwd: string): Promise; /** * The findings that bear on what is about to happen. * * Keyed on the path being touched and the first two words of the command being run — `npm test` * matches a finding filed against `npm test`, and `npm` alone does not match everything. */ export declare function relevant(findings: readonly Finding[], about: { paths?: readonly string[]; commands?: readonly string[]; }): Finding[]; /** * The first two words of a command, which is what identifies it. * * `npm test` and `npm run build` are different things to know about; `npm test -- --watch` is the * same thing as `npm test`. One word would file every npm command together, and the whole line * would file every variation separately. */ export declare function commandKey(command: string): string; /** * Marks each finding stale or not, given which paths have changed since it was learned. * * `changedSince` is asked once per distinct sha rather than once per finding, because it costs a git * process each time. A finding with no sha cannot be checked and is reported as current — it is * usually a timing or a rule, which code changes do not invalidate the way they invalidate a claim * about a particular file. */ export declare function withStaleness(findings: readonly Finding[], changedSince: (sha: string) => Promise): Promise; /** * What to put in front of the model, or nothing. * * Stale findings are included but marked, rather than hidden. Hiding them loses the one thing they * still tell you — that this used to be true and the code has moved — which is often exactly what a * reader needs in order to not be surprised. */ export declare function describeFindings(findings: readonly RecalledFinding[], limit?: number): string | null; /** * Findings read out of a session's own record of what it did. * * This is the part that makes the ledger fill up. Asking the model to notice what it learned works * occasionally; deriving it from the trajectory works every time, because the trajectory is written * whether anyone is being thoughtful or not. * * Three patterns, chosen because each is unambiguous and each answers a question the next session * would otherwise pay to answer again: * * - a command that failed and then succeeded in a different form. This is "how you run things * here", and it is the single most expensive thing to rediscover: the failure is silent * knowledge that lives in one session's scrollback. * - how long a slow command actually takes. A session that knows the suite takes ninety-five * seconds waits for it; one that does not, kills it at thirty and concludes it hangs. * - a file that needed several passes. Not a judgement about the file — just that it did, which * is a reason to read it properly before starting rather than editing it four times again. * * Deliberately not derived: anything requiring an opinion about *why*. A guess about cause, written * down as a finding and read by twenty later sessions, is worse than nothing. */ export declare function deriveFindings(events: readonly { t: string; at: number; name?: string; ms?: number; ok?: boolean; detail?: string; }[], about?: { sha?: string; session?: string; now?: () => string; }): Finding[]; /** * Which paths have changed since a commit, or null when the question cannot be answered. * * Null rather than an empty list, and the distinction matters: "nothing changed" and "this is not a * repository, or that commit is gone" lead to opposite conclusions about whether a finding can be * trusted. Conflating them would mark every finding current in a directory with no git at all. */ export declare function changedSince(cwd: string, sha: string): Promise; /** The commit a finding should be anchored to, or undefined outside a repository. */ export declare function currentSha(cwd: string): Promise; //# sourceMappingURL=ledger.d.ts.map