/** * Verified-outcome learning loop, CLI half (#4581, ADR-502). * * The ruflo-mods `learningLoop` option records one trajectory per main-loop * turn in `.claude-flow/data/verified-trajectories.jsonl`. A mod may not run * processes or call MCP tools, so it can only observe: a turn is * `verified-success` or `verified-failure` when a recognised test, build or * typecheck command ran after the turn's last edit and its process exit * status said so; anything else is `unknown`. * * This module is the only place those records reach SONA: * - `verified-*` records train SONA (`processTrajectoryOutcome`); * - `unknown` records are counted and never train; * - each record is ingested once (an id ledger, never a rewrite of the * mod's file, which the mod appends to concurrently); * - task text is PII-redacted before it reaches SONA's keyword store. * * It then writes `.claude-flow/data/learned-lessons.json`: patterns that * verified outcomes produced, those at or above the SONA routing gate as * lessons, and repeated verified failures as cautions. The mod reads that * file and adds at most three cited lessons to a prompt. * * ADR-503 hardens it against gamed signals (verified-guards.ts, * verified-reverify.ts): a tainted success does not train unless the project * allows its taint class, an opt-in re-verify can downgrade a success before * it trains, and an anomaly gate holds suspicious patterns back from the * prompt. Every one of these can only downgrade. */ import { type LoopConfig, type PatternStats, type Taint } from './verified-guards.js'; import { type ReverifyRunner } from './verified-reverify.js'; import { type LessonsFile, type PatternExport } from './verified-lessons.js'; export { buildLessons, CAUTION_MIN_FAILURES, LESSON_GATE, lessonFile, lessonId, MAX_LESSONS, type Lesson, type LessonsFile } from './verified-lessons.js'; export declare const VERIFIED_PATH: string; export declare const LEDGER_PATH: string; export declare const LESSONS_PATH: string; /** Ingested ids remembered; the mod keeps at most 500 lines, so this always covers the whole file. */ export declare const MAX_LEDGER_IDS = 2000; /** A file past this many bytes is not read (the mod caps its own at far less). */ export declare const MAX_FILE_BYTES: number; export type Outcome = 'verified-success' | 'verified-failure' | 'unknown'; export type VerifierKind = 'test' | 'build' | 'typecheck' | 'lint'; export interface VerifiedRecord { readonly id: string; readonly task: string; readonly agent: string; readonly outcome: Outcome; readonly verifier: { readonly kind: VerifierKind; readonly command: string; readonly passed: boolean; } | null; readonly steps: ReadonlyArray<{ readonly tool: string; readonly ok: boolean; }>; readonly edits: number; /** ADR-503 §2.1: what the turn did to its own check; only a `verified-success` carries any. */ readonly taint: readonly Taint[]; /** The mod process that recorded it, when the mod wrote one (ADR-503 §2.4). */ readonly session: string | null; /** ADR-511: files a verified turn edited, screened (`lessonFile`); empty for `unknown`. */ readonly files: readonly string[]; readonly startedAt: number; readonly endedAt: number; } export interface IngestLedger { version: 1; ids: string[]; /** mtime of the jsonl at the last ingest, so a caller can skip an unchanged file cheaply. */ lastMtimeMs: number; totals: { verifiedSuccess: number; verifiedFailure: number; unknown: number; rejected: number; tainted: number; }; /** * Pattern keys verified outcomes trained, with the last verifier command that decided them (the lesson's evidence) * and what the anomaly gate needs (ADR-503 §2.4). */ patterns: Record; }>; /** ADR-503 §2.3: the rotation cursor and the latest re-verifications (which commit, which check, what happened). */ reverify: { cursor: number; log: ReverifyLogEntry[]; }; } export interface ReverifyLogEntry { readonly id: string; readonly check: string | null; readonly commit: string | null; readonly status: 'pass' | 'fail' | 'timeout' | 'error'; readonly at: number; } export declare const MAX_REVERIFY_LOG = 50; export interface IngestResult { /** `tainted`: successes whose taint the project did not allow, counted and never trained (ADR-503 §2.2). */ ingested: { verifiedSuccess: number; verifiedFailure: number; unknown: number; rejected: number; tainted: number; }; trained: number; skipped: 'no-file' | 'unchanged' | 'too-large' | null; lessons: number; cautions: number; belowGate: number; held: LessonsFile['held']; /** * ADR-503 §2.3: checks run, successes that passed or were downgraded to unknown, and successes left for an explicit * ingest because this caller may not run project code. */ reverify: { enabled: boolean; ran: number; passed: number; downgraded: number; deferred: number; }; totals: IngestLedger['totals']; sonaAvailable: boolean; } /** The SONA surface ingest needs; injectable so tests never touch `.swarm/`. */ export interface SonaLike { processTrajectoryOutcome(outcome: { trajectoryId: string; task: string; agent: string; success: boolean; steps?: Array<{ action: string; result: string; quality: number; timestamp: string; }>; duration?: number; feedback?: string; /** ADR-511: the edited files, which decide only which pattern the outcome reinforces. */ anchors?: string[]; }): { learned: boolean; patternKey: string; confidence: number; }; exportPatterns(): PatternExport; } /** One line of the mod's file, validated; anything malformed is null. Unknown fields never survive. */ export declare function parseRecord(line: string): VerifiedRecord | null; export declare function emptyLedger(): IngestLedger; /** A ledger file read back; a corrupt or hostile one starts over (re-ingest is bounded by the mod's 500-line cap). */ export declare function readLedger(raw: string | null): IngestLedger; export interface IngestOptions { /** Project root; defaults to process.cwd(), where SONA keeps `.swarm/`. */ root?: string; /** Ingest even when the file's mtime matches the last ingest. */ force?: boolean; /** Count only: train nothing and write nothing. */ dryRun?: boolean; /** SONA to train; defaults to the process's optimizer. Null means unavailable. */ sona?: SonaLike | null; now?: number; /** `learningLoop` settings; defaults to `.claude-flow/config.json` under `root` (ADR-503). */ config?: LoopConfig; /** * Whether this caller may run project code to re-verify (ADR-503 §2.3). Only an explicit ingest passes true; a hot * caller (hooks_route) leaves successes un-ingested when re-verify is on, instead of blocking for the timeout. */ allowReverify?: boolean; /** Re-verify runner; defaults to the detached-git-worktree runner. Tests inject one. */ reverifier?: ReverifyRunner; } export declare function ingestVerifiedOutcomes(options?: IngestOptions): Promise; /** * The doctor's view: whether the loop is recording and whether its records * reach SONA. Read-only. */ export declare function learningLoopHealth(root?: string): { status: 'pass' | 'warn'; message: string; fix?: string; }; //# sourceMappingURL=verified-outcomes.d.ts.map