/** * The read side of the effect ledger: what this system changed, and on what evidence. * * Lives here rather than inside the gateway executor's switch so it can be tested against * a real ledger without standing up an agent. The first cut was written inline and its * tests drove everything EXCEPT the tool path, which is how a projection that could state * a falsehood passed seven green tests. * * The falsehood it could state: rows are capped, coverage is not. With 40 attributed and * 80 unattributed changes in a window, a default call returned 50 rows - all unattributed, * because the newest changes skew that way while `task_update` has no cause field - beside * a coverage count saying 40 were explainable, and nothing anywhere saying 120 matched. A * model reading its own output could truthfully report "nothing I did is explainable". * * So: the counts and the rows must describe the same population, the cap must be visible, * and a filter the caller misspells must fail loudly rather than return zero rows - "no * rows" and "nothing changed" are the same sentence to a reader, and only one of them is * ever true. */ import type { EffectQuery, EffectRecord, EffectTarget } from '../evidence/effects.js'; export interface ChangesLedger { listChanges(query: EffectQuery): EffectRecord[]; changeCoverage(sinceMs?: number, targetType?: EffectTarget): { attributed: number; unattributed: number; }; } export interface ChangesReadInput { since?: unknown; target_type?: unknown; cause_state?: unknown; limit?: unknown; } export interface ChangesReadFailure { success: false; code: 'invalid_argument'; error: string; } export interface ChangesReadResult { success: true; coverage: { attributed: number; unattributed: number; }; since: string; /** Changes matching the filter in the window - not the number returned. */ total: number; returned: number; changes: Array<{ kind: string; target_type: string; target_id: string; cause_state: string; /** What KIND of cause moved this - the rubric's cause-citation item reads it. */ cause_kind: string; source_event_ids: string[]; channel: string | null; run_id: string | null; at: string; }>; } /** * The window a read covers. * * Unparseable input falls back to the default and the resolved window is returned to the * caller, so whatever was asked for, what was actually read is stated. Non-string input is * not a window at all - a model emitting epoch milliseconds used to crash the tool on * `.trim()`. */ export declare function parseChangesSince(since: unknown, nowMs: number): number; export declare function readChanges(ledger: ChangesLedger, input: ChangesReadInput, nowMs: number): ChangesReadResult | ChangesReadFailure; //# sourceMappingURL=changes-projection.d.ts.map