"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveRenderedTitleIfChanged = exports.resolveIssueTitleProgress = exports.resolveCommentNextForWrite = exports.buildWorkflowStateForCanonicalComment = exports.buildSameIssueReplanIntent = void 0;
exports.resolveSameIssueReplanPublicationResult = resolveSameIssueReplanPublicationResult;
exports.collectRoutingFacts = collectRoutingFacts;
exports.collectExistingPullRequestResolution = collectExistingPullRequestResolution;
exports.executeEffects = executeEffects;
exports.buildEventHookRunSummary = buildEventHookRunSummary;
// biome-ignore lint/nursery/noExcessiveLinesPerFile: effect execution stays centralized so publish side effects remain auditable.
const canonical_workflow_state_1 = require("../../comments/canonical-workflow-state");
const workflow_update_1 = require("../../comments/workflow-update");
const control_plane_config_1 = require("../../config/control-plane-config");
const issue_completion_1 = require("../../config/issue-completion");
const catalog_1 = require("../../i18n/catalog");
const types_1 = require("../../platform/ticket-tracker/types");
const types_2 = require("../../platform/workflow-state/types");
const constants_1 = require("../../worker/constants");
const publisher_events_1 = require("../../workflows/publisher-events");
const auto_merge_state_1 = require("../../workflows/auto-merge-state");
const pre_plan_state_1 = require("../../workflows/pre-plan-state");
const types_3 = require("../../workflows/types");
const workflow_timeline_scan_1 = require("../../workflows/workflow-timeline-scan");
const issue_logger_1 = require("../issue-logger");
const log_text_1 = require("../log-text");
const effect_log_1 = require("./effect-log");
const effects_pr_handoff_support_1 = require("./effects-pr-handoff-support");
const effects_state_support_1 = require("./effects-state-support");
const effects_support_1 = require("./effects-support");
const pr_publication_1 = require("./pr-publication");
const needs_human_merge_state_1 = require("./needs-human-merge-state");
const presentation_1 = require("./presentation");
const publisher_factory_1 = require("./publisher-factory");
const results_1 = require("./results");
const shared_1 = require("./shared");
const run_limit_policy_1 = require("./run-limit-policy");
var effects_state_support_2 = require("./effects-state-support");
Object.defineProperty(exports, "buildSameIssueReplanIntent", { enumerable: true, get: function () { return effects_state_support_2.buildSameIssueReplanIntent; } });
Object.defineProperty(exports, "buildWorkflowStateForCanonicalComment", { enumerable: true, get: function () { return effects_state_support_2.buildWorkflowStateForCanonicalComment; } });
Object.defineProperty(exports, "resolveCommentNextForWrite", { enumerable: true, get: function () { return effects_state_support_2.resolveCommentNextForWrite; } });
Object.defineProperty(exports, "resolveIssueTitleProgress", { enumerable: true, get: function () { return effects_state_support_2.resolveIssueTitleProgress; } });
Object.defineProperty(exports, "resolveRenderedTitleIfChanged", { enumerable: true, get: function () { return effects_state_support_2.resolveRenderedTitleIfChanged; } });
function resolveSameIssueReplanPublicationResult(input) {
    return {
        commentUrl: input.publication.publishStatus === "applied"
            ? (0, publisher_factory_1.resolveRunWorkflowPublicationCommentUrl)(input.publication)
            : null,
        issueWorkflowState: input.publication.targetWorkflowProjection !== null
            ? { ...input.publication.targetWorkflowProjection }
            : input.publication.publishStatus === "applied"
                ? { ...input.nextSnapshot }
                : { ...input.currentIssueWorkflowState },
    };
}
async function collectRoutingFacts(context, workspacePath, branchName, baseBranch) {
    const [branchAheadOfBase, pullRequest] = await Promise.all([
        context.gitRepository.isBranchAheadOfBase(workspacePath, baseBranch),
        context.repositoryHost.findPullRequestByBranch(branchName),
    ]);
    const matchingPullRequest = (0, results_1.filterPullRequestByBase)(pullRequest, baseBranch);
    return (0, results_1.buildRoutingResolution)(branchAheadOfBase, matchingPullRequest, await (0, results_1.resolvePullRequestThreadRecord)(context.ticketTracker, matchingPullRequest));
}
async function collectExistingPullRequestResolution(ticketTracker, repositoryHost, branchName, baseBranch) {
    const pullRequest = await repositoryHost.findPullRequestByBranch(branchName);
    const matchingPullRequest = (0, results_1.filterPullRequestByBase)(pullRequest, baseBranch);
    return (0, results_1.buildRoutingResolution)(false, matchingPullRequest, await (0, results_1.resolvePullRequestThreadRecord)(ticketTracker, matchingPullRequest));
}
async function executeEffects(repoRoot, config, currentUserLogin, repositoryHost, ticketTracker, issueId, state, logger, workflowLabels, initialResult = (0, results_1.createEffectExecutionResult)(state.pullRequestUrl, null, state.pullRequestThreadId), publicationOrigin = "worker_publish") {
    const plannedEffects = resolvePlannedEffects(state, currentUserLogin, config);
    const result = { ...initialResult };
    const context = {
        repoRoot,
        config,
        currentUserLogin,
        repositoryHost,
        ticketTracker,
        issueId,
        state,
        logger,
        workflowLabels,
        result,
        publicationOrigin,
    };
    await logger.appendSection((0, log_text_1.issueLogSection)("plannedEffects"), (0, issue_logger_1.formatList)(plannedEffects));
    for (const effect of shared_1.EFFECT_EXECUTION_ORDER) {
        if (!plannedEffects.includes(effect)) {
            continue;
        }
        try {
            await executeEffect(effect, context);
        }
        catch (error) {
            await (0, effect_log_1.appendEffectFailureLog)(logger, [
                (0, log_text_1.issueLogField)("effect", effect),
                (0, results_1.renderFailureDetails)(error, logger.locale),
            ]);
            throw new shared_1.EffectExecutionError((0, results_1.buildFailureSummary)(error), result, error, resolvePendingWaitHumanPublicationRecovery(effect, context));
        }
    }
    await logger.appendSection((0, log_text_1.issueLogSection)("finalLabelsCloseResult"), (0, presentation_1.renderFinalWorkflowStateSection)(state.nextSnapshot, (0, results_1.shouldCloseTerminalWorkflowSuccess)(state), workflowLabels, logger.locale));
    return result;
}
function resolvePendingWaitHumanPublicationRecovery(effect, context) {
    if (effect !== "write_comment" ||
        context.state.finalOutcome !== constants_1.WORKER_OUTCOMES.waitHuman ||
        context.state.nextSnapshot.status !== types_2.WORKFLOW_STATUSES.aiNeedsHuman ||
        context.pendingWaitHumanPublicationPublishRequest === undefined) {
        return undefined;
    }
    return {
        kind: shared_1.PENDING_WAIT_HUMAN_PUBLICATION_KIND,
        transitionState: context.state,
        publishRequest: context.pendingWaitHumanPublicationPublishRequest,
    };
}
function resolvePlannedEffects(state, currentUserLogin, config) {
    const plannedEffects = [];
    const pausedForIssueCompletionReview = isPausedIssueCompletionReviewTransition(state, config);
    if (state.issue.kind === types_1.TRACKER_ITEM_KINDS.issue &&
        !pausedForIssueCompletionReview &&
        state.facts.branchAheadOfBase &&
        !state.facts.openPullRequestExists) {
        plannedEffects.push("ensure_pr");
    }
    if (currentUserLogin?.trim() &&
        !pausedForIssueCompletionReview &&
        (state.facts.openPullRequestExists ||
            (state.facts.branchAheadOfBase && !state.facts.openPullRequestExists))) {
        plannedEffects.push("sync_pr_assignees");
    }
    if (isPullRequestReviewReadyTransition(state, config) ||
        isPullRequestHandoffTransition(state)) {
        plannedEffects.push("mark_pr_ready");
        plannedEffects.push("sync_pr_reviewers");
    }
    plannedEffects.push("write_comment");
    return plannedEffects;
}
function isPullRequestHandoffTransition(state) {
    return (state.issue.kind === types_1.TRACKER_ITEM_KINDS.issue &&
        state.nextSnapshot.status === types_2.WORKFLOW_STATUSES.aiWorkingInPr);
}
function isPullRequestReviewReadyTransition(state, config) {
    if (state.issue.kind === types_1.TRACKER_ITEM_KINDS.issue &&
        state.finalOutcome === constants_1.WORKER_OUTCOMES.waitHuman &&
        state.needsHumanReason === "review_ready") {
        const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(config, state.workflowState);
        return effectiveConfig.issueCompletion.target !== "pause_on_issue";
    }
    return (state.issue.kind === types_1.TRACKER_ITEM_KINDS.pullRequest &&
        state.finalOutcome === constants_1.WORKER_OUTCOMES.waitHuman &&
        state.needsHumanReason === "review_ready");
}
function isPausedIssueCompletionReviewTransition(state, config) {
    if (state.issue.kind !== types_1.TRACKER_ITEM_KINDS.issue ||
        state.finalOutcome !== constants_1.WORKER_OUTCOMES.waitHuman ||
        state.needsHumanReason !== publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.reviewReady) {
        return false;
    }
    const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(config, state.workflowState);
    return effectiveConfig.issueCompletion.target === "pause_on_issue";
}
function appendPublicationRecord(context, publication) {
    context.result.publications.push({
        ...publication,
        origin: context.publicationOrigin,
        run: buildEventHookRunSummary(context.state),
    });
}
function buildEventHookRunSummary(state) {
    const nextStep = state.finalOutcome === constants_1.WORKER_OUTCOMES.waitHuman &&
        state.needsHumanReason === publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.question
        ? state.presentation.commentNext
        : state.presentation.cliNext;
    return {
        runMode: state.runMode,
        workerOutcome: state.finalOutcome,
        needsHumanReason: state.needsHumanReason,
        summary: state.summary,
        nextStep,
        workflowTemplateName: state.workflowTemplateName,
    };
}
async function executeEffect(effect, context) {
    switch (effect) {
        case "ensure_pr":
            await executeEnsurePullRequestEffect(context);
            return;
        case "sync_pr_assignees":
            await executeSyncPullRequestAssigneesEffect(context);
            return;
        case "write_comment":
            await executeWriteCommentEffect(context);
            return;
        case "mark_pr_ready":
            await executeMarkPullRequestReadyEffect(context);
            return;
        case "sync_pr_reviewers":
            await executeSyncPullRequestReviewersEffect(context);
            return;
    }
}
async function executeEnsurePullRequestEffect(context) {
    const pullRequestResult = await (0, results_1.ensurePullRequest)(context.repositoryHost, context.state.issue, context.state.branchName, context.state.baseBranch);
    const pullRequestThreadRecord = await context.ticketTracker.getPullRequestThreadRecord({
        pullRequestNumber: pullRequestResult.number,
        pullRequestUrl: pullRequestResult.url,
    });
    if (pullRequestThreadRecord === null) {
        throw new Error("Unable to resolve pull request tracker thread.");
    }
    context.state.pullRequestThreadId = pullRequestThreadRecord.id;
    context.state.pullRequestUrl = pullRequestResult.url;
    context.state.facts.openPullRequestExists = true;
    context.state.facts.pullRequestThreadId = pullRequestThreadRecord.id;
    context.result.pullRequestUrl = pullRequestResult.url;
    context.result.pullRequestThreadId = pullRequestThreadRecord.id;
    await (0, effect_log_1.appendEffectResultLog)(context.logger, [
        (0, log_text_1.issueLogField)("effect", "ensure_pr"),
        (0, log_text_1.issueLogField)("pr", pullRequestResult.url),
        (0, log_text_1.issueLogField)("source", pullRequestResult.source),
    ]);
}
async function executeSyncPullRequestAssigneesEffect(context) {
    const pullRequest = await findOpenPullRequestByBranch(context.repositoryHost, context.state.branchName, context.state.baseBranch);
    const assignee = context.currentUserLogin?.trim() ?? null;
    if (pullRequest && assignee) {
        await context.repositoryHost.syncPullRequestAssignees(pullRequest.number, [
            assignee,
        ]);
    }
    await (0, effect_log_1.appendEffectResultLog)(context.logger, [
        (0, log_text_1.issueLogField)("effect", "sync_pr_assignees"),
        (0, log_text_1.issueLogField)("note", pullRequest && assignee ? "synced" : "no_op"),
    ]);
}
async function executeWriteCommentEffect(context) {
    if ((0, effects_state_support_1.shouldPublishSameIssueReplanThroughPublisher)(context.state)) {
        const publication = await (0, effects_state_support_1.publishSameIssueReplanTransition)(context);
        appendPublicationRecord(context, {
            request: publication.request,
            result: publication.result,
        });
        const sameIssueReplanResult = resolveSameIssueReplanPublicationResult({
            publication: publication.result,
            currentIssueWorkflowState: context.state.issue.workflowState,
            nextSnapshot: context.state.nextSnapshot,
        });
        context.result.commentUrl = sameIssueReplanResult.commentUrl;
        context.state.issue.workflowState =
            sameIssueReplanResult.issueWorkflowState;
        await (0, effect_log_1.appendEffectResultLog)(context.logger, [
            (0, log_text_1.issueLogField)("effect", "publish_same_issue_replan"),
            (0, log_text_1.issueLogField)("note", `publishStatus=${publication.result.publishStatus}`),
            (0, log_text_1.issueLogField)("commentUrl", publication.result.publishStatus === "guard_rejected"
                ? "(guard_rejected)"
                : (context.result.commentUrl ??
                    (publication.result.visibleUpdateComment === null
                        ? "(skipped)"
                        : "-"))),
        ]);
        return;
    }
    const publication = shouldSyncHandoffPullRequestLabels(context.state)
        ? await publishPullRequestHandoffTransition(context)
        : await publishStandardTransition(context);
    const sourcePublication = "sourceIssueResult" in publication
        ? publication.sourceIssueResult
        : publication.result;
    if ("sourceIssueResult" in publication) {
        appendPublicationRecord(context, {
            request: publication.sourceIssueRequest,
            result: publication.sourceIssueResult,
        });
        if (publication.pullRequestThreadRequest &&
            publication.pullRequestThreadResult) {
            appendPublicationRecord(context, {
                request: publication.pullRequestThreadRequest,
                result: publication.pullRequestThreadResult,
            });
        }
    }
    else {
        appendPublicationRecord(context, {
            request: publication.request,
            result: publication.result,
        });
    }
    context.result.commentUrl =
        (0, publisher_factory_1.resolveRunWorkflowPublicationCommentUrl)(sourcePublication);
    context.state.issue.workflowState = sourcePublication.targetWorkflowProjection
        ? { ...sourcePublication.targetWorkflowProjection }
        : { ...context.state.nextSnapshot };
    await (0, effect_log_1.appendEffectResultLog)(context.logger, [
        (0, log_text_1.issueLogField)("effect", "write_comment"),
        (0, log_text_1.issueLogField)("commentUrl", context.result.commentUrl ??
            (sourcePublication.visibleUpdateComment === null ? "(skipped)" : "-")),
    ]);
}
async function executeMarkPullRequestReadyEffect(context) {
    const pullRequest = await findOpenPullRequestByBranch(context.repositoryHost, context.state.branchName, context.state.baseBranch);
    if (pullRequest?.isDraft) {
        await context.repositoryHost.markPullRequestReadyForReview(pullRequest.number);
    }
    await (0, effect_log_1.appendEffectResultLog)(context.logger, [
        (0, log_text_1.issueLogField)("effect", "mark_pr_ready"),
        (0, log_text_1.issueLogField)("note", pullRequest?.isDraft ? "marked_ready" : "no_op"),
    ]);
}
async function executeSyncPullRequestReviewersEffect(context) {
    const pullRequest = await findOpenPullRequestByBranch(context.repositoryHost, context.state.branchName, context.state.baseBranch);
    if (pullRequest) {
        await context.repositoryHost.syncPullRequestReviewers(pullRequest.number, context.config.repositoryHost.pullRequest?.defaultReviewers ?? []);
    }
    await (0, effect_log_1.appendEffectResultLog)(context.logger, [
        (0, log_text_1.issueLogField)("effect", "sync_pr_reviewers"),
        (0, log_text_1.issueLogField)("note", pullRequest ? "synced" : "no_op"),
    ]);
}
function shouldSyncHandoffPullRequestLabels(state) {
    return (state.issue.kind === types_1.TRACKER_ITEM_KINDS.issue &&
        state.nextSnapshot.status === types_2.WORKFLOW_STATUSES.aiWorkingInPr &&
        state.handoffPullRequestSnapshot !== null &&
        state.pullRequestThreadId !== null);
}
async function findOpenPullRequestByBranch(repositoryHost, branchName, baseBranch) {
    return (0, results_1.filterPullRequestForBase)(await repositoryHost.findPullRequestByBranch(branchName), baseBranch);
}
async function publishStandardTransition(context) {
    const comments = await (0, shared_1.loadSortedIssueComments)(context.ticketTracker, context.issueId, {
        timeline: "issue-only",
    });
    const workflowState = context.state.workflowState === null
        ? null
        : (0, effects_state_support_1.buildWorkflowStateForCanonicalComment)(context.state, comments);
    const request = await (0, effects_support_1.buildStandardTransitionPublishRequest)({
        context,
        comments,
        workflowState,
    });
    if (shouldCapturePendingWaitHumanPublicationRequest(context.state)) {
        context.pendingWaitHumanPublicationPublishRequest =
            (0, effects_support_1.buildPendingWaitHumanPublicationPublishRequest)({
                request,
                comments,
                state: context.state,
            });
    }
    const publisher = (0, publisher_factory_1.createRunWorkflowPublisher)(context);
    const result = await publisher.publishWorkflow(request);
    if (result.targetClosed) {
        context.state.issue.isOpen = false;
    }
    await (0, effects_support_1.maybePublishLinkedIssueTitleForPullRequest)({
        context,
        publisher,
        workflowState,
    });
    return { request, result };
}
function shouldCapturePendingWaitHumanPublicationRequest(state) {
    return (state.finalOutcome === constants_1.WORKER_OUTCOMES.waitHuman &&
        state.nextSnapshot.status === types_2.WORKFLOW_STATUSES.aiNeedsHuman);
}
async function publishPullRequestHandoffTransition(context) {
    const sourceComments = await (0, shared_1.loadSortedIssueComments)(context.ticketTracker, context.issueId, {
        timeline: "issue-only",
    });
    const plan = await resolvePullRequestHandoffPublicationPlan({
        context,
        sourceComments,
    });
    const publisher = (0, publisher_factory_1.createRunWorkflowPublisher)(context);
    return publishPullRequestHandoffPlan({ publisher, plan });
}
async function resolvePullRequestHandoffPublicationPlan(input) {
    const { sourceScannedInputCursor, sourceWorkflowState: sourceBaseWorkflowState, } = resolvePullRequestHandoffSourcePublicationBaseState({
        context: input.context,
        sourceComments: input.sourceComments,
    });
    const pullRequestPublicationState = await resolvePullRequestHandoffPullRequestPublicationState({
        context: input.context,
        sourceWorkflowState: sourceBaseWorkflowState,
    });
    const sourceWorkflowState = sourceBaseWorkflowState;
    const sourceIssueTitle = (0, effects_state_support_1.resolveRenderedTitleIfChanged)({
        issue: input.context.state.issue,
        workflowState: sourceWorkflowState,
        locale: input.context.state.responseLanguage,
        suppressDoneLabel: true,
        surface: "issue",
        enabled: input.context.config.ticketTracker.showStepProgressInTitles === true,
    });
    (0, effects_state_support_1.applyPullRequestHandoffSourcePresentation)({
        state: input.context.state,
        mode: pullRequestPublicationState.visibleUpdateMode,
    });
    if (pullRequestPublicationState.preserveRunningPullRequestState) {
        (0, effects_state_support_1.applyRunningPullRequestHandoffPresentation)(input.context.state);
    }
    const sourceVisibleUpdate = await (0, effects_support_1.buildStandardTransitionVisibleUpdate)({
        context: input.context,
        workflowState: sourceWorkflowState,
        eventOverride: publisher_events_1.WORKFLOW_VISIBLE_EVENTS.prHandoff,
        omitNeedsHumanReason: true,
    });
    const plan = (0, pr_publication_1.buildPullRequestHandoffPublicationPlan)({
        workflowLabels: input.context.workflowLabels,
        responseLanguage: input.context.state.responseLanguage,
        sourceIssue: input.context.state.issue,
        sourceIssueWorkflowState: sourceWorkflowState,
        sourceProjection: {
            previous: input.context.state.previousSnapshot,
            next: input.context.state.nextSnapshot,
            previousNeedsHumanReason: (0, workflow_update_1.findLatestNeedsHumanReason)(input.sourceComments),
            nextNeedsHumanReason: null,
            previousWorkflowState: pullRequestPublicationState.hasExistingCanonicalState
                ? null
                : input.context.state.workflowState,
            nextWorkflowState: sourceWorkflowState,
            ...(input.context.state.labelProjection !== undefined
                ? { labelProjection: input.context.state.labelProjection }
                : {}),
        },
        sourceVisibleUpdate: addSourceRunLimitNoticeForPullRequestHandoff({
            visibleUpdate: sourceVisibleUpdate,
            context: input.context,
            sourceWorkflowState,
            pullRequestIssue: pullRequestPublicationState.pullRequestIssue,
            pullRequestNeedsHumanReason: pullRequestPublicationState.nextNeedsHumanReason,
        }),
        sourceScannedInputCursor,
        ...(sourceIssueTitle !== null
            ? { sourceTrackerTitle: sourceIssueTitle }
            : {}),
        pullRequestIssue: pullRequestPublicationState.pullRequestIssue,
        pullRequestWorkflowState: pullRequestPublicationState.pullRequestWorkflowState,
        pullRequestVisibleWorkflowState: pullRequestPublicationState.pullRequestVisibleWorkflowState,
        pullRequestProjection: {
            previous: pullRequestPublicationState.pullRequestIssue.workflowState,
            next: pullRequestPublicationState.pullRequestNextSnapshot,
            previousNeedsHumanReason: pullRequestPublicationState.previousNeedsHumanReason,
            nextNeedsHumanReason: pullRequestPublicationState.nextNeedsHumanReason,
            previousWorkflowState: pullRequestPublicationState.existingPullRequestWorkflowState,
            nextWorkflowState: pullRequestPublicationState.pullRequestWorkflowState,
            ...(pullRequestPublicationState.pullRequestCurrentLabelNames !== undefined
                ? {
                    currentLabelNames: pullRequestPublicationState.pullRequestCurrentLabelNames,
                }
                : {}),
        },
        pullRequestVisibleUpdate: pullRequestPublicationState.pullRequestVisibleUpdate,
        ...(pullRequestPublicationState.pullRequestTitle !== null
            ? {
                pullRequestTrackerTitle: pullRequestPublicationState.pullRequestTitle,
            }
            : {}),
    });
    if (plan.kind !== "pr_handoff") {
        throw new Error("Expected a PR handoff publication plan.");
    }
    return plan;
}
async function publishPullRequestHandoffPlan(input) {
    const sourceIssueResult = await input.publisher.publishWorkflow(input.plan.sourceIssueRequest);
    const pullRequestThreadResult = input.plan.pullRequestThreadRequest === null
        ? null
        : await input.publisher.publishWorkflow(input.plan.pullRequestThreadRequest);
    return {
        sourceIssueRequest: input.plan.sourceIssueRequest,
        sourceIssueResult,
        pullRequestThreadRequest: input.plan.pullRequestThreadRequest,
        pullRequestThreadResult,
    };
}
function addSourceRunLimitNoticeForPullRequestHandoff(input) {
    if (input.visibleUpdate === null ||
        input.visibleUpdate.kind !== "workflow_update" ||
        input.pullRequestNeedsHumanReason !== publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit) {
        return input.visibleUpdate;
    }
    const pullRequestUrl = input.pullRequestIssue.url;
    const locale = input.context.state.responseLanguage;
    const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, input.sourceWorkflowState);
    const currentRuns = input.sourceWorkflowState?.consecutiveAiRuns ?? 0;
    const currentHours = input.sourceWorkflowState?.totalAiRunHours ?? 0;
    const runLimit = (0, run_limit_policy_1.resolveAiRunLimitPolicy)({
        config: effectiveConfig,
        consecutiveAiRuns: currentRuns,
        totalAiRunHours: currentHours,
    });
    const maxHours = effectiveConfig.aiRunLimits.maxHours ?? "none";
    return {
        ...input.visibleUpdate,
        summary: [
            input.visibleUpdate.summary,
            (0, catalog_1.translate)(locale, "run.presentation.prHandoff.sourceRunLimitSummary", {
                pullRequestUrl,
                runLimitSummary: (0, presentation_1.buildAiRunLimitSummary)(locale, {
                    reachedKind: runLimit.reachedKind,
                    consecutiveAiRuns: currentRuns,
                    totalAiRunHours: currentHours,
                    maxRuns: effectiveConfig.aiRunLimits.maxRuns,
                    maxHours: effectiveConfig.aiRunLimits.maxHours,
                }),
            }),
        ].join("\n\n"),
        next: (0, catalog_1.translate)(locale, "run.presentation.prHandoff.sourceRunLimitNext"),
        dedupeKey: [
            "pr-handoff-run-limit",
            pullRequestUrl,
            input.context.state.handoffPullRequestNeedsHumanReason ?? "run_limit",
            `runs:${currentRuns}/${effectiveConfig.aiRunLimits.maxRuns}`,
            `hours:${currentHours}/${maxHours}`,
        ].join(":"),
    };
}
async function resolvePullRequestHandoffPullRequestPublicationState(input) {
    const { issue: pullRequestIssue, allowInitialTitleSync, currentLabelNames, } = await (0, effects_pr_handoff_support_1.loadPullRequestIssueForHandoff)(input.context);
    const pullRequestComments = await (0, shared_1.loadSortedIssueComments)(input.context.ticketTracker, pullRequestIssue.id, {
        timeline: "issue-only",
    });
    const pullRequestCanonicalResolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(pullRequestComments, input.context.state.responseLanguage);
    if (pullRequestCanonicalResolution.kind === "invalid") {
        throw new Error(pullRequestCanonicalResolution.summary);
    }
    const existingPullRequestWorkflowState = pullRequestCanonicalResolution.kind === "found"
        ? pullRequestCanonicalResolution.workflowState
        : null;
    const pullRequestConfigWorkflowState = existingPullRequestWorkflowState ?? input.sourceWorkflowState;
    const pullRequestEffectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, pullRequestConfigWorkflowState);
    const preserveRunningPullRequestState = pullRequestIssue.workflowState.status === types_2.WORKFLOW_STATUSES.aiRunning;
    const pullRequestNextSnapshot = preserveRunningPullRequestState
        ? pullRequestIssue.workflowState
        : resolvePullRequestHandoffNextSnapshot({
            baseSnapshot: input.context.state.handoffPullRequestSnapshot,
            effectiveConfig: pullRequestEffectiveConfig,
            handoffNeedsHumanReason: input.context.state.handoffPullRequestNeedsHumanReason,
        });
    const previousNeedsHumanReason = (0, workflow_update_1.findLatestNeedsHumanReason)(pullRequestComments);
    const nextNeedsHumanReason = preserveRunningPullRequestState
        ? previousNeedsHumanReason
        : pullRequestNextSnapshot.status === types_2.WORKFLOW_STATUSES.aiNeedsHuman
            ? resolvePullRequestHandoffNeedsHumanReason(input.context.state.handoffPullRequestNeedsHumanReason)
            : previousNeedsHumanReason;
    const pullRequestWorkflowState = buildInitialPullRequestWorkflowState({
        effectiveConfig: pullRequestEffectiveConfig,
        sourceWorkflowState: input.sourceWorkflowState,
        settingsWorkflowState: pullRequestConfigWorkflowState,
        existingPullRequestWorkflowState,
        pullRequestNextSnapshot,
        handoffNeedsHumanReason: input.context.state.handoffPullRequestNeedsHumanReason,
        preserveRunningPullRequestState,
        pullRequestComments,
    });
    const initialPullRequestAutoMergeState = pullRequestWorkflowState?.autoMergeState;
    const visibleUpdateMode = preserveRunningPullRequestState
        ? "continue_existing"
        : initialPullRequestAutoMergeState !== undefined &&
            initialPullRequestAutoMergeState !== null
            ? "auto_merge"
            : pullRequestNextSnapshot.status === types_2.WORKFLOW_STATUSES.aiNeedsHuman
                ? "wait_human"
                : "proceed";
    const pullRequestVisibleWorkflowState = pullRequestWorkflowState ?? existingPullRequestWorkflowState;
    return {
        hasExistingCanonicalState: pullRequestCanonicalResolution.kind === "found",
        pullRequestIssue,
        pullRequestNextSnapshot,
        preserveRunningPullRequestState,
        existingPullRequestWorkflowState,
        pullRequestWorkflowState,
        pullRequestVisibleWorkflowState,
        pullRequestVisibleUpdate: await (0, effects_support_1.buildPullRequestHandoffVisibleUpdate)({
            context: input.context,
            sourceWorkflowState: input.sourceWorkflowState,
            mode: visibleUpdateMode,
            needsHumanReason: nextNeedsHumanReason,
        }),
        pullRequestTitle: allowInitialTitleSync === false
            ? null
            : (0, effects_state_support_1.resolveRenderedTitleIfChanged)({
                issue: pullRequestIssue,
                workflowState: pullRequestVisibleWorkflowState,
                locale: input.context.state.responseLanguage,
                surface: "pull_request",
                enabled: input.context.config.ticketTracker.showStepProgressInTitles ===
                    true,
            }),
        pullRequestCurrentLabelNames: currentLabelNames,
        visibleUpdateMode,
        previousNeedsHumanReason,
        nextNeedsHumanReason,
    };
}
function resolvePullRequestHandoffNextSnapshot(input) {
    if (input.baseSnapshot.status !== types_2.WORKFLOW_STATUSES.aiReady &&
        input.baseSnapshot.status !== types_2.WORKFLOW_STATUSES.aiNeedsHuman) {
        return input.baseSnapshot;
    }
    if (input.handoffNeedsHumanReason === publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit) {
        return input.baseSnapshot;
    }
    return {
        status: (0, issue_completion_1.shouldSkipPullRequestReview)(input.effectiveConfig.issueCompletion) &&
            !(0, issue_completion_1.isPullRequestAutoMergePolicyEnabled)(input.effectiveConfig.issueCompletion)
            ? types_2.WORKFLOW_STATUSES.aiNeedsHuman
            : types_2.WORKFLOW_STATUSES.aiReady,
        rules: [...input.baseSnapshot.rules],
    };
}
function resolvePullRequestHandoffNeedsHumanReason(handoffNeedsHumanReason) {
    return handoffNeedsHumanReason === publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit
        ? handoffNeedsHumanReason
        : publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.reviewReady;
}
function buildInitialPullRequestWorkflowState(input) {
    if (input.preserveRunningPullRequestState ||
        input.handoffNeedsHumanReason === publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit ||
        input.sourceWorkflowState === null ||
        input.settingsWorkflowState === null ||
        (input.pullRequestNextSnapshot.status !== types_2.WORKFLOW_STATUSES.aiReady &&
            input.pullRequestNextSnapshot.status !== types_2.WORKFLOW_STATUSES.aiNeedsHuman)) {
        return null;
    }
    const workflowState = (0, pre_plan_state_1.buildSelectionTransitionWorkflowState)({
        previousState: null,
        workflowSurface: "pull_request",
        workflowTemplates: input.settingsWorkflowState.workflowTemplates,
        baseBranch: input.settingsWorkflowState.baseBranch,
        schedule: (0, types_3.createOffWorkflowScheduleState)(),
        configOverrides: input.settingsWorkflowState.configOverrides,
        consecutiveAiRuns: input.sourceWorkflowState.consecutiveAiRuns,
        totalAiRunHours: input.sourceWorkflowState.totalAiRunHours,
        lastScannedCommentCursor: input.existingPullRequestWorkflowState?.lastScannedCommentCursor ??
            resolveLatestCommentCursor(input.pullRequestComments),
    });
    const shouldStartAutoMergeImmediately = (0, issue_completion_1.shouldSkipPullRequestReview)(input.effectiveConfig.issueCompletion) &&
        (0, issue_completion_1.isPullRequestAutoMergePolicyEnabled)(input.effectiveConfig.issueCompletion) &&
        input.pullRequestNextSnapshot.status === types_2.WORKFLOW_STATUSES.aiReady;
    return shouldStartAutoMergeImmediately
        ? (0, needs_human_merge_state_1.resolveAutoMergeWorkflowState)({
            workflowState,
            autoMergeState: auto_merge_state_1.WORKFLOW_AUTO_MERGE_STATES.pending,
        })
        : workflowState;
}
function resolveLatestCommentCursor(comments) {
    const latestComment = comments[comments.length - 1];
    return latestComment === undefined
        ? null
        : (0, workflow_timeline_scan_1.toWorkflowCommentCursor)(latestComment);
}
function resolvePullRequestHandoffSourcePublicationBaseState(input) {
    return {
        sourceScannedInputCursor: input.context.state.scannedInputCursor,
        sourceWorkflowState: input.context.state.workflowState === null
            ? null
            : (0, effects_state_support_1.buildPullRequestHandoffSourceWorkflowStateBase)({
                state: input.context.state,
                comments: input.sourceComments,
            }),
    };
}
