/** * Append-only gate audit trail for evidence-based verification (T832 / ADR-051). * * Every `cleo verify` write appends a single JSON line to * `.cleo/audit/gates.jsonl`. Emergency overrides (`CLEO_OWNER_OVERRIDE=1`) * additionally append to `.cleo/audit/force-bypass.jsonl`. * * Writers MUST use {@link appendGateAuditLine} or * {@link appendForceBypassLine} — direct file writes are forbidden. * * Signed variants ({@link appendSignedGateAuditLine}, * {@link verifyAuditHistory}) were added in T947 / ADR-054 (draft) and attach * an Ed25519 `_sig` field produced by `llmtxt/identity`. Unsigned entries * remain valid for backwards compatibility. * * @task T832 * @task T947 * @adr ADR-051 §6 * @adr ADR-054 (draft) */ import type { GateEvidence } from '@cleocode/contracts'; import { type AgentIdentity, type AuditSignature } from '../identity/cleo-identity.js'; /** * One record appended to `.cleo/audit/gates.jsonl`. * * The record schema is append-only — new fields MAY be added, existing * field semantics MUST NOT change. * * @task T832 */ export interface GateAuditRecord { /** ISO 8601 timestamp of the write. */ timestamp: string; /** Task ID being verified. */ taskId: string; /** Gate name, or "*all*" for a multi-gate set. */ gate: string; /** Write action. */ action: 'set' | 'reset' | 'all'; /** Evidence backing this write (undefined for reset). */ evidence?: GateEvidence; /** Agent identifier. */ agent: string; /** Session identifier. */ sessionId: string | null; /** True when the gate is now passed. */ passed: boolean; /** True when CLEO_OWNER_OVERRIDE was set. */ override: boolean; } /** * Extended record appended to `.cleo/audit/force-bypass.jsonl` when an * override was used. * * @task T832 * @task T1501 * @task T1502 */ export interface ForceBypassRecord extends GateAuditRecord { /** Reason supplied via CLEO_OWNER_OVERRIDE_REASON env. */ overrideReason: string; /** Process ID that performed the bypass. */ pid: number; /** CLI command line invocation (best-effort). */ command: string; /** * 1-based ordinal of this override within the current session (T1501 / P0-5). * Enables post-hoc audit of escalation patterns within a single session. */ sessionOverrideOrdinal?: number; /** * True when the same evidence atom was applied to >3 distinct tasks and * `--shared-evidence` was passed to acknowledge the reuse (T1502 / P0-6). */ sharedEvidence?: boolean; /** * True when the same evidence atom was applied to >3 distinct tasks but * `--shared-evidence` was NOT passed — a warning was emitted instead of * a hard reject (non-strict mode, T1502 / P0-6). */ sharedAtomWarning?: boolean; /** * True when the override originated from a worktree-orchestrate workflow and * was exempt from the per-session cap counter (T1504 / ADR-059 §D3). * * The entry is still logged in force-bypass.jsonl for full audit coverage. */ workTreeContext?: boolean; } /** * Resolve the project-relative path of the gates audit log. * * @param projectRoot - Absolute path to the project root * @returns Absolute path to `.cleo/audit/gates.jsonl` * * @task T832 */ export declare function getGateAuditPath(projectRoot: string): string; /** * Resolve the project-relative path of the force-bypass audit log. * * @param projectRoot - Absolute path to the project root * @returns Absolute path to `.cleo/audit/force-bypass.jsonl` * * @task T832 */ export declare function getForceBypassPath(projectRoot: string): string; /** * Append a single line to `.cleo/audit/gates.jsonl`. * * Creates the audit directory on first use. Serialises as a single line * with no trailing whitespace other than the final newline so the file * remains valid JSON-lines (`.jsonl`). * * @param projectRoot - Absolute path to the project root * @param record - Audit record to append * * @example * ```ts * await appendGateAuditLine('/project', { * timestamp: new Date().toISOString(), * taskId: 'T832', * gate: 'implemented', * action: 'set', * evidence, * agent: 'opus-lead', * sessionId: 'ses_xxx', * passed: true, * override: false, * }); * ``` * * @task T832 * @adr ADR-051 §6.1 */ export declare function appendGateAuditLine(projectRoot: string, record: GateAuditRecord): Promise; /** * Append a single line to `.cleo/audit/force-bypass.jsonl`. * * @param projectRoot - Absolute path to the project root * @param record - Override record to append * * @task T832 * @adr ADR-051 §6.2 */ export declare function appendForceBypassLine(projectRoot: string, record: ForceBypassRecord): Promise; /** * A {@link GateAuditRecord} that carries an optional Ed25519 signature. * * When present, `_sig` is computed over the UTF-8 bytes of the canonical * JSON line **without** the `_sig` field. Unsigned entries (pre-T947 or * opt-out writers) omit the field entirely. * * @task T947 */ export interface SignedGateAuditRecord extends GateAuditRecord { /** Optional signature envelope attached to signed entries. */ _sig?: AuditSignature; } /** * Append a cryptographically-signed line to `.cleo/audit/gates.jsonl`. * * The on-disk line contains the same fields as {@link appendGateAuditLine} * plus a `_sig: { sig, pub }` envelope. The signature covers the canonical * (alphabetically-sorted) JSON bytes of the record **before** `_sig` is * attached — call {@link verifyAuditHistory} to validate a file after * writing. * * Backwards compatibility: consumers that encounter entries without `_sig` * MUST treat them as valid-but-unsigned (pre-T947 migration). * * @param projectRoot - Absolute path to the project root. * @param record - Audit record to sign and append. * @param identity - Optional pre-loaded identity (avoids repeated disk I/O). * @returns The signature envelope that was attached. * * @task T947 * @adr ADR-054 (draft) */ export declare function appendSignedGateAuditLine(projectRoot: string, record: GateAuditRecord, identity?: AgentIdentity): Promise; /** * Counts returned by {@link verifyAuditHistory}. * * @task T947 */ export interface AuditHistoryReport { /** Total parsed lines (invalid JSON lines are skipped silently). */ total: number; /** Entries that carried a `_sig` field (valid or not). */ signed: number; /** Signed entries whose signature validated against the embedded `pub`. */ verified: number; /** Entries with no `_sig` field (legacy / backwards-compat). */ unsigned: number; } /** * Read `.cleo/audit/gates.jsonl` and report signature integrity. * * For every line: * - Lines without `_sig` increment `unsigned`. * - Lines with `_sig` increment `signed`; if the signature validates against * the embedded `pub`, they additionally increment `verified`. * - Malformed JSON is silently skipped (neither signed nor unsigned). * * This helper is intentionally permissive: it is a **read-only report**, not * an enforcement gate. Policy enforcement (e.g. refuse to boot with unsigned * entries after a cutoff date) is the caller's responsibility. * * @param projectRoot - Absolute path to the project root. * @returns Aggregate counts across the entire file. * * @task T947 * @adr ADR-054 (draft) */ export declare function verifyAuditHistory(projectRoot: string): Promise; //# sourceMappingURL=gate-audit.d.ts.map