Add --auto-merge flag to merge PR/MR and close git issues #38

Closed
opened 2026-07-28 12:54:07 +00:00 by david · 0 comments
Owner

Implementation Plan — pi-loop: Auto-merge Flag

Step-by-step engineering plan to add the --auto-merge flag, poll-and-merge
logic, 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

  • Keep all non-LLM logic deterministic and unit-tested.
  • Re-use existing seams (PiLoopConfig, IssueCommentClient, MrClient,
    runMrStage, runPipeline, IssueRef).
  • Follow pi-loop's existing error-handling policy: fail-fast before the MR
    stage; non-fatal for close/write-back after the MR exists.
  • Update schemas and tests together.

Milestone 1 — Configuration & CLI flag

Goal: --auto-merge and PILOOP_AUTO_MERGE are parsed and loaded.

File changes

  1. src/config/types/config.ts

    • Add AutoMergeConfig interface.
    • Add autoMerge: AutoMergeConfig to PiLoopConfig and MaskedConfig.
  2. src/config/services/configLoader.ts

    • Parse PILOOP_AUTO_MERGE as boolean-ish (1/true → enabled).
    • Parse PILOOP_AUTO_MERGE_TIMEOUT_MS and PILOOP_AUTO_MERGE_POLL_MS as
      positive integers with safe defaults.
    • Update maskConfig to show the three new values.
  3. src/cli/helpers/args.ts

    • Add autoMerge: boolean | undefined to ParsedArgs.
    • Parse --auto-merge (no value) in parseArgs.
    • Update HELP_TEXT.
  4. src/cli/services/runCli.ts

    • Resolve final autoMerge.enabled as:
      parsed.autoMerge ?? env-based value.
    • Pass the resolved flag/config into runPipeline.

Unit tests

  • configLoader.test.ts: defaults, env overrides, invalid values fall back to
    defaults, masking.
  • args.test.ts: --auto-merge parsing, unknown options still rejected, help
    text includes the flag.

Exit condition

npm run lint and npm test pass; npm start -- --help shows --auto-merge;
running with PILOOP_AUTO_MERGE=1 loads config.autoMerge.enabled = true.


Milestone 2 — Issue close clients

Goal: every issue client can optionally close an issue; Jira no-ops.

File changes

  1. src/issue/types/issueCloseClient.ts (new file)

    • Define IssueCloseClient interface:
      interface IssueCloseClient {
        closeIssue(ref: IssueRef): Promise<void>;
      }
      
  2. src/issue/index.ts

    • Re-export IssueCloseClient.
  3. src/forgejo/services/forgejoIssueClient.ts

    • Add closeIssue(ref) using PATCH /api/v1/repos/{owner}/{repo}/issues/{index} with body { state: "closed" }.
    • Re-use existing request retry/auth plumbing.
  4. src/github/services/gitHubIssueClient.ts

    • Add closeIssue(ref) using PATCH /repos/{owner}/{repo}/issues/{number}
      with body { state: "closed" }.
  5. src/gitlab/services/gitLabIssueClient.ts

    • Add closeIssue(ref) using PUT /projects/{id}/issues/{iid} with body
      { state_event: "close" }.
  6. src/jira/services/jiraRestClient.ts

    • Add closeIssue(ref) that logs a debug note and returns (no-op).
    • The Jira client should still satisfy a narrow IssueCloseClient adapter.

Unit tests

  • Per-client tests with mocked HTTP:
    • success closes issue,
    • 401/403 → auth error (fail-fast when called from preflight),
    • 404 → not-found,
    • 429/5xx → retried.
  • Jira no-op test asserts no network call is made.

Exit condition

All issue clients pass unit tests; npm run lint and npm test pass.


Milestone 3 — Merge polling abstraction

Goal: a platform-agnostic way to ask "is PR/MR #N mergeable yet?"

File changes

  1. src/mr/types/mergePoller.ts (new file)

    • Define types:
      interface MergePoller {
        isMergeable(input: { repo: string; mrNumber: number }): Promise<boolean>;
        merge(input: { repo: string; mrNumber: number }): Promise<void>;
      }
      interface MergePollerFactory {
        (platform: MrPlatform, config: PiLoopConfig, originHost: string): MergePoller;
      }
      
  2. src/mr/services/forgejoMergePoller.ts (new file)

    • REST implementation for Forgejo:
      • isMergeable: GET /api/v1/repos/{owner}/{repo}/pulls/{index} and check
        mergeable === true.
      • merge: POST /api/v1/repos/{owner}/{repo}/pulls/{index}/merge with
        default strategy (empty body or { Do: "merge" }).
  3. src/mr/services/pollUntilMergeable.ts (new file)

    • Deterministic polling loop:
      async function pollUntilMergeable(
        poller: MergePoller,
        input: { repo: string; mrNumber: number },
        options: { timeoutMs: number; pollMs: number; log?: (msg) => void },
      ): Promise<void>
      
    • Throws MrStageError('auto-merge-timeout') on timeout.
  4. src/mr/helpers/mrStageError.ts

    • Add 'auto-merge-timeout' | 'auto-merge-failed' to MrStageErrorKind.

Unit tests

  • pollUntilMergeable.test.ts:
    • becomes mergeable on first check,
    • becomes mergeable after N checks,
    • timeout when never mergeable,
    • respects pollMs and timeoutMs (use fake timers).
  • forgejoMergePoller.test.ts:
    • isMergeable true/false/unknown parsing,
    • merge success,
    • merge failure mapping.

Exit condition

Unit tests pass; poller can be injected into runMrStage in 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

  1. src/mr/types/mr.ts

    • Extend MrResult:
      interface MrResult {
        // existing fields
        autoMerge?: {
          enabled: true;
          merged: boolean;
          mergedAt?: string;
          error?: string;
        };
      }
      
    • Add autoMerge: AutoMergeConfig to RunMrStageInput.
  2. src/mr/services/runMrStage.ts

    • Accept autoMerge from input.
    • When autoMerge.enabled:
      • Pass draft: false to createMr (Forgejo) and to the agent prompt
        (GitHub/GitLab).
      • After MR URL is known, extract mrNumber from URL or platform metadata.
      • Call pollUntilMergeable with the platform poller.
      • Call poller.merge().
      • Populate mrResult.autoMerge with merged: true and mergedAt.
    • On timeout/merge failure, throw MrStageError with the new kinds.
    • Ensure verbose logging of each poll attempt via logger.forStage('mr').
  3. src/mr/helpers/parseMrOutput.ts

    • Parse an optional MR_NUMBER trailer from agent output for GitHub/GitLab so
      polling/merge can use it.
  4. src/artifacts/services/mrResultArtifact.ts and
    src/artifacts/helpers/validateMrResult.ts

    • Accept the new autoMerge shape in the schema.

Unit tests

  • runMrStage.test.ts:
    • auto-merge disabled → draft true, no polling.
    • auto-merge enabled Forgejo → non-draft, polls, merges, records
      autoMerge.merged = true.
    • auto-merge timeout → stage throws auto-merge-timeout.
    • auto-merge merge API failure → stage throws auto-merge-failed.

Exit condition

npm test passes; Forgejo path can create, poll, and merge in unit tests.


Milestone 5 — Orchestrator wiring & issue close

Goal: runPipeline closes the issue after a successful auto-merge and
handles close failures non-fatally.

File changes

  1. src/orchestrator/services/runPipeline.ts

    • Extend defaultMakeCommentClient to return an object that also implements
      IssueCloseClient where supported (all providers now, with Jira no-op).
    • After runMr, if autoMerge.enabled && mrResult.autoMerge?.merged:
      • Try client.closeIssue(issueRef).
      • Log success.
      • On failure: log warning, surface in summary, but do not fail the run.
    • Pass autoMerge config into runMr.
  2. src/orchestrator/types/orchestrator.ts

    • Add autoMerge?: { merged: boolean; closed?: boolean; closeError?: string }
      to RunPipelineResult success branch.

Unit tests

  • runPipeline.test.ts:
    • auto-merge disabled → no close call.
    • auto-merge succeeds → close called.
    • close fails → run still succeeds with closeError in result.
    • Jira source → close no-op, no error.

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

  1. src/writeback/helpers/formatWritebackSummary.ts

    • Accept outcome: 'draft' | 'merged' | 'merge-failed'.
    • Return the appropriate message from Design §5.6.
  2. src/writeback/services/runWriteback.ts

    • Accept optional outcome in RunWritebackInput.
    • Pass it to the formatter.
  3. src/writeback/types/writeback.ts

    • Add outcome?: 'draft' | 'merged' | 'merge-failed' to RunWritebackInput.
  4. src/orchestrator/services/runPipeline.ts

    • Compute outcome from autoMerge state and pass it to runWriteback.

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

  1. src/artifacts/helpers/validateMrResult.ts

    • Allow optional autoMerge object.
  2. src/orchestrator/helpers/buildFinalSummary.ts

    • Include auto-merge status and issue-close status in the text summary.
  3. src/mr/helpers/buildMrDescription.ts

    • No change required for v1 (no close keyword).

Unit tests

  • validateMrResult.test.ts: valid with/without autoMerge; 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

  1. src/mr/services/githubMergePoller.ts (new file)

    • Use gh pr view <number> --json mergeStateStatus,mergeable for
      isMergeable.
    • Use gh pr merge <number> --merge for merge.
  2. src/mr/services/gitlabMergePoller.ts (new file)

    • Use glab mr view <iid> parsing or REST for isMergeable.
    • Use glab mr merge <iid> for merge.
  3. src/mr/services/makeMergePoller.ts (new file)

    • Factory that selects Forgejo/GitHub/GitLab poller based on platform.
  4. src/mr/services/runMrStage.ts

    • Use the factory instead of a hardcoded Forgejo poller.

Unit tests

  • Per-platform poller tests with mocked CLI output.
  • Factory test selects the right implementation.

Milestone 9 — Gated E2E for Forgejo

Goal: exercise a real merge + close end-to-end.

File changes

  1. src/orchestrator/services/runPipeline.e2e.test.ts

    • Add a new E2E case guarded by PILOOP_E2E=1:
      • Target a throwaway Forgejo repo.
      • Create a throwaway issue.
      • Run pi-loop <issue-ref> --auto-merge.
      • Assert:
        • ok === true.
        • mrResult.autoMerge?.merged === true.
        • The issue is closed (query Forgejo API).
        • Write-back comment includes "merged".
  2. AGENTS.md / README

    • Document --auto-merge, env vars, timeout/poll tuning, and Jira no-op.

Exit condition

PILOOP_E2E=1 npm run e2e passes for the new Forgejo auto-merge case.


Suggested sequencing

M1 ─► M2 ─► M3 ─► M4 ─► M5 ─► M6 ─► M7 ─► M8 ─► M9

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

  • Forgejo sandbox repo with a default branch and no required approvals
    blocking merge.
  • FORGEJO_TOKEN with permission to create PRs, merge PRs, and close
    issues.
  • Node.js ≥ 22.19.0 and the existing pi-loop toolchain.
  • Decision from this design doc accepted (all branches resolved).

Definition of done

  • --auto-merge flag and PILOOP_AUTO_MERGE env var work end-to-end on
    Forgejo.
  • PR/MR is created non-draft when auto-merge is enabled.
  • Poll-and-merge respects timeout/poll env vars.
  • Issue is closed via API after successful merge on Forgejo/GitHub/GitLab.
  • Jira issue is not closed.
  • Write-back comment reflects outcome.
  • All new deterministic seams have unit tests.
  • Gated Forgejo E2E passes.
  • npm run lint and npm test pass.
  • A Forgejo issue tracking this work exists and links to this plan.
# Implementation Plan — pi-loop: Auto-merge Flag Step-by-step engineering plan to add the `--auto-merge` flag, poll-and-merge logic, 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 - Keep all non-LLM logic deterministic and unit-tested. - Re-use existing seams (`PiLoopConfig`, `IssueCommentClient`, `MrClient`, `runMrStage`, `runPipeline`, `IssueRef`). - Follow pi-loop's existing error-handling policy: fail-fast before the MR stage; non-fatal for close/write-back after the MR exists. - Update schemas and tests together. --- ## Milestone 1 — Configuration & CLI flag **Goal:** `--auto-merge` and `PILOOP_AUTO_MERGE` are parsed and loaded. ### File changes 1. `src/config/types/config.ts` - Add `AutoMergeConfig` interface. - Add `autoMerge: AutoMergeConfig` to `PiLoopConfig` and `MaskedConfig`. 2. `src/config/services/configLoader.ts` - Parse `PILOOP_AUTO_MERGE` as boolean-ish (`1`/`true` → enabled). - Parse `PILOOP_AUTO_MERGE_TIMEOUT_MS` and `PILOOP_AUTO_MERGE_POLL_MS` as positive integers with safe defaults. - Update `maskConfig` to show the three new values. 3. `src/cli/helpers/args.ts` - Add `autoMerge: boolean | undefined` to `ParsedArgs`. - Parse `--auto-merge` (no value) in `parseArgs`. - Update `HELP_TEXT`. 4. `src/cli/services/runCli.ts` - Resolve final `autoMerge.enabled` as: `parsed.autoMerge ?? env-based value`. - Pass the resolved flag/config into `runPipeline`. ### Unit tests - `configLoader.test.ts`: defaults, env overrides, invalid values fall back to defaults, masking. - `args.test.ts`: `--auto-merge` parsing, unknown options still rejected, help text includes the flag. ### Exit condition `npm run lint` and `npm test` pass; `npm start -- --help` shows `--auto-merge`; running with `PILOOP_AUTO_MERGE=1` loads `config.autoMerge.enabled = true`. --- ## Milestone 2 — Issue close clients **Goal:** every issue client can optionally close an issue; Jira no-ops. ### File changes 1. `src/issue/types/issueCloseClient.ts` *(new file)* - Define `IssueCloseClient` interface: ```ts interface IssueCloseClient { closeIssue(ref: IssueRef): Promise<void>; } ``` 2. `src/issue/index.ts` - Re-export `IssueCloseClient`. 3. `src/forgejo/services/forgejoIssueClient.ts` - Add `closeIssue(ref)` using `PATCH /api/v1/repos/{owner}/{repo}/issues/{index}` with body `{ state: "closed" }`. - Re-use existing `request` retry/auth plumbing. 4. `src/github/services/gitHubIssueClient.ts` - Add `closeIssue(ref)` using `PATCH /repos/{owner}/{repo}/issues/{number}` with body `{ state: "closed" }`. 5. `src/gitlab/services/gitLabIssueClient.ts` - Add `closeIssue(ref)` using `PUT /projects/{id}/issues/{iid}` with body `{ state_event: "close" }`. 6. `src/jira/services/jiraRestClient.ts` - Add `closeIssue(ref)` that logs a debug note and returns (no-op). - The Jira client should still satisfy a narrow `IssueCloseClient` adapter. ### Unit tests - Per-client tests with mocked HTTP: - success closes issue, - `401`/`403` → auth error (fail-fast when called from preflight), - `404` → not-found, - `429`/`5xx` → retried. - Jira no-op test asserts no network call is made. ### Exit condition All issue clients pass unit tests; `npm run lint` and `npm test` pass. --- ## Milestone 3 — Merge polling abstraction **Goal:** a platform-agnostic way to ask "is PR/MR #N mergeable yet?" ### File changes 1. `src/mr/types/mergePoller.ts` *(new file)* - Define types: ```ts interface MergePoller { isMergeable(input: { repo: string; mrNumber: number }): Promise<boolean>; merge(input: { repo: string; mrNumber: number }): Promise<void>; } interface MergePollerFactory { (platform: MrPlatform, config: PiLoopConfig, originHost: string): MergePoller; } ``` 2. `src/mr/services/forgejoMergePoller.ts` *(new file)* - REST implementation for Forgejo: - `isMergeable`: `GET /api/v1/repos/{owner}/{repo}/pulls/{index}` and check `mergeable === true`. - `merge`: `POST /api/v1/repos/{owner}/{repo}/pulls/{index}/merge` with default strategy (empty body or `{ Do: "merge" }`). 3. `src/mr/services/pollUntilMergeable.ts` *(new file)* - Deterministic polling loop: ```ts async function pollUntilMergeable( poller: MergePoller, input: { repo: string; mrNumber: number }, options: { timeoutMs: number; pollMs: number; log?: (msg) => void }, ): Promise<void> ``` - Throws `MrStageError('auto-merge-timeout')` on timeout. 4. `src/mr/helpers/mrStageError.ts` - Add `'auto-merge-timeout' | 'auto-merge-failed'` to `MrStageErrorKind`. ### Unit tests - `pollUntilMergeable.test.ts`: - becomes mergeable on first check, - becomes mergeable after N checks, - timeout when never mergeable, - respects `pollMs` and `timeoutMs` (use fake timers). - `forgejoMergePoller.test.ts`: - `isMergeable` true/false/unknown parsing, - `merge` success, - `merge` failure mapping. ### Exit condition Unit tests pass; poller can be injected into `runMrStage` in 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 1. `src/mr/types/mr.ts` - Extend `MrResult`: ```ts interface MrResult { // existing fields autoMerge?: { enabled: true; merged: boolean; mergedAt?: string; error?: string; }; } ``` - Add `autoMerge: AutoMergeConfig` to `RunMrStageInput`. 2. `src/mr/services/runMrStage.ts` - Accept `autoMerge` from input. - When `autoMerge.enabled`: - Pass `draft: false` to `createMr` (Forgejo) and to the agent prompt (GitHub/GitLab). - After MR URL is known, extract `mrNumber` from URL or platform metadata. - Call `pollUntilMergeable` with the platform poller. - Call `poller.merge()`. - Populate `mrResult.autoMerge` with `merged: true` and `mergedAt`. - On timeout/merge failure, throw `MrStageError` with the new kinds. - Ensure verbose logging of each poll attempt via `logger.forStage('mr')`. 3. `src/mr/helpers/parseMrOutput.ts` - Parse an optional `MR_NUMBER` trailer from agent output for GitHub/GitLab so polling/merge can use it. 4. `src/artifacts/services/mrResultArtifact.ts` and `src/artifacts/helpers/validateMrResult.ts` - Accept the new `autoMerge` shape in the schema. ### Unit tests - `runMrStage.test.ts`: - auto-merge disabled → draft true, no polling. - auto-merge enabled Forgejo → non-draft, polls, merges, records `autoMerge.merged = true`. - auto-merge timeout → stage throws `auto-merge-timeout`. - auto-merge merge API failure → stage throws `auto-merge-failed`. ### Exit condition `npm test` passes; Forgejo path can create, poll, and merge in unit tests. --- ## Milestone 5 — Orchestrator wiring & issue close **Goal:** `runPipeline` closes the issue after a successful auto-merge and handles close failures non-fatally. ### File changes 1. `src/orchestrator/services/runPipeline.ts` - Extend `defaultMakeCommentClient` to return an object that also implements `IssueCloseClient` where supported (all providers now, with Jira no-op). - After `runMr`, if `autoMerge.enabled && mrResult.autoMerge?.merged`: - Try `client.closeIssue(issueRef)`. - Log success. - On failure: log warning, surface in summary, but do **not** fail the run. - Pass `autoMerge` config into `runMr`. 2. `src/orchestrator/types/orchestrator.ts` - Add `autoMerge?: { merged: boolean; closed?: boolean; closeError?: string }` to `RunPipelineResult` success branch. ### Unit tests - `runPipeline.test.ts`: - auto-merge disabled → no close call. - auto-merge succeeds → close called. - close fails → run still succeeds with `closeError` in result. - Jira source → close no-op, no error. ### 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 1. `src/writeback/helpers/formatWritebackSummary.ts` - Accept `outcome: 'draft' | 'merged' | 'merge-failed'`. - Return the appropriate message from Design §5.6. 2. `src/writeback/services/runWriteback.ts` - Accept optional `outcome` in `RunWritebackInput`. - Pass it to the formatter. 3. `src/writeback/types/writeback.ts` - Add `outcome?: 'draft' | 'merged' | 'merge-failed'` to `RunWritebackInput`. 4. `src/orchestrator/services/runPipeline.ts` - Compute outcome from `autoMerge` state and pass it to `runWriteback`. ### 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 1. `src/artifacts/helpers/validateMrResult.ts` - Allow optional `autoMerge` object. 2. `src/orchestrator/helpers/buildFinalSummary.ts` - Include auto-merge status and issue-close status in the text summary. 3. `src/mr/helpers/buildMrDescription.ts` - No change required for v1 (no close keyword). ### Unit tests - `validateMrResult.test.ts`: valid with/without `autoMerge`; 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 1. `src/mr/services/githubMergePoller.ts` *(new file)* - Use `gh pr view <number> --json mergeStateStatus,mergeable` for `isMergeable`. - Use `gh pr merge <number> --merge` for `merge`. 2. `src/mr/services/gitlabMergePoller.ts` *(new file)* - Use `glab mr view <iid>` parsing or REST for `isMergeable`. - Use `glab mr merge <iid>` for `merge`. 3. `src/mr/services/makeMergePoller.ts` *(new file)* - Factory that selects Forgejo/GitHub/GitLab poller based on platform. 4. `src/mr/services/runMrStage.ts` - Use the factory instead of a hardcoded Forgejo poller. ### Unit tests - Per-platform poller tests with mocked CLI output. - Factory test selects the right implementation. --- ## Milestone 9 — Gated E2E for Forgejo **Goal:** exercise a real merge + close end-to-end. ### File changes 1. `src/orchestrator/services/runPipeline.e2e.test.ts` - Add a new E2E case guarded by `PILOOP_E2E=1`: - Target a throwaway Forgejo repo. - Create a throwaway issue. - Run `pi-loop <issue-ref> --auto-merge`. - Assert: - `ok === true`. - `mrResult.autoMerge?.merged === true`. - The issue is closed (query Forgejo API). - Write-back comment includes "merged". 2. `AGENTS.md` / README - Document `--auto-merge`, env vars, timeout/poll tuning, and Jira no-op. ### Exit condition `PILOOP_E2E=1 npm run e2e` passes for the new Forgejo auto-merge case. --- ## Suggested sequencing ``` M1 ─► M2 ─► M3 ─► M4 ─► M5 ─► M6 ─► M7 ─► M8 ─► M9 ``` 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 - [ ] Forgejo sandbox repo with a default branch and no required approvals blocking merge. - [ ] `FORGEJO_TOKEN` with permission to create PRs, merge PRs, and close issues. - [ ] Node.js ≥ 22.19.0 and the existing pi-loop toolchain. - [ ] Decision from this design doc accepted (all branches resolved). --- ## Definition of done - [ ] `--auto-merge` flag and `PILOOP_AUTO_MERGE` env var work end-to-end on Forgejo. - [ ] PR/MR is created non-draft when auto-merge is enabled. - [ ] Poll-and-merge respects timeout/poll env vars. - [ ] Issue is closed via API after successful merge on Forgejo/GitHub/GitLab. - [ ] Jira issue is not closed. - [ ] Write-back comment reflects outcome. - [ ] All new deterministic seams have unit tests. - [ ] Gated Forgejo E2E passes. - [ ] `npm run lint` and `npm test` pass. - [ ] A Forgejo issue tracking this work exists and links to this plan.
david closed this issue 2026-07-29 01:39:25 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-loop#38
No description provided.