/** * AC-coverage gate — the load-bearing IVTR-closure piece. * * Runs inside `cleo complete ` BEFORE the status flip and refuses to * mark a task done when any acceptance criterion has zero * `evidence_ac_bindings` rows pointing at it. Closes the rubber-stamp * loophole that let workers complete tasks whose ACs were never tied to * evidence. * * # Binding semantics * * An AC is "covered" when at least one of the following kinds of binding * row exists for its `ac_id`: * - `direct` — Worker's own evidence atom emitted by the owning task * - `satisfies` — Cross-task atom via ADR-079-r2 `satisfies:#` * - `coverage` — Validator-computed transitive coverage marker * * All three counts contribute equally. Per ADR-079-r4 §3. * * # Override paths * * The gate is hard: programmatic evidence is the canonical satisfaction * path. Two audited override paths exist for legitimate exceptions: * 1. `--waive-ac "" --waive-reason ""` — per-AC waiver * logged to `.cleo/audit/ac-waiver.jsonl`. Waivers are scoped to a * single `cleo complete` call; the unsatisfied AC list is * recomputed AFTER applying the waiver set so partially-waived * tasks fail when any AC is left unaddressed. * 2. `CLEO_OWNER_OVERRIDE=1` (+ `CLEO_OWNER_OVERRIDE_REASON=`) * — full bypass logged to `.cleo/audit/force-bypass.jsonl` per * existing convention (ADR-051 §6.2). * * # Tasks with zero ACs * * The gate is a no-op for tasks that declare zero acceptance criteria * — nothing to satisfy means nothing to enforce. Operators that want * "every task must have ACs" should turn on the existing * `enforcement.acceptance.mode='block'` config knob, which fires * earlier in the pipeline. * * # Transactional safety * * The coverage check is run as a pre-condition inside the caller's * transaction context, BEFORE any state mutation. The DB read uses the * same accessor handle as the subsequent writes so SQLite snapshot * isolation pins the AC + binding rows for the duration of the * completion call. * * @task T10509 * @saga T10377 (SG-IVTR-AC-BINDING) * @epic T10381 (E-AC-MIGRATION) * @adr ADR-079-r4 */ import type { AcBindingRow as _AcBindingRow, AcRow, DataAccessor } from '@cleocode/contracts'; /** * Re-export so consumers of this module can pull the binding row shape * without depending directly on `@cleocode/contracts`. Keeps the dispatch * boundary thin per the SG-ARCH-SOLID contracts-fan-out invariant. */ export type AcBindingRow = _AcBindingRow; /** * Per-AC envelope returned on the `unsatisfied` array when the gate * rejects completion. Powers the CLI message + JSON details payload. */ export interface UnsatisfiedAc { /** UUIDv4 of the acceptance-criterion row. */ acId: string; /** Display alias — `AC`. */ alias: string; /** Verbatim AC text from `task_acceptance_criteria.text`. */ text: string; } /** * Outcome of the AC-coverage check. * * `ok=true` when every AC has at least one binding (or when the task * declared zero ACs). `ok=false` carries the offenders so the caller * can shape the structured error. */ export type AcCoverageResult = { ok: true; } | { ok: false; unsatisfied: UnsatisfiedAc[]; }; /** * Result of resolving caller-supplied `--waive-ac` tokens against the * task's actual AC rows. Powers the audit-log payload and the post- * waiver coverage recompute. */ export interface ResolvedWaivers { /** AC UUIDs that the operator explicitly waived. */ acIds: string[]; /** Display aliases of the waived ACs (`AC`). */ aliases: string[]; /** Verbatim AC texts captured at audit time for forensic traceability. */ texts: string[]; /** Tokens that did NOT resolve to any AC on the task. */ unresolved: string[]; } /** * Compute coverage for a task. Returns `{ok:true}` when every AC has * at least one binding OR the task declared zero ACs; otherwise * `{ok:false}` with the offending AC rows. * * @param taskId - The task being completed. * @param accessor - DataAccessor used for the AC + binding reads. * @returns Coverage outcome. */ export declare function computeAcCoverage(taskId: string, accessor: DataAccessor): Promise; /** * Resolve the comma-separated `--waive-ac` token list against the * task's AC rows. Tokens may be: * - A canonical UUIDv4 (`task_acceptance_criteria.id`) * - A display alias (`AC`) * * Tokens that resolve to neither are surfaced on `unresolved` so the * caller can warn — they are NOT silently dropped, but they also do * not fail the gate by themselves. The caller decides. * * @param waiveCsv - Comma-separated token list (may be empty / undefined). * @param acRows - The task's AC rows (from `accessor.getAcRows`). * @returns Resolved waiver descriptor. */ export declare function resolveWaivers(waiveCsv: string | undefined, acRows: readonly AcRow[]): ResolvedWaivers; /** * Subtract a waiver set from an unsatisfied list. Returns the residual * unsatisfied ACs. When the residual is empty, the gate passes; when * non-empty the caller surfaces `E_AC_COVERAGE_INCOMPLETE` with the * residual. * * @param unsatisfied - From {@link computeAcCoverage}. * @param waivedAcIds - Set of canonical AC UUIDs the operator waived. * @returns Residual unsatisfied list (ACs not covered AND not waived). */ export declare function applyWaivers(unsatisfied: readonly UnsatisfiedAc[], waivedAcIds: ReadonlySet): UnsatisfiedAc[]; /** * One row in `.cleo/audit/ac-waiver.jsonl` — appended whenever a caller * supplies `--waive-ac` with a reason. The schema is intentionally flat * for grep-friendly post-mortem analysis. */ export interface AcWaiverAuditEntry { /** ISO-8601 timestamp the waiver was recorded. */ timestamp: string; /** Task that was completed under the waiver. */ taskId: string; /** AC UUIDs that were waived (subset of the task's AC set). */ waivedAcs: string[]; /** Display aliases (`AC`) — duplicates `waivedAcs` semantically but is grep-friendly. */ waivedAliases: string[]; /** Operator-supplied justification text. Mandatory. */ reason: string; /** Agent / user identity that performed the completion. */ actor: string; /** Tokens passed to `--waive-ac` that did NOT resolve to any AC. */ unresolvedTokens: string[]; } /** * Append a single waiver entry to `.cleo/audit/ac-waiver.jsonl`. Creates * the audit directory on first use. Errors propagate — failure to write * the audit row MUST block the completion since the audit trail is the * forensic guarantee that backs the override. * * @param entry - Pre-built audit entry. * @param projectRoot - Absolute path to the project root. */ export declare function appendAcWaiverAudit(entry: AcWaiverAuditEntry, projectRoot: string): Promise; /** * One row in `.cleo/audit/force-bypass.jsonl` written when an operator * sets `CLEO_OWNER_OVERRIDE=1` to bypass the AC-coverage gate entirely. * Mirrors the existing `ForceBypassRecord` shape from `gate-audit.ts` * (T832) so the same downstream tools can read both. The `kind` field * discriminates AC-coverage entries from the existing evidence-gate * entries. */ export interface AcCoverageForceBypassEntry { /** Discriminator — `'ac-coverage'` for this gate. */ kind: 'ac-coverage'; /** ISO-8601 timestamp of the bypass. */ timestamp: string; /** Task that was completed under the override. */ taskId: string; /** Operator-supplied justification (from `CLEO_OWNER_OVERRIDE_REASON`). */ reason: string; /** Agent / user identity that performed the completion. */ actor: string; /** Unsatisfied ACs that were skipped by the bypass — captured for post-mortem. */ unsatisfied: UnsatisfiedAc[]; } /** * Append an AC-coverage force-bypass entry to `.cleo/audit/force-bypass.jsonl`. * Re-uses the canonical force-bypass log per ADR-051 §6.2; the `kind` * discriminator on the entry distinguishes AC-coverage bypasses from * existing evidence-gate bypasses. * * Errors propagate — like {@link appendAcWaiverAudit} the audit trail * is load-bearing. * * @param entry - Pre-built audit entry. * @param projectRoot - Absolute path to the project root. */ export declare function appendAcCoverageForceBypass(entry: AcCoverageForceBypassEntry, projectRoot: string): Promise; /** * Inspect the runtime env for the canonical `CLEO_OWNER_OVERRIDE=1` * + `CLEO_OWNER_OVERRIDE_REASON=` pair. Returns the reason when * BOTH are present; returns `null` when override is off or the reason * is missing/blank. * * Centralised here so the gate, the audit writer, and the dispatch * layer agree on the exact semantics. Mirrors the convention used by * `gate-audit.ts`. */ export declare function readOwnerOverride(env?: NodeJS.ProcessEnv): string | null; //# sourceMappingURL=ac-coverage-gate.d.ts.map