Add --auto-merge flag to merge PR/MR and close git issues #38
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implementation Plan — pi-loop: Auto-merge Flag
Step-by-step engineering plan to add the
--auto-mergeflag, poll-and-mergelogic, and issue-close behavior described in
docs/design-auto-merge.md.Audience: the implementer (human or AI coding agent). This plan is written to
be built from directly.
Guiding principles
PiLoopConfig,IssueCommentClient,MrClient,runMrStage,runPipeline,IssueRef).stage; non-fatal for close/write-back after the MR exists.
Milestone 1 — Configuration & CLI flag
Goal:
--auto-mergeandPILOOP_AUTO_MERGEare parsed and loaded.File changes
src/config/types/config.tsAutoMergeConfiginterface.autoMerge: AutoMergeConfigtoPiLoopConfigandMaskedConfig.src/config/services/configLoader.tsPILOOP_AUTO_MERGEas boolean-ish (1/true→ enabled).PILOOP_AUTO_MERGE_TIMEOUT_MSandPILOOP_AUTO_MERGE_POLL_MSaspositive integers with safe defaults.
maskConfigto show the three new values.src/cli/helpers/args.tsautoMerge: boolean | undefinedtoParsedArgs.--auto-merge(no value) inparseArgs.HELP_TEXT.src/cli/services/runCli.tsautoMerge.enabledas:parsed.autoMerge ?? env-based value.runPipeline.Unit tests
configLoader.test.ts: defaults, env overrides, invalid values fall back todefaults, masking.
args.test.ts:--auto-mergeparsing, unknown options still rejected, helptext includes the flag.
Exit condition
npm run lintandnpm testpass;npm start -- --helpshows--auto-merge;running with
PILOOP_AUTO_MERGE=1loadsconfig.autoMerge.enabled = true.Milestone 2 — Issue close clients
Goal: every issue client can optionally close an issue; Jira no-ops.
File changes
src/issue/types/issueCloseClient.ts(new file)IssueCloseClientinterface:src/issue/index.tsIssueCloseClient.src/forgejo/services/forgejoIssueClient.tscloseIssue(ref)usingPATCH /api/v1/repos/{owner}/{repo}/issues/{index}with body{ state: "closed" }.requestretry/auth plumbing.src/github/services/gitHubIssueClient.tscloseIssue(ref)usingPATCH /repos/{owner}/{repo}/issues/{number}with body
{ state: "closed" }.src/gitlab/services/gitLabIssueClient.tscloseIssue(ref)usingPUT /projects/{id}/issues/{iid}with body{ state_event: "close" }.src/jira/services/jiraRestClient.tscloseIssue(ref)that logs a debug note and returns (no-op).IssueCloseClientadapter.Unit tests
401/403→ auth error (fail-fast when called from preflight),404→ not-found,429/5xx→ retried.Exit condition
All issue clients pass unit tests;
npm run lintandnpm testpass.Milestone 3 — Merge polling abstraction
Goal: a platform-agnostic way to ask "is PR/MR #N mergeable yet?"
File changes
src/mr/types/mergePoller.ts(new file)src/mr/services/forgejoMergePoller.ts(new file)isMergeable:GET /api/v1/repos/{owner}/{repo}/pulls/{index}and checkmergeable === true.merge:POST /api/v1/repos/{owner}/{repo}/pulls/{index}/mergewithdefault strategy (empty body or
{ Do: "merge" }).src/mr/services/pollUntilMergeable.ts(new file)MrStageError('auto-merge-timeout')on timeout.src/mr/helpers/mrStageError.ts'auto-merge-timeout' | 'auto-merge-failed'toMrStageErrorKind.Unit tests
pollUntilMergeable.test.ts:pollMsandtimeoutMs(use fake timers).forgejoMergePoller.test.ts:isMergeabletrue/false/unknown parsing,mergesuccess,mergefailure mapping.Exit condition
Unit tests pass; poller can be injected into
runMrStagein the next milestone.Milestone 4 — Wire auto-merge into the MR stage
Goal: when enabled, create non-draft, poll, merge, and record the outcome.
File changes
src/mr/types/mr.tsMrResult:autoMerge: AutoMergeConfigtoRunMrStageInput.src/mr/services/runMrStage.tsautoMergefrom input.autoMerge.enabled:draft: falsetocreateMr(Forgejo) and to the agent prompt(GitHub/GitLab).
mrNumberfrom URL or platform metadata.pollUntilMergeablewith the platform poller.poller.merge().mrResult.autoMergewithmerged: trueandmergedAt.MrStageErrorwith the new kinds.logger.forStage('mr').src/mr/helpers/parseMrOutput.tsMR_NUMBERtrailer from agent output for GitHub/GitLab sopolling/merge can use it.
src/artifacts/services/mrResultArtifact.tsandsrc/artifacts/helpers/validateMrResult.tsautoMergeshape in the schema.Unit tests
runMrStage.test.ts:autoMerge.merged = true.auto-merge-timeout.auto-merge-failed.Exit condition
npm testpasses; Forgejo path can create, poll, and merge in unit tests.Milestone 5 — Orchestrator wiring & issue close
Goal:
runPipelinecloses the issue after a successful auto-merge andhandles close failures non-fatally.
File changes
src/orchestrator/services/runPipeline.tsdefaultMakeCommentClientto return an object that also implementsIssueCloseClientwhere supported (all providers now, with Jira no-op).runMr, ifautoMerge.enabled && mrResult.autoMerge?.merged:client.closeIssue(issueRef).autoMergeconfig intorunMr.src/orchestrator/types/orchestrator.tsautoMerge?: { merged: boolean; closed?: boolean; closeError?: string }to
RunPipelineResultsuccess branch.Unit tests
runPipeline.test.ts:closeErrorin result.Exit condition
Pipeline unit tests pass; close behavior is exercised with mocked clients.
Milestone 6 — Outcome-aware write-back
Goal: the comment posted on the source issue reflects whether merge happened.
File changes
src/writeback/helpers/formatWritebackSummary.tsoutcome: 'draft' | 'merged' | 'merge-failed'.src/writeback/services/runWriteback.tsoutcomeinRunWritebackInput.src/writeback/types/writeback.tsoutcome?: 'draft' | 'merged' | 'merge-failed'toRunWritebackInput.src/orchestrator/services/runPipeline.tsautoMergestate and pass it torunWriteback.Unit tests
formatWritebackSummary.test.ts: all three message variants.runWriteback.test.ts: passes outcome through to the comment body.Exit condition
Write-back tests pass; message selection is deterministic.
Milestone 7 — Schema, artifacts, and final summary
Goal: artifacts and final summary include auto-merge state.
File changes
src/artifacts/helpers/validateMrResult.tsautoMergeobject.src/orchestrator/helpers/buildFinalSummary.tssrc/mr/helpers/buildMrDescription.tsUnit tests
validateMrResult.test.ts: valid with/withoutautoMerge; rejects malformed.buildFinalSummary.test.ts: includes merge + close lines when applicable.Milestone 8 — GitHub/GitLab merge polling
Goal: bring auto-merge to parity for the agent-driven platforms.
File changes
src/mr/services/githubMergePoller.ts(new file)gh pr view <number> --json mergeStateStatus,mergeableforisMergeable.gh pr merge <number> --mergeformerge.src/mr/services/gitlabMergePoller.ts(new file)glab mr view <iid>parsing or REST forisMergeable.glab mr merge <iid>formerge.src/mr/services/makeMergePoller.ts(new file)src/mr/services/runMrStage.tsUnit tests
Milestone 9 — Gated E2E for Forgejo
Goal: exercise a real merge + close end-to-end.
File changes
src/orchestrator/services/runPipeline.e2e.test.tsPILOOP_E2E=1:pi-loop <issue-ref> --auto-merge.ok === true.mrResult.autoMerge?.merged === true.AGENTS.md/ README--auto-merge, env vars, timeout/poll tuning, and Jira no-op.Exit condition
PILOOP_E2E=1 npm run e2epasses for the new Forgejo auto-merge case.Suggested sequencing
Milestones are strictly sequential because each builds on the previous seam.
M8 can be deferred if Forgejo-only auto-merge is sufficient for the first
usable slice.
Dependencies / prerequisites checklist
blocking merge.
FORGEJO_TOKENwith permission to create PRs, merge PRs, and closeissues.
Definition of done
--auto-mergeflag andPILOOP_AUTO_MERGEenv var work end-to-end onForgejo.
npm run lintandnpm testpass.