/** * SqliteDeadLetterStore — durable store for pain signals that failed to be * recorded by PainToPrincipleService. * * Task 3: When `emitPainDetectedEvent` in pain.ts catches an exception from * `service.recordPain()`, the pain data is written here so it is not silently * lost (rc-9: no silent fallback). `pd pain retry --pain-id ` can later * read the dead letter and replay the pain through PainSignalBridge. * * ERR checklist: * - EP-01 / ERR-001, ERR-005: DB rows treated as unknown; painData parsed as * unknown and never `as`-cast to a typed shape. * - EP-01 / ERR-013: Object.hasOwn not needed here — we read column names from * sqlite rows via String()/Number() guards, not `in` or bracket access on * untrusted keys. * - EP-03 / ERR-002: write failures return { ok: false, error } so callers * can log an observable reason (rc-9). Parse failures on read are wrapped * into a structured object so the caller can observe them. * - EP-05 / ERR-015: markRetried reads fresh row state via UPDATE ... WHERE * pain_id = ?, so retry_count is always incremented from the latest value. */ import type { SqliteConnection } from '../sqlite-connection.js'; /** A row in the dead_letter_pains table. painData is unknown (rc-1). */ export interface DeadLetterRow { id: string; painId: string; painData: unknown; failedAt: string; retryCount: number; retriedAt: string | null; } /** Result of an insert or markRetried operation. */ export type DeadLetterOpResult = { ok: true; } | { ok: false; error: string; }; export declare class SqliteDeadLetterStore { private readonly connection; constructor(connection: SqliteConnection); /** * Persist a pain signal that failed to be recorded. * painData is JSON.stringify'd before storage; non-serializable values are * wrapped in a fallback envelope so the insert never silently drops data. */ insertDeadLetter(input: { painId: string; painData: unknown; }): DeadLetterOpResult; /** List dead letters, most recent first. */ listDeadLetters(filter?: { limit?: number; }): DeadLetterRow[]; /** * Mark the most recent dead letter for a painId as retried. * - success=true: retry_count++, retried_at = now * - success=false: retry_count++ only (retried_at stays null so it remains retryable) * * Targets only the single latest row (ORDER BY failed_at DESC LIMIT 1) so * historical rows for the same painId keep their original audit state. The * caller (pd pain retry) only replays the latest dead letter via * getByPainId(), so updating older rows would inflate their retry_count * without any corresponding replay. */ markRetried(painId: string, success: boolean): DeadLetterOpResult; /** Get the most recent dead letter for a painId, or null if none. */ getByPainId(painId: string): DeadLetterRow | null; } //# sourceMappingURL=sqlite-dead-letter-store.d.ts.map