/** * Oversight-quality metrics for EU AI Act Art. 14 "effective oversight" / automation-bias evidence. * * These metrics are INDICATORS, not proof of reviewer cognition. See the Honest Limits section * in docs/record-contracts.md. Pace statistics can be gamed; metrics complement (not replace) * identity signing and authorizing provenance. * * All computations are deterministic with injected `now`. */ import type { Claim, Evidence, TrustBundle, VerificationEvent } from "@kontourai/surface"; import type { ReviewDecision } from "./review-resource.js"; export interface ReviewerOversightMetrics { /** Actor id this row belongs to. */ readonly actorId: string; /** Total decisions made by this reviewer in the window. */ readonly decisionCount: number; /** * Average decisions per hour, computed as decisionCount / elapsed hours between * the first and last reviewedAt timestamp (or 0 when decisionCount < 2). */ readonly decisionsPerHour: number; /** * Fraction of decisions where the reviewer chose a candidate that differs from * the item's pre-selected (proposed) candidate — i.e. the reviewer changed the * outcome suggested by the system. Ranges 0–1. */ readonly overrideRate: number; /** * Fraction of decisions where the authorizing block carries action === "typed", * indicating the reviewer also wrote a rationale note. Ranges 0–1. */ readonly typedRationaleRate: number; /** * Median inter-decision gap in seconds. Undefined when fewer than 2 decisions. */ readonly medianInterDecisionSeconds: number | undefined; /** * Fraction of presented items that were decided, when the caller supplies * presentedCount. Omitted (undefined) otherwise. */ readonly samplingCoverage: number | undefined; } export interface AggregateOversightMetrics { /** Total decisions across all reviewers. */ readonly decisionCount: number; /** * Average decisions per hour across the full window (first decision to last * decision, all reviewers combined). */ readonly decisionsPerHour: number; /** Override rate across all decisions. */ readonly overrideRate: number; /** Typed-rationale rate across all decisions. */ readonly typedRationaleRate: number; /** Median inter-decision gap in seconds across all decisions. */ readonly medianInterDecisionSeconds: number | undefined; /** * Fraction of presented items decided, when presentedCount was supplied. * Omitted otherwise. */ readonly samplingCoverage: number | undefined; } export interface OversightMetrics { /** One row per unique actorId found in the decisions. */ readonly byReviewer: readonly ReviewerOversightMetrics[]; /** Aggregate across all reviewers. */ readonly aggregate: AggregateOversightMetrics; /** ISO 8601 timestamp of the earliest decision in the window. */ readonly windowStart: string | undefined; /** ISO 8601 timestamp of the latest decision in the window. */ readonly windowEnd: string | undefined; /** Number of calendar days covered by the window (may be fractional). */ readonly windowDays: number | undefined; /** Number of input decisions used for these metrics. */ readonly inputDecisionCount: number; } export interface DeriveOversightMetricsOptions { /** Current time — injected for determinism. */ readonly now: Date; /** * Optional rolling window in days. Decisions older than `now - windowDays` * are excluded. When omitted all supplied decisions are included. */ readonly windowDays?: number; /** * Optional total number of items that were PRESENTED to reviewers (the * denominator for samplingCoverage). When omitted, samplingCoverage is * not computed. */ readonly presentedCount?: number; } /** * Derives per-reviewer and aggregate oversight-quality metrics from a stream * of ReviewDecision resources. * * @param decisions - Array of ReviewDecision Kubernetes-style resources as * produced by the review workbench or adapters. * @param options - `now` is required for determinism; `windowDays` and * `presentedCount` are optional. */ export declare function deriveOversightMetrics(decisions: readonly ReviewDecision[], options: DeriveOversightMetricsOptions): OversightMetrics; /** * A single projected oversight-quality claim from the metrics layer. * Uses claimType "oversight-quality" per the task specification. */ export interface OversightQualityClaim { readonly claim: Claim; readonly evidence: Evidence; readonly event: VerificationEvent; } export interface OversightMetricsClaimsSubject { /** Surface subject type (e.g. "review-session", "reviewer-actor"). */ readonly subjectType: string; /** Surface subject id (e.g. a session name or actor id). */ readonly subjectId: string; /** Surface facet (e.g. "review.oversight"). */ readonly facet: string; /** Actor id to record on events. */ readonly actor: string; /** ISO 8601 timestamp for claim created/updated times. */ readonly observedAt: string; /** Producer identifier for collectedBy field. */ readonly collectedBy: string; } /** * Projects oversight metrics as Surface-ready claims. * * Produces one claim per measurable metric in `aggregate` (per task spec: claimType * "oversight-quality", fieldOrBehavior per metric, numeric value). Evidence excerpts * summarise the computation inputs (count, window) so Annex-pack rules can apply value * predicates. * * Callers can pass these claims into buildSurveyTrustBundle by constructing a * minimal SurveyInput with manual-entry raw sources, or merge the returned Claim / * Evidence / VerificationEvent objects directly into an existing TrustBundle. * * @param metrics - Output of `deriveOversightMetrics`. * @param subject - Surface identity for the claims. */ export declare function oversightMetricsToClaims(metrics: OversightMetrics, subject: OversightMetricsClaimsSubject): OversightQualityClaim[]; /** * Merges oversight-quality claims into an existing TrustBundle. * Convenience wrapper for callers who already have a bundle and want to append * oversight metrics without rebuilding from SurveyInput. */ export declare function mergeTrustBundleWithOversightMetrics(bundle: TrustBundle, claims: readonly OversightQualityClaim[]): TrustBundle;