cligentic
← back to catalog
cligentic block
audit-lifecycle
Two-phase append-only audit lifecycle for dangerous or retryable operations: beginAudit() writes pending, then complete() or fail() appends the final record with the same audit id.
Install
NPM dependencies
zero deps — pure TS
Block dependencies
standalone
Size
81 LOC
Source
The full file, 81 lines.
This is the exact TypeScript that lands in your project when you run the install command. Read it, copy it, edit it, own it. cligentic never touches it again.
src/cli/foundation/audit-lifecycle.ts
// cligentic block: audit-lifecycle
//
// Two-phase append-only audit records for operations that must survive retries.
import { appendFileSync, mkdirSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { join } from "node:path";
export type AuditLifecycleResult = "pending" | "ok" | "error" | "blocked" | "dry-run";
export type AuditLifecycleRecord = {
kind: string;
command: string;
meta?: Record<string, unknown>;
tier?: string;
profile?: string;
};
export type AuditLifecycle = {
auditId: string;
complete(meta?: Record<string, unknown>): void;
fail(meta?: Record<string, unknown>): void;
block(meta?: Record<string, unknown>): void;
dryRun(meta?: Record<string, unknown>): void;
};
type StoredLifecycleRecord = AuditLifecycleRecord & {
ts: string;
result: AuditLifecycleResult;
meta: Record<string, unknown>;
};
function todayFilename(): string {
return `${new Date().toISOString().slice(0, 10)}.jsonl`;
}
export function beginAudit(
auditDir: string,
record: AuditLifecycleRecord,
opts: { auditId?: string } = {},
): AuditLifecycle {
const auditId = opts.auditId ?? randomUUID();
const started = Date.now();
const baseMeta = { ...(record.meta ?? {}), audit_id: auditId };
appendLifecycleRecord(auditDir, record, "pending", baseMeta);
const finish = (result: Exclude<AuditLifecycleResult, "pending">, meta = {}) => {
appendLifecycleRecord(auditDir, record, result, {
...baseMeta,
...meta,
duration_ms: Date.now() - started,
});
};
return {
auditId,
complete: (meta) => finish("ok", meta),
fail: (meta) => finish("error", meta),
block: (meta) => finish("blocked", meta),
dryRun: (meta) => finish("dry-run", meta),
};
}
export function appendLifecycleRecord(
auditDir: string,
record: AuditLifecycleRecord,
result: AuditLifecycleResult,
meta: Record<string, unknown> = {},
): void {
mkdirSync(auditDir, { recursive: true });
const file = join(auditDir, todayFilename());
const stored: StoredLifecycleRecord = {
ts: new Date().toISOString(),
...record,
result,
meta,
};
appendFileSync(file, `${JSON.stringify(stored)}\n`, { mode: 0o600 });
}