/** * Per-session page-QA signal: the pointer that lets `agents status` report a * verdict with the runner's own clock instead of leaving an operator to read * session age as if it were QA time. * * The problem this closes: a status box showing `session 58m` invites the * reading "page QA took 58 minutes". Session age measures the agent, not the * run. So the runner's wall time is recorded here and rendered beside the * verdict, and admission-queue wait is carried as a separate number because * `wall_time_ms.total` is runner stages only and never includes the queue * (see QaRunResult.wall_time_ms in the toolkit contracts). * * Layering: the qa-run matrix runner is toolkit tier (`src/lib/browser/**`) * and must not import `src/core`, so the runner cannot write this pointer * itself. The command layer owns the write and calls `recordQaSignal()` after * a run finishes. Core importing toolkit *types* is the permitted direction. * * Storage: `/.harnery/qa/.json`, one file per session * generation, atomic temp+rename so a concurrent status read never sees a * torn write. Last run wins; each run directory remains the authoritative * record of the run itself. * * Every function here is best-effort by contract. A missing, unreadable, * malformed, or partial pointer yields null rather than throwing: the status * box must render even when QA state is broken. */ import type { QaRunEvidenceSource, QaRunResult, QaRunVerdict } from "../../lib/browser/qa-run-contracts.js"; export declare const QA_SIGNAL_SCHEMA_VERSION: 1; /** Pointers older than this render as `stale ()` and nothing else: an * age-of-day-old verdict says nothing about the page as it stands now, and * showing its timings beside a current session invites the same conflation * this signal exists to prevent. */ export declare const QA_SIGNAL_STALE_AFTER_MS: number; /** Timings split the way the result contract splits them: `total` is runner * stages only, `queue` is admission wait before any browser work and is never * part of `total`. */ export interface QaSignalWallTime { total: number; queue?: number; } export interface QaSignalPointer { schema_version: typeof QA_SIGNAL_SCHEMA_VERSION; run_id: string; verdict: QaRunVerdict; evidence_source: QaRunEvidenceSource; /** ISO-8601 UTC instant the run completed. */ completed_at: string; /** Absolute run directory holding the authoritative result document. */ out_dir: string; wall_time_ms: QaSignalWallTime; target: string; } /** * Absolute path of one session's QA pointer. Throws on an instance id that * could escape the directory; callers in this module treat that as "no * pointer" rather than propagating it. */ export declare function qaSignalPath(coordRoot: string, instanceId: string): string; export interface QaSignalTarget { /** Defaults to the resolved coordination root. */ coordRoot?: string | null; /** Defaults to the current session's instance id. */ instanceId?: string | null; } /** * Write the pointer for one completed run. Returns what was written, or null * when the session/root could not be resolved or the write failed — a QA run * must never fail because its status breadcrumb could not be recorded. */ export declare function recordQaSignal(result: QaRunResult, target?: QaSignalTarget): QaSignalPointer | null; /** Read one session's pointer. Null when absent, unreadable, or malformed. */ export declare function readQaSignal(target?: QaSignalTarget): QaSignalPointer | null; /** * Validate an untrusted pointer document. Fail-closed: a partial document — * the shape a torn write or a schema change produces — is not a pointer, and * renders no row at all rather than a half-true one. */ export declare function parseQaSignal(document: unknown): QaSignalPointer | null; /** * Coarse single-unit age, matching the status box's existing age vocabulary * (`4m`, `3h`, `2d`). Deliberately coarser than the duration formatter: an * operator reads age to judge relevance, not to compare timings. */ export declare function formatQaSignalAge(ms: number): string; /** * Duration in the units an operator compares runs by. Seconds stay seconds up * to two minutes so a 90-second run reads `90s` rather than being rounded into * a minute bucket that hides the difference between fast runs. */ export declare function formatQaSignalDuration(ms: number): string; /** * Render the status-box `qa` value, or null when there is nothing honest to * say. Three shapes: * - fresh runner evidence: `passed 4m ago · 90s runner (2m queued)` * - fresh manual evidence: `manual 12m ago · not a pass` * - anything over the staleness horizon: `stale (2d)` * * Manual evidence never reports a verdict or a runner clock: nothing * re-executable ran, so the contract caps it below a pass and there is no * runner time to attribute. */ export declare function formatQaSignalRow(pointer: QaSignalPointer | null, now?: number): string | null; /** * The whole status-box contribution in one best-effort call: read this * session's pointer and render its row. Null means render no `qa` row. */ export declare function qaSignalStatusRow(target?: QaSignalTarget, now?: number): { value: string; pointer: QaSignalPointer; } | null; //# sourceMappingURL=qa-signal.d.ts.map