/** * `cleo complete` proves testsPassed/qaPassed from merged CI itself (T12960). * * Once a task's latest implementation has merged, its PR's required CI on the * merge commit is the strongest test evidence there is (D11149), and the * scoped runs recorded before merge stop counting (D11150, T12656, T12965). * Rather than refuse and send the agent off to run the whole suite locally, * completion synthesizes the `ci:` atom, validates it through the one * gate-write path (`validateGateVerify`, so nothing here is a second * validator), and records it with a note saying who recorded it and why. * * When CI does not hold, the refusal names the remedy that can work: still * pending → wait for CI and retry; a final red, a skipped or missing required * job, or a PR the `pr:` check refuses → fix CI, or record a targeted * `test-run:` on the default branch after the merge that covers the change * (T13623), which then stands in for testsPassed. Never an open-ended wait on CI that has * already concluded. When a lookup failed (`gh` unreachable, commits missing * locally), which PR carries the change is unknown: nothing is recorded and * the agent is told to retry or record `tool:test`, never to fix CI. * * Gates recorded here persist even when completion then fails on a later, * unrelated check (dependencies, AC coverage, IVTR): they are valid evidence * on their own, validated through the same path as `cleo verify`, and the * next `cleo complete` reuses them instead of resolving CI again. * * @task T12960 * @task T12656 * @task T12965 */ import type { AcRow, Task, VerificationGate } from '@cleocode/contracts'; import type { EngineResult } from '../engine-result.js'; import type { GateVerifyParams, GateVerifyResult } from '../validation/engine-ops.js'; import { type MergeProbeDeps } from './affected-scope.js'; /** Injectable I/O for {@link satisfyGatesFromMergedCi}. */ export interface MergedCiDeps { /** Merge-state probes (change-set `gh` lookup, git ancestry). */ merge?: MergeProbeDeps; /** Whether the project accepts `ci:` evidence; defaults to `evidence.ciSatisfies`. */ ciSatisfies?: (storeRoot: string) => boolean; /** * Current tree hash of the task's tree; defaults to the tool cache's * `captureTreeHash` in the task's change-set root — the root `cleo done` * and the `test-run:` binding use (T12965 review M2). */ currentTree?: (storeRoot: string, taskId: string) => Promise | string | null; /** Whether a commit has landed on origin's default branch; defaults to `isLandedOnOriginDefault` (T13627). */ landed?: (root: string, sha: string) => boolean; /** Canonical acceptance criteria of the task; defaults to the task store. */ acRows?: (storeRoot: string, taskId: string) => Promise; /** The gate write; defaults to `validateGateVerify`. */ recordGates?: (storeRoot: string, params: GateVerifyParams) => Promise>; } /** What {@link satisfyGatesFromMergedCi} did. */ export type MergedCiOutcome = { /** CI evidence was validated and recorded for {@link gates}. */ kind: 'recorded'; /** The PR whose merge-commit CI was recorded (`` or `@`). */ pr: string; /** Gates recorded from that CI. */ gates: VerificationGate[]; } | { /** The change merged and its required CI is still running: wait and retry. */ kind: 'wait-for-ci'; /** Why the CI does not hold yet. */ reason: string; } | { /** * The change merged and its required CI concluded without proving the * gates (red, skipped or missing required jobs, or the PR is refused): * fix CI, or record a covering test-run on the default branch (T13623). */ kind: 'ci-red'; /** Why the CI does not hold. */ reason: string; } | { /** Nothing recorded; the ordinary gate checks decide. */ kind: 'skipped'; /** Why the recorded testsPassed no longer stands, when it does not. */ testsPassedReason: string | null; /** Why the recorded qaPassed no longer stands (a qa-run whose tree moved, T13427). */ qaPassedReason: string | null; /** Why merged CI could not stand in, when it was tried. */ ciUnavailable?: string; }; /** * Whether a CI refusal describes checks that have not concluded yet, as * opposed to a final failure. Pending only when the reason names a pending * run and no concluded failure (`startup_failure` and every other * `*_failure` conclusion included), skip or missing check. * * @param reason - The refusal text from the `ci:`/`pr:` validators. * @returns True when waiting for CI can resolve it. * @task T12960 */ export declare function isPendingCiReason(reason: string): boolean; /** * Satisfy a task's testsPassed/qaPassed from its merged PR's required CI when * they are missing or no longer stand, before `cleo complete` checks gates. * * Runs only for required CI gates that are unrecorded, or a testsPassed that * {@link testsPassedSupersededReason} no longer accepts (a scoped run after * merge, or a test-run whose tree moved). Ordinary completions with standing * gates pay nothing. The PR is the merged PR that carries the task's latest * implementation ({@link taskChangeMergeState}) — never one that merely cites * the task; a stacked PR is not judged. * * @param task - The task being completed (as loaded). * @param storeRoot - CLEO store root. * @param requiredGates - The project's required verification gates. * @param deps - Injectable I/O. * @returns What was recorded, a CI refusal, or why nothing was. * @example * ```ts * const ci = await satisfyGatesFromMergedCi(task, root, ['implemented', 'testsPassed']); * if (ci.kind === 'wait-for-ci') throw new Error(ci.reason); * ``` * @task T12960 */ export declare function satisfyGatesFromMergedCi(task: Task, storeRoot: string, requiredGates: readonly VerificationGate[], deps?: MergedCiDeps): Promise; //# sourceMappingURL=complete-ci.d.ts.map