/** * Recall-compliance sensor for `totem doctor --compliance` (ADR-029 minimal * slice, mmnto-ai/totem#2362). * * ADR-029 § 1 "Passive Log Analysis" specifies the telemetry pair by design: * `.totem/.search-log.jsonl` (produced by the MCP `search_knowledge` tool) + * git commit timestamps — write interception was explicitly rejected as too * intrusive. The Compliance Rate (§ 3) is the % of coding sessions in which a * `search_knowledge` call preceded the session's first commit. * * Commit-granularity caveat (§ 1, ruled in on #2362): a search landing between * a file write and its commit still counts as compliant. That is inherent to * the ADR's chosen design and acceptable for a warning-threshold sensor * (Tenet 13) — it is a caveat on the readout, not a rename of the metric. * * Sensor-not-gate (Tenet 13): this command is a pure readout. It never throws, * never sets a non-zero exit code, and is not part of the gating `--strict` * suite — it only ever reports. * * Session model (ADR-029 § 2, verbatim): a coding session is "contiguous * `search_knowledge` calls and git commits occurring within a rolling 2-hour * window" — ONE merged event stream. An intervening commit extends a session * exactly like a search does; search-only clusters with window-attached commits * are NOT equivalent and were reworked out (2026-07-15 panel fold, codex * architecture lens, verified against the ADR text). * * Why the rate is repo-wide only (same fold): commits carry no seat identity — * SHA + timestamp — so a per-seat Compliance Rate would have to guess which * seat's search "owns" a commit, fabricating attribution (Tenet 4). * `agent_source` renders as an attribution-coverage diagnostic (entry counts * per seat; `unattributed` = the ~420 pre-schema entries + hookless sessions), * explicitly not a per-seat rate. The per-seat rate activates when a * commit-side identity primitive exists (ADR-078 boundary / commit-stamped * session ids). `session_id` is stamped by the producer for that same forward * join and is deliberately unused in this windowing — there is nothing * commit-side to join it against yet. * * Known precision limit: a later `git rebase` rewrites commit timestamps, which * retroactively shifts the 2-hour windows a past run computed against — so a * historical Compliance Rate is only as stable as the commit timestamps it read. * This is inherent to the passive-log design (§ 1) and is not corrected here. */ /** A parsed `.search-log.jsonl` entry — only the fields the metric needs. */ export interface ComplianceLogEntry { timestamp: string; agent_source: string | null; session_id: string | null; } /** * The git-history SEAM: a commit's sha + ISO timestamp. The doctor section * supplies this from real git via `readCommitRecords`; tests supply literal * arrays (no git spawn, no temp dirs). */ export interface CommitRecord { sha: string; timestamp: string; } /** Rate numerator/denominator. */ export interface RateStat { /** Counted sessions (coding sessions — windows containing ≥1 commit). */ n: number; /** Of `n`, how many had a search precede the window's first commit. */ compliant: number; } export interface ComplianceReport { /** Repo-wide Compliance Rate over merged-stream § 2 windows. */ overall: RateStat; /** * Attribution coverage — entry counts per `agent_source` bucket, sorted by * name (`unattributed` = null/pre-schema). A diagnostic, NOT compliance: * commits carry no seat identity, so per-seat rates are non-identifiable * until a commit-side join primitive exists. */ coverage: Array<{ bucket: string; entries: number; }>; /** * Windows that searched but never committed. Not a coding session per * ADR-029 § 3, so excluded from the denominator — but surfaced so a * search-heavy/commit-light stretch is visible, not hidden. */ searchOnlySessions: number; /** Raw count of parsed entries with no `agent_source` (the unattributed backlog). */ unattributedEntries: number; } export interface ParseResult { entries: ComplianceLogEntry[]; /** Lines that were non-empty but failed JSON.parse or timestamp validation. */ malformedCount: number; } /** * Parse the raw `.search-log.jsonl` contents. A malformed/corrupt line (bad * JSON, or a missing/unparseable timestamp) is skipped and counted — the * command never crashes on a partial write or a hand-edit (Tenet 13 sensor * pattern: record + continue). */ export declare function parseSearchLog(content: string): ParseResult; /** * Compute the Compliance Rate report from parsed log entries + the commit seam. * * 1. Merge searches + commits into ONE repo-wide event timeline (§ 2 verbatim: * sessions are contiguous searches AND commits in a rolling 2-hour window — * an intervening commit extends a session exactly like a search does). * 2. Roll windows: a new session starts when the gap from the previous event * exceeds 2 hours. * 3. Score each window containing ≥1 commit: compliant iff its earliest search * precedes its earliest commit (a commit-only window is non-compliant by * construction). Search-only windows are excluded from the denominator (not * coding sessions per § 3) but surfaced. * 4. Coverage: entry counts per `agent_source` — a diagnostic, never a rate * (commits carry no seat identity; see the header). */ export declare function computeCompliance(entries: ComplianceLogEntry[], commits: CommitRecord[]): ComplianceReport; /** * Format a rate for display. Below `MIN_SESSIONS_FOR_RATE` counted sessions * (including n=0), a percentage is meaningless — render the honest * "insufficient data (n=x)" instead (ruled in on #2362: keep the metric name * "Compliance Rate", surface low-n honestly rather than an over-precise %). */ export declare function formatRate(stat: RateStat): string; export interface ComplianceCliOptions { /** Test seam — production callers omit and the command uses `process.cwd()`. */ cwdForTest?: string; /** Test seam — inject raw log contents instead of reading `.search-log.jsonl`. */ logContentForTest?: string; /** Test seam — inject commits instead of spawning git. */ commitsForTest?: CommitRecord[]; } /** * CLI entry — renders the Compliance Rate readout. Pure sensor: never throws * for a compliance verdict, never sets a non-zero exit code (Tenet 13). Absent * log file → the doctor `skip` idiom pointing at the MCP wiring, NOT a fail and * NOT 0%. */ export declare function doctorComplianceCliCommand(options?: ComplianceCliOptions): Promise; //# sourceMappingURL=doctor-compliance.d.ts.map