/** * What kind of durable change happened. Closed on purpose - see the module * note. HONESTY (S2 review #5): only the task kinds have live writers today * (task-ledger). report_update/report_publish/memory_write/wiki_write are * declared surface with zero recorders - coverage claims are therefore * scoped to task effects; widening the ledger is named deferred work. */ export type EffectKind = 'task_create' | 'task_update' | 'report_update' | 'report_publish' | 'memory_write' | 'wiki_write'; /** What it happened to. */ export type EffectTarget = 'task' | 'report_slot' | 'memory' | 'wiki_page'; /** Whether the change could name what caused it. */ export type CauseState = 'attributed' | 'unattributed'; /** * WHY the change happened - the closed set (S2). `event` is the only kind that * carries source event ids; the others are honestly id-less: a clock advanced, * the owner asked in chat, or a card transition cascaded. Writers pass their * kind explicitly - the ledger never infers. */ export type CauseKind = 'event' | 'owner_message' | 'clock' | 'card_transition'; /** Kinds an unattributed (id-less) change may claim. */ export type UnattributedCauseKind = Exclude; /** Fields every durable change carries, whether or not it can name a cause. */ export interface ChangeInput { /** * The model run that produced the change, so it is traceable to a transcript. * Null when no model run did - a host-internal write is not made more honest by * inventing a run id for it. */ runId?: string | null; /** Evidence channel this change concerns, when it concerns one. */ channelId?: string | null; kind: EffectKind; targetType: EffectTarget; targetId: string; /** The written payload; hashed, never stored, so the ledger cannot leak content. */ payload: unknown; atMs: number; } export interface EffectInput extends ChangeInput { /** Events that caused it. Must not be empty. */ sourceEventIds: readonly string[]; } export interface EffectRecord { id: number; runId: string | null; channelId: string | null; causeState: CauseState; causeKind: CauseKind; sourceEventIds: string[]; kind: EffectKind; targetType: EffectTarget; targetId: string; payloadHash: string; atMs: number; } /** * Whether a value can serve as a cause at all. * * Callers ask before writing rather than discovering it as a constraint violation, * because the answer changes what they record, not whether they may proceed: a malformed * cause makes a change unattributed, it does not make the change illegitimate. Legacy * rows really do carry 400-character source identifiers, and refusing to let the operator * update a work item because its upstream id is the wrong shape would be enforcement * bought with the owner's work. */ export declare function isUsableCause(value: string | null | undefined): value is string; export declare const EVIDENCE_EFFECTS_DDL: string; interface EffectAdapter { prepare(sql: string): { run(...params: unknown[]): { lastInsertRowid?: number | bigint; }; all(...params: unknown[]): unknown[]; }; } export declare function ensureEffectLedger(adapter: EffectAdapter): void; export declare class EffectWithoutCauseError extends Error { constructor(kind: EffectKind, targetId: string); } /** Stable hash of what was written. The ledger proves a change happened, not what it said. */ export declare function payloadHash(payload: unknown): string; /** * Record a durable change. * * Throws rather than returning a failure when the cause is missing. A caller that wrote * something and then could not say why must not proceed as if the write were accounted * for - silently skipping the ledger row is how 1,169 runs closed as `done` while five * receipts existed. */ export declare function recordEffect(adapter: EffectAdapter, input: EffectInput): number; /** * Record a durable change that could not name what caused it. * * Deliberately a separate function with an uncomfortable name. Every call is an admission * that the system changed something it cannot explain, and the point is that the count of * these is visible next to the count of real effects rather than absent from both. */ export declare function recordUnattributedChange(adapter: EffectAdapter, input: ChangeInput, causeKind: UnattributedCauseKind): number; export interface EffectQuery { runId?: string; targetType?: EffectTarget; targetId?: string; causeState?: CauseState; sinceMs?: number; limit?: number; } /** Read effects back, newest first - the substrate a report projects from. */ export declare function listEffects(adapter: EffectAdapter, query?: EffectQuery): EffectRecord[]; export interface ChangeCoverage { /** Changes that named the events behind them. */ attributed: number; /** Changes the system made and could not explain. */ unattributed: number; } /** * How much of what the system changed rests on evidence. * * The single number this ledger exists to make answerable, and the one the previous shape * of the system could not produce at all. */ export declare function changeCoverage(adapter: EffectAdapter, sinceMs?: number, targetType?: EffectTarget): ChangeCoverage; export {}; //# sourceMappingURL=effects.d.ts.map