/** * Evidence-based gate validation (ADR-051 / T832). * * Parses and validates evidence atoms for `cleo verify`. Each atom is checked * against the filesystem, git, structured test-run JSON output (vitest / * pytest / cargo-nextest etc.), or a project-resolved toolchain exit code. * Soft evidence (`url:`, `note:`) is accepted without validation. * * Tool resolution is project-agnostic per T1534 / ADR-061: * - {@link resolveToolCommand} maps `tool:` to a runnable command * using `.cleo/project-context.json` and per-`primaryType` fallbacks. * - {@link runToolCached} memoises results per * `(canonical, cmd, args, treeHash, envFingerprint, resourceEnv)` (the * heap and worker limits the run gets, T12989) and serialises * concurrent identical runs via a cross-process lock, * preventing the resource thrash observed when multiple `cleo verify` * invocations spawned full toolchains in parallel. * * @task T832 * @task T1534 * @adr ADR-051 * @adr ADR-061 */ import type { EvidenceAtom, EvidenceMergeAnchor, EvidenceValidationContext, GateEvidence, Task, VerificationGate } from '@cleocode/contracts'; import type { ChangeSetDeps } from './change-set.js'; import type { ViewComponentPr } from './component-pr.js'; import { type ToolRunResult } from './tool-cache.js'; import { CANONICAL_TOOLS, type CanonicalTool } from './tool-resolver.js'; import { classifyEvidenceTask, isDocumentArtifact } from './verification-policy.js'; /** * Valid tool names recognised by the `tool:` evidence atom. * * Sourced from {@link listValidToolNames} so canonical names + every legacy * alias resolve identically. Use {@link isValidToolName} or pass to * {@link resolveToolCommand} directly — direct array indexing is no longer * the canonical path (post-T1534). * * @task T832 * @task T1534 */ export declare const VALID_TOOLS: readonly string[]; /** * Type of a supported evidence tool. Post-T1534, this is widened to every * canonical tool name plus every alias accepted by {@link resolveToolCommand}. * * Existing callers that assigned `'pnpm-test' | 'biome' | ...` continue to * compile because those literal types remain assignable to `string`. * * @task T832 * @task T1534 */ export type EvidenceTool = CanonicalTool | string; /** * Test whether a string is a recognised tool name (canonical or alias). * * @task T1534 */ export declare function isValidToolName(name: string): boolean; /** * @deprecated Since T1534 — tool commands are resolved per-project from * `.cleo/project-context.json` via {@link resolveToolCommand}. This export * is retained as an empty record for back-compat with downstream callers * that destructured the legacy table; new code MUST call the resolver. * * @task T1534 */ export declare const TOOL_COMMANDS: Record; /** * Minimum evidence required for each verification gate. * * - A single atom kind means at least one atom of that kind MUST be present. * - A tuple of kinds means all listed kinds MUST be present. * - Alternatives are modeled as separate sets — if ANY set is satisfied the * evidence is accepted. * * **Source of truth (T10337):** the underlying spec now lives in * {@link GATE_EVIDENCE_REQUIREMENTS} in `@cleocode/contracts`. This export * is the legacy projection — `Record` — kept for back- * compat with consumers that hard-coded the nested-array shape. New code * SHOULD use {@link validateEvidenceForGate} from `@cleocode/contracts` * directly. * * @task T832 * @task T1515 * @task T10337 * @adr ADR-051 §2.3 */ export declare const GATE_EVIDENCE_MINIMUMS: Record; /** * Minimum LOC reduction percentage required when the `engine-migration` label * is present on a task. * * Tasks claiming to migrate an engine MUST demonstrate a measurable reduction * in lines of code to prevent structural-only migrations (T1604). * * @task T1604 */ export declare const ENGINE_MIGRATION_MIN_REDUCTION_PCT = 10; /** * Result of parsing a raw evidence string into structured atoms. * * @task T832 */ export interface ParsedEvidence { atoms: ParsedAtom[]; } /** * An atom that has been parsed from the CLI syntax but not yet validated * against filesystem / git / tools. * * @task T832 */ export type ParsedAtom = { kind: 'commit'; sha: string; } | { kind: 'files'; paths: string[]; } | { kind: 'test-run'; path: string; } | { kind: 'qa-run'; path: string; } | { kind: 'tool'; tool: string; } | { kind: 'url'; url: string; } | { kind: 'note'; note: string; } | { kind: 'loc-drop'; fromLines: number; toLines: number; } | { kind: 'callsite-coverage'; symbolName: string; relativeSourcePath: string; } | { /** * Decision atom — a brain_decisions row that IS the canonical artifact * for a decision-only task. Satisfies the `implemented` gate when combined * with `files:` pointing to a research note. * * @task T1875 */ kind: 'decision'; /** Decision ID from `brain_decisions.id` (e.g. `"D-arch-001"`). */ decisionId: string; } | { /** * Pull-request atom — references a GitHub PR by number. Validation * resolves the PR via `gh pr view` and checks state=MERGED plus * all required-workflow checks green. This provides provenance; * task criteria, artifacts, testing and review require separate proof. * * @task T9764 * @task T9838 */ kind: 'pr'; /** PR number (positive integer); the integration PR in `pr:@`. */ prNumber: number; /** Component PR the integration PR landed (`pr:@`, T12671). */ componentPrNumber?: number; /** `pr:/#`: the lowercased repository the number belongs to (T13912). */ repo?: string; } | { /** * Merge-commit CI atom — required checks green on a merged PR's merge * commit (D11149). Accepted only with `evidence.ciSatisfies`. * * @task T12634 */ kind: 'ci'; /** PR number (positive integer); the integration PR in `ci:@`. */ prNumber: number; /** Component PR the integration PR landed (`ci:@`, T12671). */ componentPrNumber?: number; /** `ci:/#`: the lowercased repository the number belongs to (T13912). */ repo?: string; } | { /** * Cross-task AC-binding atom — references an acceptance criterion on * another task in the same Saga (or root Epic when no Saga). The atom * carries either the canonical UUIDv4 (`targetAcId`) OR the positional * alias (`targetAcAlias`) — never both, never neither. Optional * `versionPin` captures the target AC's `updated_at` at mint time. * * This entry covers PARSING ONLY (T10506); the 5-check validator * semantics (target exists, target not terminal, AC exists, same-saga * scope) ship in T10507. See ADR-079-r2 §2.1 for the ABNF and §2.4 * for the runtime validator contract. * * @task T10506 * @adr ADR-079-r2 */ kind: 'satisfies'; /** Target task ID — `T<1-7 digits>` per ADR-079-r2 §2.1. */ targetTaskId: string; /** Lowercase UUIDv4 — populated for canonical form; undefined for alias form. */ targetAcId?: string; /** `AC<1-4 digits>` alias — populated for alias form; undefined for UUID form. */ targetAcAlias?: string; /** Optional `@` pin captured at mint time. */ versionPin?: string; }; /** * Parse the CLI `--evidence` string into structured atoms. * * Syntax: * evidence-list := atom ';' atom ';' ... * atom := kind ':' payload * payload for files: comma-separated paths * payload for everything else: opaque string until next ';' * * Delegates the syntactic parse to * {@link parseEvidenceString} in `@cleocode/contracts` (T10337) and wraps the * `EvidenceParseError` in the legacy `CleoError(VALIDATION_ERROR, ...)` * envelope so existing CLI surfaces continue to receive the expected exit * code and `fix:` hint. * * @param raw - Raw CLI string from `--evidence` * @returns Parsed atoms ready for {@link validateAtom} * @throws CleoError(VALIDATION_ERROR) for malformed input * * @example * ```ts * parseEvidence('commit:abc123;files:a.ts,b.ts;tool:biome'); * // => { atoms: [{kind:'commit',sha:'abc123'}, ...] } * ``` * * @task T832 * @task T10337 */ export declare function parseEvidence(raw: string): ParsedEvidence; /** * Result of validating one atom — success carries the validated form * (with sha256 / exit codes populated), failure carries a human-readable * reason string. * * @task T832 */ export type AtomValidation = { ok: true; atom: EvidenceAtom; } | { ok: false; reason: string; codeName: string; }; /** * The two roots an evidence atom may be about, named so an arm cannot inherit * the wrong one. * * gh#1365 was fixed once by threading a single `executionRoot` parameter, and * the fix reached two of the switch's arms. The reason a single parameter could * not close it is that **"root" is one word for two things**: the tree the * evidence is ABOUT, and the store CLEO keeps its own records in. A parameter * called `root` relocates that ambiguity into a signature rather than removing * it, so the next arm silently picks whichever the author had in mind. * * With both fields named, every arm DECLARES what it is about by which field it * reads. `decision:` reading {@link storeRoot} is self-documenting rather than * an exception needing a comment; `pr:` reading both is obviously correct * rather than obviously odd; and a new arm cannot inherit a default, because * there is no default. * * The rule, stated once: **use the root that matches what the atom is about.** * Evidence about the repository under test → {@link executionRoot}. Evidence * about CLEO's own records → {@link storeRoot}. * * @task T12238 (gh#1365) */ export interface EvidenceRoots { /** * Where CLEO keeps its own state — `.cleo/`, the BRAIN db, project context, * the evidence caches. NOT necessarily a git repository. */ readonly storeRoot: string; /** * The repository the evidence is about. Differs from {@link storeRoot} * whenever the CLEO root is a parent of, or a worktree sibling of, the git * checkout — which is this fleet's normal multi-project layout. */ readonly executionRoot: string; } /** * Validate a single parsed atom against the filesystem / git / tools. * * @param parsed - Parsed atom from {@link parseEvidence} * @param projectRoot - Absolute path to project root (for resolving files, git) * @param taskId - Optional CLEO task ID. When provided, enables branch-scope * check (T9178), content-intersect check (T9245), worktree-aware HEAD * resolution (T-WT-3), and git-show file fallback for branch-only files * (T11959). * @param siblingCommitSha - Optional sha of a sibling `commit:` atom from the * same evidence string. When provided, `files:` paths are resolved against * that commit's tree FIRST (T12107 / gh#1195) before falling back to the * worktree and git refs — covers a merged commit whose files are not yet * present in the local checkout. * @param context - Current task, gate and acceptance criteria used to validate scoped proof. * @returns Validation outcome with canonicalised form on success * @example * ```ts * const result = await validateAtom(parsedAtom, projectRoot, task.id, undefined, context); * ``` * * @task T832 * @task T11959 * @task T12107 * @adr ADR-051 §3 */ export declare function validateAtom(parsed: ParsedAtom, projectRoot: string, /** T9178: When provided, validates commit is on task/ branch. */ taskId?: string, /** T12107: sha of a sibling `commit:` atom in the same evidence string. */ siblingCommitSha?: string, context?: EvidenceValidationContext): Promise; /** * Warning code emitted when the T9245 content-intersect check was computed * from acceptance PROSE rather than from a declared `task.files` list, and so * was downgraded from blocking to advisory (gh#1240). * * A distinct, stable code so the downgrade is COUNTABLE: "how many tasks rely * on derived AC files?" is answerable by grepping envelopes rather than by * reading code. If the answer is "almost all", `--files` adoption is the real * problem and this tier boundary is hiding it; if "almost none", the change * costs nearly nothing. Shipping a gate change blind to its own blast radius * would be a poor look given what gh#1240 is about. * * @task T12118 (gh#1240) */ export declare const W_AC_FILES_DERIVED = "W_AC_FILES_DERIVED"; /** * Heuristic guard that decides whether a regex-extracted slash token is a * plausible repository file path rather than a URL or prose fragment. * * A token passes when it either: * - starts with a known {@link REPO_DIR_PREFIXES} directory, OR * - ends with a file extension in {@link REPO_FILE_EXTENSIONS} * * The guard intentionally accepts false negatives (omits exotic paths) rather * than false positives (includes internet hostnames). When a task owner needs * an exotic path captured they can use the explicit `--files` flag on * `cleo add` / `cleo update`. * * @param token - Slash-containing token extracted from AC text. * @returns `true` when the token looks like a repo path. * * @internal * @task T11960 */ export declare function isRepoPathLike(token: string): boolean; /** * Extract the canonical list of files a task's acceptance criteria declare. * * Resolution order: * 1. `task.files` array — authoritative when populated (set via `--files`) * 2. AC string parsing — extract path-like tokens from each * `task.acceptance` string, then filter with {@link isRepoPathLike} * to discard URL/prose false-positives (T11960). * * Returns `null` when the task declares no AC files at all — caller MUST * interpret this as "skip content-intersect" rather than "verify fails". * * @internal * @task T9245 * @task T11960 */ export declare function extractTaskAcFiles(task: { files?: string[] | null; acceptance?: ReadonlyArray | null; }): string[] | null; /** * Where an AC file list came from. * * `declared` — someone wrote `--files`. A statement of intent, and the only * tier with the authority to BLOCK a gate. * * `derived` — a regex found path-shaped tokens in acceptance prose. A guess, * and advisory only. See {@link extractTaskAcFilesWithProvenance}. * * @task T12118 (gh#1240) */ export type AcFilesProvenance = 'declared' | 'derived'; /** * Extract a task's AC file list together with its provenance. * * ## Why provenance matters (gh#1240) * * `task.files` is the documented SSoT, and prose parsing is documented as a * fallback for legacy tasks predating `--files`. But the T9245 content- * intersect gate treated both identically and BLOCKED on either — so **a * heuristic fallback was wired to a blocking gate**. * * The reported symptom is the sharpest possible demonstration: an AC reading * "the diff touches no line of src/lib/compound-catalog.ts" had the filename * scraped out of it and the negation discarded, so the gate demanded a * modification to the one file the task forbade touching. The gate became * satisfiable only by violating the criteria it was enforcing, and every * honest escape was worse than the check: modify the forbidden file, rewrite * the AC to hide the filename, or burn an audited owner override on correct * work. * * The negation is the most spectacular symptom, not the defect. A regex * cannot tell a target from a prohibition, an example, or a cross-reference, * and the incentive created by getting it wrong is to stop writing negative * constraints — which are among the most valuable ACs there are, because * "do not modify X" is how a blast radius gets pinned. * * So: **enforcement requires a declaration; a guess may only advise.** The * caller blocks on `declared` and warns on `derived`. * * @param task - Task fields to read. * @returns Files plus the provenance the caller must gate enforcement on. * * @task T12118 (gh#1240) * @task T9245 */ export declare function extractTaskAcFilesWithProvenance(task: { files?: string[] | null; acceptance?: ReadonlyArray | null; }): { files: string[] | null; provenance: AcFilesProvenance; }; /** * Determine whether ` ∩ ` is non-empty. * * Tolerates path mismatches caused by: * - leading `./` prefix * - trailing slashes (directories listed in AC) * - case-insensitive filesystems (rare on Linux/CI but defensive) * - monorepo nesting: AC sub-path `scripts/x.py` matches diff path * `plugins//scripts/x.py` via segment-boundary suffix (T12029). * The `/${ac}` guard prevents partial-name false positives like * `ipts/x.py` matching `scripts/x.py`. * * @internal * @task T9245 * @task T12029 */ export declare function diffIntersectsAc(diffFiles: string[], acFiles: string[]): boolean; /** * Resolve a worktree path to the canonical main repo path by parsing * the .git gitlink file. Returns projectRoot as-is for non-worktree dirs. * * When a git worktree is active, `.git` is a plain FILE containing a line of * the form `gitdir: /abs/path/to/main/.git/worktrees/`. Stripping the * last 3 path components from that gitdir path yields the main repo root. * * Used by {@link checkCommitContentIntersect} to ensure task metadata is always * read from the canonical `tasks.db` in the main repository rather than from * the stale point-in-time copy that was bootstrapped into the worktree at spawn * time (Bug C from the E-WORKTREE-IVTR spec). * * @param projectRoot - Absolute path that may be a git worktree or the main repo. * @returns Absolute path to the canonical main repository root. * * @task T-WT-2 * @epic T9586 */ export declare function resolveCanonicalProjectRoot(projectRoot: string): string; /** * What the evidence under validation reveals about which repository it belongs * to, used only to break a tie between sibling checkouts. * * @task T12308 */ export interface EvidenceExecutionRootHints { /** * SHA the evidence is about — the `commit:` atom's own, or the sibling * `commit:`/`pr:` merge SHA that anchors a `files:` atom. */ commitSha?: string; } /** * Resolve the tree that evidence tools should RUN in, given the CLEO store * root. * * `projectRoot` is the store root. For a git worktree, `getProjectRoot()` * deliberately resolves to the MAIN repo so all worktrees share one `.cleo/` * database — correct for the store, wrong as a tool's working directory. * Using it to spawn `tool:` evidence made a verify launched from a worktree * measure the main checkout instead (gh#1220, gh#1226): a peer's in-flight * branch and untracked files decided the result. A red peer produced a false * FAIL; a green peer produced a silent false PASS attesting a run that never * touched the code under test (gh#1230). * * Resolution is deliberately CONSERVATIVE: the caller's git toplevel is used * only when it belongs to the SAME project — i.e. when its canonical main * repo equals `projectRoot`. Anything else (an unrelated cwd, a test harness * pointing at a tmpdir, a non-git cwd) falls back to `projectRoot`, so * single-checkout behaviour is bit-for-bit unchanged. * * gh#1462 added the other direction: when `projectRoot` is a PARENT of the * checkout, neither the caller's cwd (which is that parent) nor `projectRoot` * is a git work tree, and `gh`/`git` run there fail with "fatal: not a git * repository". The checkout is a direct child in that layout, so it is used * when exactly one child is a work tree. * * T12308 closed the two holes that layout still had. A root parenting SEVERAL * repositories — the reported case had thirteen — had no resolution at all, * and the remediation the failure printed (`GIT_DIR`/`GIT_WORK_TREE`) could not * work, because the guard asks whether the CURRENT DIRECTORY is in a work tree * and the CLEO root never is. So the order is now, most to least specific: * * 1. an operator declaration (`CLEO_EVIDENCE_GIT_ROOT`, `GIT_WORK_TREE`, or * `evidence.gitRoot` in `.cleo/project-context.json`) — honoured by * RUNNING there, which is what the old advice never did; * 2. the caller's own checkout, when it belongs to this project; * 3. `projectRoot` itself; * 4. the one child checkout, or — when the evidence names a commit — the one * child checkout that actually CONTAINS it, which resolves a multi-repo * root with no configuration at all. * * Anything still ambiguous keeps `projectRoot`, and the atom reports the * candidates by name rather than attesting against a guessed repository. * * @param projectRoot - Absolute CLEO store root. * @param cwd - Directory the CLI was invoked from. Defaults to `process.cwd()`. * @param hints - What the evidence itself reveals about which repo it is about. * @returns Absolute path of the tree to execute in. * * @task T12112 (gh#1220, gh#1226, gh#1230) * @task gh#1462 * @task T12308 */ export declare function resolveEvidenceExecutionRoot(projectRoot: string, cwd?: string, // CWD-OK: the caller's invocation dir is the subject, not a stand-in for the project root (gh#1220) hints?: EvidenceExecutionRootHints): string; /** * One sentence naming the heap and worker plan a memory-bound tool ran under * (T13122), for a message about a kill; `''` for any other tool. */ /** * The `E_EVIDENCE_TOOL_KILLED` validation for a tool run that started and was * killed, by a signal or for resources (a wrapper's exit 128 + n, a heap OOM * the runner caught), else `null` (T13013). One message for every path that * runs a tool (`tool:`, the affected-tests run, `cleo done`), naming the * systemd memory ceiling when one applies ({@link describeMemoryLimit}) and * where the heap is really set. * * gh#1381: a tool killed after it started is a different fact from a tool * that never started; "binary missing" sent operators hunting a binary that * was present. * * @param label - How the run is named: `Tool "test" → pnpm run test`. * @param canonical - The canonical tool, for the memory ceiling. * @param result - The run. * @returns The failed validation, or `null` when the run was not killed. * * @task T13013 */ export declare function killedValidation(label: string, canonical: CanonicalTool, result: Pick): Extract | null; export { CANONICAL_TOOLS }; /** * Check that the provided evidence atoms satisfy the LOC-drop requirement for * engine-migration tasks. * * Returns `null` when the requirement is satisfied; otherwise returns a human- * readable reason string suitable for use as an `E_EVIDENCE_INSUFFICIENT` * error message. * * @param atoms - Already-validated evidence atoms. * @param minReductionPct - Minimum reduction percentage required (default: 10%). * @returns `null` on success, error message on failure. * * @task T1604 */ export declare function checkEngineMigrationLocDrop(atoms: EvidenceAtom[], minReductionPct?: number): string | null; /** * The canonical label that triggers callsite-coverage gate enforcement. * * When a task carries this label the `implemented` gate MUST be accompanied * by a `callsite-coverage` evidence atom proving the exported symbol is * referenced from a production callsite. * * @task T1605 */ export declare const CALLSITE_COVERAGE_LABEL = "callsite-coverage"; /** * The task linkage `pr:` provenance requires, shared with `ci:` (T12634): * the PR's changed-file inventory is complete, and the PR either cites the * task in its title, body or head branch, or its diff intersects the task's * declared files — and when files are declared, the diff MUST intersect them. * * @param prNumber - The PR being linked. * @param pr - The verified PR (title, body, head branch, changed files). * @param context - The task the evidence is being recorded for. * @returns The refusal, or null when the PR is linked to the task. * @task T12634 */ export declare function checkPrTaskLinkage(prNumber: number, pr: { title: string; body: string; headRefName: string; changedPaths: string[]; changedFileCount: number; changedFilesError?: string; }, context: EvidenceValidationContext): { ok: false; reason: string; codeName: string; } | null; /** The PR whose text and change link a task: the PR itself, or its component. */ type LinkedPr = { title: string; body: string; headRefName: string; changedPaths: string[]; changedFileCount: number; changedFilesError?: string; /** Deleted paths (component PRs only, T12689). */ deletedPaths?: string[]; }; /** * For `pr:`/`ci:` evidence, the PR a task is linked through (T12671): the PR * itself, or — with a component — the component PR verified as landed by the * integration PR, carrying the component's surviving files only. * * @param prNumber - The (integration) PR the atom names. * @param landing - Its verified resolution. * @param roots - Evidence roots (git runs in the execution root). * @param componentPrNumber - Component PR, when the atom names one. * @param view - Component PR reader (tests inject; defaults to `gh pr view`). * @returns The linkage subject, or the refusal. * @task T12671 */ export declare function linkedPrChange(prNumber: number, landing: { title: string; body: string; headRefName: string; headRefOid?: string; mergeCommitSha: string; changedPaths: string[]; changedFileCount: number; changedFilesError?: string; }, roots: EvidenceRoots, componentPrNumber?: number, view?: ViewComponentPr): Promise<{ ok: true; prNumber: number; pr: LinkedPr; } | { ok: false; reason: string; codeName: string; }>; export { classifyEvidenceTask, isDocumentArtifact }; /** * The merge commit whose tree holds a `files:` path's bytes when one evidence * write carries several `pr:` atoms (T12848): the PR that changed the path — * the latest merge when several did — else the latest merge overall, whose * tree is the newest state the write attests. With one PR it is that PR's * merge, exactly as before. Verify and complete-time re-validation both use * this, so the bytes hashed at verify are the bytes re-checked at complete. * * @param prs - Verified PR anchors of the write (merge sha, mergedAt, changed paths). * @param path - The `files:` path being inspected (repo-relative). * @returns The anchor merge SHA, or `undefined` when there is no PR. * @example * ```ts * const sha = prMergeCommitForPath(prAtoms, 'src/fix.ts'); * ``` * @task T12848 */ export declare function prMergeCommitForPath(prs: ReadonlyArray, path: string): string | undefined; /** * Require explicit criterion linkage to inspected artifacts and real gate results. * @param context - Current task classification, requested gates and criterion records. * @param gate - Individual gate whose evidence is being assessed. * @param atoms - Independently validated artifacts, results and criterion references. * @returns A refusal reason when proof is insufficient, otherwise null. * @remarks PR state and CI provenance alone cannot establish every acceptance gate. * @example * ```ts * const refusal = checkTaskEvidenceContext(context, "implemented", validatedAtoms); * ``` */ export declare function checkTaskEvidenceContext(context: EvidenceValidationContext, gate: VerificationGate, atoms: EvidenceAtom[]): string | null; /** * Check that the provided evidence atoms satisfy the callsite-coverage * requirement for tasks carrying the `callsite-coverage` label. * * Returns `null` when the requirement is satisfied; otherwise returns a * human-readable reason string suitable for use as an `E_EVIDENCE_INSUFFICIENT` * error message. * * @param atoms - Already-validated evidence atoms. * @returns `null` on success, error message string on failure. * * @task T1605 */ export declare function checkCallsiteCoverageAtom(atoms: EvidenceAtom[]): string | null; /** * Was this task's `implemented` gate satisfied by a DECISION rather than by a * code change? * * ADR-051 lets `implemented` be satisfied by `[decision, files]` or * `[decision, note]` — the shape of a pure audit, review or spike: read code, * record findings, change nothing. But `testsPassed` accepts only * `test-run | tool | pr` and `qaPassed` only `tool | pr`, so such a task could * not complete (gh#1215). The remaining escapes are all unusable by * construction: `tool:test` is meaningless for a task that changed nothing, * and `CLEO_OWNER_OVERRIDE` is capped per session and rejected on critical * gates anyway. A correctly-evidenced audit task simply stayed pending. * * A decision-only task has no tests to run and nothing to lint, so those gates * are satisfied by ABSENCE — the same reasoning as the T12083 * `notApplicable` tool atom, which records that a gate was satisfied because * the project has no such toolchain rather than silently passing it. * * Deliberately narrow: a `commit:` or `pr:` atom anywhere in the `implemented` * evidence means code DID change, and the normal gates apply in full. The * exemption cannot be reached by choosing weaker evidence — `implemented` * still had to be satisfied first, and `decision:` is a hard atom validated * against the BRAIN decision-store. * * @param implementedEvidence - Stored evidence for the `implemented` gate. * @returns `true` when the task demonstrably changed no code. * * @task T12125 (gh#1215) * @adr ADR-051 §2.3 */ export declare function isDecisionOnlyImplementation(implementedEvidence: { atoms?: ReadonlyArray<{ kind: string; }>; } | null | undefined): boolean; /** * Gates whose evidence minimum a decision-only task skips (T12220): they are * RELAXED, not satisfied. `testsPassed` and `qaPassed` measure the effect of a * code change; with none, a `decision:` atom (with a `note:`) is accepted in * place of a test or QA result, but each gate is still verified explicitly * (`cleo verify --gate testsPassed --evidence "decision:;note:"`). * Nothing is satisfied by absence. * * @task T12125 (gh#1215) * @task T12220 */ export declare const DECISION_ONLY_RELAXED_GATES: readonly VerificationGate[]; /** * What a `testsPassed`/`qaPassed` refusal adds about the routes for work with * no code change (T12220), or `null` for any other gate: the decision-only * route (each relaxed gate verified explicitly with `decision:`+`note:`, and * that re-verifying a gate REPLACES its atoms), and the gate profiles * (T13474) that make the gates not owed at all. When `implemented` carries a * `commit:`/`pr:` atom it says only that the change is code and needs a test * or QA result: it never explains how to strip code evidence. * * @param gate - The refused gate. * @param implemented - The task's recorded `implemented` evidence, if any. * @returns The note, or `null`. * @task T12220 */ export declare function decisionOnlyRouteNote(gate: VerificationGate, implemented: { atoms: readonly EvidenceAtom[]; } | undefined): string | null; /** * Check whether a set of validated atoms satisfies the minimum evidence * required for a given gate. * * @param gate - The gate being verified * @param atoms - Validated atoms * @returns null when satisfied; otherwise the reason message * * @task T832 * @adr ADR-051 §2.3 */ export declare function checkGateEvidenceMinimum(gate: VerificationGate, atoms: EvidenceAtom[]): string | null; /** * Detailed variant of {@link checkGateEvidenceMinimum} that returns the * machine-readable failure (with `message` + `hint`) instead of a flat * string. Use this when the caller needs the multi-line CLI `fix:` hint * (T9949) — `E_EVIDENCE_INSUFFICIENT` surfaces in engine-ops.ts populate * `engineError({fix: result.hint})` with this output. * * Returns `null` when the gate is satisfied. * * @param gate - The gate being verified * @param atoms - Validated atoms * @returns `null` when satisfied; `{ message, hint }` when not * * @task T9949 * @adr ADR-051 §2.3 */ export declare function checkGateEvidenceMinimumDetailed(gate: VerificationGate, atoms: EvidenceAtom[]): { message: string; hint: string; } | null; /** * Compose a {@link GateEvidence} record from validated atoms. * * @param atoms - Validated evidence atoms * @param capturedBy - Agent identifier * @param override - True when CLEO_OWNER_OVERRIDE is set * @param overrideReason - Reason supplied with the override * @param context - Task and criterion context used to capture durable proof bindings. * @param gate - Gate associated with the captured result and artifact references. * @returns Canonical GateEvidence ready to persist * * @task T832 */ export declare function composeGateEvidence(atoms: EvidenceAtom[], capturedBy: string, override?: boolean, overrideReason?: string, context?: EvidenceValidationContext, gate?: VerificationGate): GateEvidence; /** * Result of re-verifying stored evidence at complete time. * * @task T832 */ export interface RevalidationResult { stillValid: boolean; failedAtoms: Array<{ atom: EvidenceAtom; reason: string; }>; } /** * Why a `test-run:`/`qa-run:` result, or a `tool:` run (T13003), does not * describe this task's work (T14004), or null. The binding records the HEAD it * was bound to (a tool run, the HEAD it measured), and that HEAD must have seen * the task's `implemented` work: * * - `commit:` — the run head is that commit or contains it. An older commit * (the branch base, main before the work) never saw the work. * - `pr:` — the run head is, or contains, the merge commit or the PR's final * head: its recorded `headSha` (a squash merge too), else the merge's second * parent. An earlier branch commit never saw the whole change. * * "Seen" also holds when the HEAD carries a patch-equivalent commit * (`git cherry`): a rebase or a message-only amend keeps the patch, not the * SHA (T13003). * * Evidence bound in another task's worktree is refused even when `cleo * complete` runs from that same worktree. A commit git cannot read (a shallow * clone) is refused and said so. Skipped while no implemented commit is * recorded, or for an atom with no HEAD. * * @param task - The task (its recorded `implemented` evidence is read). * @param atoms - The gate's atoms. * @param projectRoot - The CLEO store root (the task's checkout is resolved from it). * @returns The refusal, or null. * @task T14004 * @task T13003 */ export declare function boundHeadGapReason(task: Pick, atoms: readonly EvidenceAtom[], projectRoot: string): Promise; /** * The evidence kind whose gates a task owes now, and why a non-code kind was * raised to `code` (T14001, reviews of #2413). * * @task T14001 */ export interface TaskEvidenceKindReport { /** The kind to read {@link requiredGatesFor} with. */ kind: ReturnType; /** Why a research or documentation task owes the code gates, when it does. */ reason?: string; } /** * The evidence kind whose gates a task owes now (T14001, reviews of #2413): * {@link classifyEvidenceTask}, raised to `code` when its recorded * `implemented` evidence or `extraPaths` (a change set) touch any * non-document path — `files:` paths, a `pr:` atom's changed paths, a * `commit:` atom's diff (merge and root commits included) — or when the * task's own work does ({@link taskOwnWorkPaths}: branches citing it, commits * on the default branch whose subject cites it, and its merged PRs). A kind * or label set by any agent never lets a code change skip its tests, and * work that cannot be inspected fails closed to `code`, with the reason. * A task {@link declaredEvidenceKind} leaves undecided is classified by that * same change set (T14048): documents only make it documentation. * * @param task - The task (kind, files, labels and recorded evidence are read). * @param projectRoot - The CLEO store root (commits are read in the task's checkout). * @param extraPaths - Further paths the work touched (a derived change set). * @param cwd - Invocation directory, which locates the task's worktree. * @param deps - Merged-PR discovery (tests inject; defaults to `gh`). * @returns The kind, and the reason when a non-code kind owes the code gates. * @task T14001 */ export declare function taskEvidenceKindReport(task: Pick, projectRoot: string, extraPaths?: readonly string[], cwd?: string, deps?: Pick): Promise; /** * {@link taskEvidenceKindReport}'s kind alone. * * @param task - The task (kind, files, labels and recorded evidence are read). * @param projectRoot - The CLEO store root. * @param extraPaths - Further paths the work touched (a derived change set). * @param cwd - Invocation directory, which locates the task's worktree. * @returns The kind to read {@link requiredGatesFor} with. * @task T14001 */ export declare function taskEvidenceKind(task: Pick, projectRoot: string, extraPaths?: readonly string[], cwd?: string): Promise>; /** * Options for {@link revalidateEvidence}. * * @task T12102 */ export interface RevalidateOptions { /** * Progress sink invoked once per hard atom as it is re-validated, with a * short human-readable line (e.g. `commit:abc1234 ok (cached) 3ms`). The * `cleo complete` path forwards this to stderr so a long re-validation is * observable; stdout stays pure JSON per the output contract. Other * callers omit it and see no behavioural change. * * @task T12102 (gh#1196) */ onProgress?: (message: string) => void; } /** * Critical gates for which `override`-only evidence is NEVER accepted. Closes * the override-loophole proven 2026-05-12 — 13 mis-completed tasks in the * 2026-05-11 campaign passed `implemented`/`testsPassed` with override-only * evidence. Audit: `.cleo/rcasd/campaign-validation-2026-05-12/SYNTHESIS.md`. * * Other gates (`qaPassed`, `documented`, `securityPassed`, `cleanupDone`, * `nexusImpact`) MAY still accept `note:` waivers / overrides per ADR-051. * * @task T9245 * @adr ADR-051 */ export declare const CRITICAL_GATES_NO_OVERRIDE: readonly VerificationGate[]; /** * Whether an evidence atom is a "hard" (programmatically-validated) atom. * * Hard atoms — `commit`, `files`, `test-run`, `tool`, `pr`, `decision` — carry * programmatic proof. Soft atoms — `override`, `note`, `url` — do not. The * T9245 critical-gate rule (`implemented`, `testsPassed`) requires at least one * hard atom even under an owner override. * * This is the SINGLE SOURCE OF TRUTH for the predicate. Both the verify-time * acceptance check ({@link ../validation/engine-ops.validateGateVerify}) and the * complete-time re-validation ({@link revalidateEvidence}) MUST use it so the * two checks can never diverge (gh#1105: verify accepted an override+soft-atom * green that complete then reversed). * * @task T9245 * @task T12015 * @adr ADR-051 */ export declare function isHardAtom(atom: EvidenceAtom): boolean; /** * Re-validate stored evidence to detect tampering between verify and complete. * * Hard atoms (commit, files, test-run, tool) are re-executed. Soft atoms * (url, note, override) pass through unchanged. * * T9245: when `gate` ∈ {@link CRITICAL_GATES_NO_OVERRIDE}, override-only * evidence is rejected at re-validation time. Forces the worker to produce * real programmatic proof for `implemented` and `testsPassed`. * * @param evidence - Previously-stored evidence * @param projectRoot - Absolute path to project root * @param gate - Optional gate name; when provided enables the critical-gate * override-rejection check (T9245). Omit for back-compat callers. * @param taskId - Optional CLEO task ID; enables git-show fallback for files * that were on the task branch at verify time but are now on main after * merge (T11959). When provided, sha256 comparison uses the same git-show * resolution path as validate time so the checksums remain consistent. * @param options - Optional {@link RevalidateOptions} progress sink (T12102). * @returns Revalidation outcome * * @task T832 * @task T9245 * @task T11959 * @task T12102 * @task T12107 * @adr ADR-051 §5 / §8 (Decision 8) */ export declare function revalidateEvidence(evidence: GateEvidence, projectRoot: string, gate?: VerificationGate, taskId?: string, options?: RevalidateOptions): Promise; //# sourceMappingURL=evidence.d.ts.map