/** * Release engine operations — business logic layer. * * Contains all release domain logic migrated from * `packages/cleo/src/dispatch/engines/release-engine.ts` (ENG-MIG-5 / T1572). * * Each exported function returns `EngineResult` and is importable from * `@cleocode/core/internal` so the CLI dispatch layer can call them without * any intermediate engine file. * * Functions that require git CLI interaction (push, tag, commit, rollback-full, * changelog-since, ship) use `execFileSync` from `node:child_process` — the * same pattern already present in `release-manifest.ts`. * * @task T1572 — ENG-MIG-5 * @epic T1566 */ import { execFileSync } from 'node:child_process'; import { type EngineResult } from '../engine-result.js'; import { type ReleaseListOptions } from './release-manifest.js'; /** * Run a `git` invocation with stale-`.git/index.lock` recovery. * * Some developer environments (IDE indexers, shell prompts, status-line * scripts) race against the release engine's git operations and momentarily * hold the index lock, causing git to fail with * `fatal: Unable to create '.git/index.lock': File exists`. * * The CLEO release pipeline is a long-running multi-step flow — a transient * lock contention should not fail the whole release. This helper detects the * lock-conflict signature, removes a stale lock file (only if no other git * process is observed in stderr), waits briefly, and retries up to `maxRetries`. * * @param args Arguments for `git` (e.g. `['add', 'CHANGELOG.md']`). * @param opts Options passed to `execFileSync` (cwd, encoding, stdio…). * @param maxRetries How many times to retry on a lock conflict (default 3). * @returns The output of the successful invocation. * @throws The last error from `execFileSync` if all retries are exhausted. */ /** * @internal Exported for unit testing only. Not part of the public API. */ export declare function runGitWithLockRetry(args: readonly string[], opts: Parameters[2], maxRetries?: number): string; /** * Result returned by {@link pollPrMerged} on success. */ export interface PollPrMergedResult { /** The canonical merge commit OID returned by GitHub (40-char hex). */ mergeCommitOid: string; } /** * Poll `gh pr view --json state,mergeCommit` until the PR reaches * `state === "MERGED"` with a non-empty `mergeCommit.oid`, then return * the merge-commit OID. * * This prevents the tag-after-merge race where `git rev-parse HEAD` is * called before GitHub has landed the merge commit on the target branch * (T9504). * * @param prUrl The GitHub PR URL (or `owner/repo#number` form). * @param opts Polling options. * @param opts.pollIntervalMs Delay between polls. Default: 5 000. * @param opts.timeoutMs Total wait budget. Default: 300 000. * @param opts.cwd Working directory for `gh` invocation. * @returns `{ mergeCommitOid }` on success, `null` on timeout. * * @task T9504 */ export declare function pollPrMerged(prUrl: string, opts?: { pollIntervalMs?: number; timeoutMs?: number; cwd?: string; }): PollPrMergedResult | null; /** * Project-relative audit sentinel that proves the IVTR decoupling has run at * least once for a project. First-run also appends a JSONL audit row so the * decoupling event is traceable (matches the convention used by * `force-bypass.jsonl`, `worker-mismatch.jsonl`, etc.). * * @task T9537 */ export declare const IVTR_DECOUPLED_SENTINEL_FILE = ".cleo/audit/ivtr-decoupled.flag"; /** * Project-relative JSONL audit log for IVTR-decoupling events. Appended once * on the first {@link releaseShip} invocation per project; subsequent runs are * no-ops because the sentinel file already exists. * * @task T9537 */ export declare const IVTR_DECOUPLED_AUDIT_FILE = ".cleo/audit/ivtr-decoupled.jsonl"; /** * Write a one-time audit entry confirming the IVTR gate has been decoupled * from the release pipeline per T9537. The sentinel file * `.cleo/audit/ivtr-decoupled.flag` prevents re-logging on subsequent runs. * * Best-effort: never throws — release flow must not fail because the audit * directory is read-only or missing. * * @task T9537 */ export declare function writeIvtrDecouplingAuditOnce(cwd: string, epicId: string): boolean; /** * release.gate — Standalone IVTR phase gate check (RELEASE-03). * * Inspects all child tasks of the given epic and verifies that every task * whose IVTR loop has been started has reached the `released` phase. Tasks * without an IVTR loop (docs, chores) are reported as `unchecked` but are * NOT blocking. * * The `--force` flag bypasses the gate with a loud warning (owner-level * override only). * * @param epicId - Epic whose child tasks are inspected. * @param force - Bypass gate; emits owner-level warning. * @param projectRoot - Optional working directory. * @returns EngineResult with ReleaseGateCheckResult on success. * * @task T820 RELEASE-03 * @task T1416 */ export declare function releaseGateCheck(epicId: string, force: boolean, projectRoot?: string): Promise; /** * release.ivtr-suggest — IVTR → release auto-suggest (RELEASE-07). * * Called after an IVTR loop transitions to `released` for a given task. * Checks whether all sibling tasks in the parent epic are also released. If * so, emits a `suggestedCommand` pointing the operator toward `release ship`. * * @deprecated As of T9537 (Phase 5 of T9498), the IVTR gate has been * decoupled from the release pipeline per SPEC-T9345 §7 / ivtr-conflation-audit.md * Priority 1. This function still works for telemetry and external callers, * but `releaseShip` no longer reads or recommends it. ADR-051 evidence atoms * (validated by `runReleaseGates`) are the sole gate execution surface. * Remove in a future major version once all external integrations migrate. * * @param taskId - Task that just reached the `released` phase. * @param projectRoot - Optional working directory. * @returns EngineResult with IvtrAutoSuggestResult data. * * @task T820 RELEASE-07 * @task T1416 * @task T9537 — deprecated marker (decoupled from releaseShip) */ export declare function releaseIvtrAutoSuggest(taskId: string, projectRoot?: string): Promise; /** * release.prepare — Prepare a release. * * @task T4788 */ export declare function releasePrepare(version: string, tasks?: string[], notes?: string, projectRoot?: string): Promise; /** * release.list — List all releases (query operation via data read). * * @task T4788 */ export declare function releaseList(optionsOrProjectRoot?: ReleaseListOptions | string, projectRoot?: string): Promise; /** * release.show — Show release details (query operation via data read). * * @task T4788 */ export declare function releaseShow(version: string, projectRoot?: string): Promise; /** * release.commit — Mark release as committed (metadata only). * * @task T4788 */ export declare function releaseCommit(version: string, projectRoot?: string): Promise; /** * release.tag — Mark release as tagged (metadata only). * * @task T4788 */ export declare function releaseTag(version: string, projectRoot?: string): Promise; /** * release.gates.run — Run release gates (validation checks). * * @task T4788 */ export declare function releaseGatesRun(version: string, projectRoot?: string): Promise; /** * release.rollback — Rollback a release. * * @task T4788 */ export declare function releaseRollback(version: string, reason?: string, projectRoot?: string): Promise; /** * release.rollback.full — Full rollback: delete git tag, revert commit, * remove release record from DB, and optionally unpublish from npm. * * Sequence: * 1. Delete remote git tag (if pushed) * 2. Delete local git tag * 3. Revert the release commit (creates a new revert commit) * 4. Remove/flip release record in DB to 'rolled_back' * 5. (Optional) npm deprecate if npm registry is configured * * @task T820 RELEASE-05 */ export declare function releaseRollbackFull(version: string, options: { reason?: string; force?: boolean; unpublish?: boolean; }, projectRoot?: string): Promise; /** * release.cancel — Cancel and remove a release in draft or prepared state. * * @task T5602 */ export declare function releaseCancel(version: string, projectRoot?: string): Promise; /** * release.push — Push release to remote via git. * * Uses execFileSync (no shell) for safety. Respects config.release.push policy. * * Agent protocol guard (T4279): When running in agent context * (detected via CLEO_SESSION_ID or CLAUDE_AGENT_TYPE env vars), * requires a release manifest entry for the version. This ensures * agents go through the proper release.ship workflow rather than * calling release.push directly, maintaining provenance tracking. * * @task T4788 * @task T4276 * @task T4279 */ export declare function releasePush(version: string, remote?: string, projectRoot?: string, opts?: { explicitPush?: boolean; }): Promise; /** A single CI check status entry returned by release.pr-status. */ export interface PRCheckStatus { /** Check name (e.g. 'build', 'test'). */ name: string; /** GitHub check run status: 'QUEUED' | 'IN_PROGRESS' | 'COMPLETED'. */ status: string; /** GitHub check run conclusion: 'SUCCESS' | 'FAILURE' | 'CANCELLED' | null. */ conclusion: string | null; } /** Result shape for release.pr-status. */ export interface PRStatusResult { /** The version queried. */ version: string; /** The release branch name (e.g. release/v2026.5.43). */ releaseBranch: string; /** The PR URL resolved from the release branch. */ prUrl: string | null; /** All CI check statuses from gh pr checks. */ checks: PRCheckStatus[]; /** Whether all checks have completed successfully. */ allPassed: boolean; /** Whether any check has failed or been cancelled. */ anyFailed: boolean; } /** * release.pr-status — Resolve the open PR for a release branch and return * the current CI check statuses. * * Provides a way to manually poll after `release ship` is interrupted or * times out waiting for CI. * * @param version - Release version (with or without leading 'v'). * @param projectRoot - Optional working directory. * @returns EngineResult with PRStatusResult. * * @task T9095 */ export declare function releasePrStatus(version: string, projectRoot?: string): Promise>; //# sourceMappingURL=engine-ops.d.ts.map