/** * Branch integration for agent worktrees (T10862): default-branch resolution, * upstream fetch and landed-detection, the merge decision, the `--no-ff` merge * of `task/` into the local target branch (ADR-062) and its audit row. * * Owns merge / fetch / checkout policy only. Worktree add, remove, list and * lock stay in the lifecycle module ({@link ./branch-lock.ts}) and the * `@cleocode/worktree` facade; this module calls {@link pruneWorktree} after a * merge and never touches `git worktree` itself. * * @task T10862 * @task T10863 * @task T10864 * @adr ADR-062 */ import type { EvidenceAtom, WorktreeIntegrationOutcome, WorktreeIntegrationPlan, WorktreeMergeResult } from '@cleocode/contracts'; /** * Resolve the project's default integration branch in a project-agnostic way. * * Resolution order (per ADR-062): * * 1. `.cleo/config.json::git.defaultBranch` (explicit override). * 2. `git symbolic-ref refs/remotes/origin/HEAD` (what the remote calls * default — works for `master`, `main`, `trunk`, etc.). * 3. Probe local branches in order: `main`, `master`, `develop`, `trunk`. * 4. Fallback to `'main'`. * * @param projectRoot - Absolute path to the project root. * @returns The resolved default branch name (never throws). * * @task T1587 * @adr ADR-062 */ export declare function getDefaultBranch(projectRoot: string): string; /** * Verdict of {@link assessUpstreamIntegration}. * * - `landed` — the task branch is already on the upstream default branch * (ancestor of it, or its changes are fully contained in it). No local * merge may run. * - `nothing` — the task branch has no commits beyond the local default * branch (e.g. a worktree that was never committed to). There is nothing to * integrate, and it is NOT reported as landed upstream. * - `stale-target` — the local default branch is behind the upstream default * branch (possibly also ahead of it — diverged); a merge there would fork it * from upstream. No local merge may run. * - `proceed` — no upstream to compare against, or the local default branch is * current; the ADR-062 local `--no-ff` integration may run. * * @task T12773 */ export type UpstreamIntegrationKind = 'landed' | 'nothing' | 'stale-target' | 'proceed'; /** * Result of {@link assessUpstreamIntegration}. * * @task T12773 */ export interface UpstreamIntegrationAssessment { /** What the caller may do next. */ kind: UpstreamIntegrationKind; /** Remote-tracking ref compared against (e.g. `refs/remotes/origin/main`), or null. */ upstreamRef: string | null; /** * True when this assessment ran `git fetch ` (whether * or not it succeeded), so a later integration step need not fetch again. */ fetched: boolean; /** Commits on the upstream ref missing from the local default branch. */ behind: number; /** Commits on the local default branch missing from the upstream ref. */ ahead: number; /** * Exact shell command that syncs the local default branch with upstream, * or empty when no sync is needed. NEVER run by CLEO — the operator's * checkout is never moved (hint-only policy). */ syncCommand: string; /** Operator-facing explanation / next step (empty for `proceed`). */ hint: string; } /** * Default timeout for the upstream `git fetch` in * {@link assessUpstreamIntegration}. Short on purpose: the fetch is * best-effort, and a hung network or credential prompt must never stall * `cleo done` for the 180s generic git timeout. * * @task T12773 */ export declare const UPSTREAM_FETCH_TIMEOUT_MS = 20000; /** * Decide whether a local ADR-062 `--no-ff` integration of `branch` into * `targetBranch` is safe, by comparing against the upstream default branch * (`/`). * * T12773: `cleo done --pr ` for a task whose PR had already merged on * origin ran `git merge --no-ff task/` into a local `main` that was 199 * commits behind origin, creating a local merge commit that forked `main` * from `origin/main`. This check runs BEFORE any checkout or merge: * * - branch has no commits beyond the local default branch → `nothing`. * - branch already on upstream → `landed`; when the local default branch is * behind, the hint names the exact sync command. The checkout is never * moved (hint-only). * - local default branch behind upstream → `stale-target`; a distinct hint * when it is also ahead (diverged: unpushed local commits). * - otherwise (no remote, no upstream ref, current target) → `proceed`. * * The fetch is best-effort, non-interactive (`GIT_TERMINAL_PROMPT=0`, ssh * `BatchMode`) and bounded by {@link UPSTREAM_FETCH_TIMEOUT_MS}; offline, the * last-known remote-tracking ref is used. Never creates a commit and never * moves any ref other than the remote-tracking one the fetch updates. * * @param gitRoot - Absolute git root of the project checkout. * @param branch - Task branch (e.g. `task/T123`). * @param targetBranch - Resolved default branch (e.g. `main`). * @param opts.skipFetch - Skip `git fetch ` (fixtures). * @param opts.remote - Remote name (default `origin`). * @param opts.fetchTimeoutMs - Fetch timeout (default {@link UPSTREAM_FETCH_TIMEOUT_MS}). * @returns The assessment. * * @task T12773 * @adr ADR-062 */ export declare function assessUpstreamIntegration(gitRoot: string, branch: string, targetBranch: string, opts?: { skipFetch?: boolean; remote?: string; fetchTimeoutMs?: number; }): UpstreamIntegrationAssessment; /** * Options for {@link planWorktreeIntegration} and the merge run that shares * its decision (T14053). */ export interface WorktreeIntegrationDecisionOptions { /** Override the resolved default branch. */ targetBranch?: string; /** * Set when integration is switched off (`cleo complete --no-integrate`, * `CLEO_NO_AUTO_WORKTREE_COMPLETE`): the decision is `skip` with this reason. */ disabledReason?: string; /** Skip the upstream `git fetch` (the plan never fetches). */ skipFetch?: boolean; /** Timeout for the upstream fetch. */ fetchTimeoutMs?: number; } /** * Preview what completing `taskId` does with its worktree branch, without * changing git: the same decision the merge run makes, from local refs only * (no fetch). Shown by `cleo done --plan` (T14053). * * @param taskId - The CLEO task ID. * @param projectRoot - Absolute path to the project root. * @param opts - Target override and the switched-off reason, if any. * @returns Branch, worktree path, target branch, `merge`/`skip`/`refuse` and why. * * @example * ```ts * planWorktreeIntegration('T123', root).action; // 'skip' for a task merged by PR * ``` * @task T14053 */ export declare function planWorktreeIntegration(taskId: string, projectRoot: string, opts?: Omit): WorktreeIntegrationPlan; /** * Complete a worker task's worktree via `git merge --no-ff` (ADR-062). * * Canonical worktree integration per ADR-062. Preserves the full agent * commit graph instead of rewriting SHAs, so `git log --grep "T"` * returns the originating commits with their original authorship. * * Steps performed inside the worktree at * `~/.local/share/cleo/worktrees///`: * * 1. Resolve target branch via {@link getDefaultBranch} (or `opts.targetBranch` * override). NEVER hardcodes "main". * 1. T14053: {@link decideWorktreeIntegration} first — the same decision * `cleo done --plan` shows. A branch CLEO did not provision is skipped * (`notProvisioned`), never merged; a main checkout on another branch, * with uncommitted tracked changes, or a linked worktree is refused * (`rootRefused`) without switching or merging anything. * 1a. T12773: {@link assessUpstreamIntegration} — when `task/` already * landed on `origin/` the merge is skipped * (`landedUpstream: true`); when it has no commits beyond the local * target it is `nothingToIntegrate` without `landedUpstream`; when the * local target is behind (or diverged from) origin the merge is refused * (`staleTarget: true`) with a `hint`. None of these paths creates a * commit or moves the operator's checkout — syncing is hint-only. * 2. Optionally `git fetch` (skipped when step 1a already fetched), then * `git checkout ` in the project's git root. The task * branch is NOT rebased — agent SHAs are preserved. * 3. Run `git merge --no-ff task/ -m ": (worktree * merge)"`; a failed merge is aborted so the repo is left clean, and the * worktree is preserved. * 4. Capture the merge commit SHA and report it. * 5. Delegate worktree+branch removal to {@link pruneWorktree} (T1462). * * Project-agnostic: no string in this function hardcodes "main", "master", * or any other branch name. Test fixtures pass arbitrary `targetBranch`. * * @param taskId - The CLEO task ID (e.g. `"T1587"`). * @param projectRoot - Absolute path to the project root. * @param opts.targetBranch - Override the resolved default branch. * @param opts.taskTitle - Task title used in the merge commit message subject. * @param opts.skipFetch - Skip every `git fetch` (test fixtures). * @param opts.evidence - The task's evidence atoms; the prune keeps a worktree holding the only copy (T13696). * @param opts.fetchTimeoutMs - Timeout for the upstream fetch (default * {@link UPSTREAM_FETCH_TIMEOUT_MS}). * @param opts.disabledReason - Integration is switched off; nothing is touched (T14053). * @returns Merge integration result, with the decision it acted on in `plan`. * * @task T1587 * @task T14053 * @adr ADR-062 */ export declare function completeAgentWorktreeViaMerge(taskId: string, projectRoot: string, opts?: { targetBranch?: string; disabledReason?: string; taskTitle?: string; skipFetch?: boolean; fetchTimeoutMs?: number; evidence?: readonly EvidenceAtom[]; }): WorktreeMergeResult; /** * Result of a complete post-merge worktree integration. * * Extends `WorktreeMergeResult` with an additional audit log path field. * * @task T9043 * @adr ADR-062 */ export interface WorktreeIntegrationResult extends WorktreeMergeResult { /** Path to the audit log entry that was written (if any). */ auditLogEntry: string | null; } /** * Complete a worker task's worktree integration via merge, cleanup, and audit log. * * This is the orchestrator-facing convenience wrapper around * `completeAgentWorktreeViaMerge`. In addition to the merge+prune steps it: * * 1. Delegates all merge and cleanup to `completeAgentWorktreeViaMerge`. * 2. Appends a structured entry to `.cleo/audit/worktree-integration.jsonl` * recording the merge commit, task ID, and cleanup outcome. * * Orchestrators MUST call this (not `completeAgentWorktreeViaMerge` directly) * so every integration is auditable. * * @param taskId - The CLEO task ID (e.g. `"T1587"`). * @param projectRoot - Absolute path to the project root. * @param opts.targetBranch - Override the resolved default branch. * @param opts.taskTitle - Task title used in the merge commit message subject. * @param opts.skipFetch - Skip the `git fetch origin` step (test fixtures). * @param opts.auditLogPath - Override the audit JSONL path (testing). * @param opts.evidence - The task's evidence atoms; the prune keeps a worktree holding the only copy (T13696). * @returns Integration result including audit log path. * * @task T9043 * @adr ADR-062 */ export declare function completeAgentWorktreeIntegration(taskId: string, projectRoot: string, opts?: { targetBranch?: string; taskTitle?: string; skipFetch?: boolean; auditLogPath?: string; evidence?: readonly EvidenceAtom[]; }): WorktreeIntegrationResult; /** * The single integration result for a completion envelope (T14053): what the * run actually did, so it can never contradict the merge. `merge` only when a * merge commit was made; a decided merge that failed reads as `refuse`. * * @param result - The merge run's result (its `plan` is the decision it acted on). * @returns `{branch, worktreePath, targetBranch, action, reason, mergeCommit?, pushed: false}`. * * @example * ```ts * integrationOutcome(completeAgentWorktreeIntegration('T1', root)).action; // 'skip' * ``` * @task T14053 */ export declare function integrationOutcome(result: WorktreeMergeResult): WorktreeIntegrationOutcome; //# sourceMappingURL=branch-integration.d.ts.map