Skip to content

Run-once

patchmill run-once advances one actionable issue through the configured workflow. For a fresh agent-ready issue it initializes strict planning-pr-v1 state and snapshots the configured planning review gates. That snapshot stays immutable for the Issue run.

Preview selection without mutation:

patchmill run-once --dry-run

For independent issues, start separate commands from the same clone root:

# Terminal 1
patchmill run-once --issue 101

# Terminal 2
patchmill run-once --issue 102

Each process owns its selected issue, phase workspaces, state, artifacts, logs, and Pi sessions. Explicit selection reads only the requested issue. Other active or resumable issues do not prevent new explicit work.

If the requested issue already has an owner, Patchmill reports issue already in progress. and returns exit code 0. The losing process leaves the issue unchanged. This result does not require lease repair.

Without --issue, automatic selection remains serial. Automatic commands cannot overlap explicit commands, other automatic commands, or mutating recovery commands. Dry-run reserves no ownership.

Select independent issues. Control model spending and the number of processes. Allocate CPU capacity, ports, test databases, and external services for each workspace. Patchmill does not predict conflicts between independent changes.

Use one supported Patchmill version and one clone root. The Git common directory binds the host repository, state directory, workspace root, and todo definition to one namespace. Symlinks do not create another namespace.

Before an upgrade or state migration, stop all affected processes. A changed namespace requires deliberate migration while no process owns it.

Unsupported cases include:

  • Coordination across separate clones or hosts.
  • Concurrent configuration changes or artifact-edit commands such as set-plan.
  • Manual shared Git mutations during active Run attempts.
  • Cleanup hooks that remove shared services or another issue’s resources.
  • Multiple writers for the same issue.

CAUTION: Do not remove another Run attempt’s locks or use destructive cleanup as recovery. These actions can destroy owned work or permit conflicting writers.

The default todo root remains .pi/todos inside the owned workspace. Relative custom roots use that workspace, not the shared clone root. Absolute shared roots use issue-scoped reads and mutations. Startup and cleanup preserve other issues’ tasks and disable global todo garbage collection in shared roots.

Resume preserves existing todo locations, titles, tags, and completion rules. Ambiguous locations block instead of combining or moving tasks. Each new Run attempt receives separate logs and sessions, even with identical timestamps.

When issueState.provider is comments, Run-once reads the latest trusted Patchmill state comment instead of repository labels for workflow state such as agent-ready, in-progress, spec-review, plan-review, and agent-done. State changes append new comments and do not edit or delete older state comments.

Priority labels still affect automatic issue ordering when they exist. If you cannot use priority labels, Run-once falls back to issue-number ordering among eligible issues.

The saved gate snapshot assigns spec and plan artifacts to a phase workspace: the owned worktree and branch for one spec, plan, or implementation phase, pinned to saved remote-base evidence.

  1. Patchmill fetches the target base and resolves assigned artifacts. Exactly one matching regular file under the configured directory satisfies assigned work; none creates work and multiple candidates block.

  2. An agent edits, self-reviews, and commits only the requested artifact in its phase workspace.

  3. If that phase has a required planning review gate, Patchmill pushes it in a non-closing planning pull request and returns review-pending with exit code 0.

  4. A human reviews and merges that exact pull request. Run ordinary recovery:

    patchmill run-once --issue N

    Patchmill verifies the exact saved pull request and its merge on the saved target base before advancing. Comments and labels do not unlock a fresh phase.

  5. Implementation always produces an open, issue-closing pull request. Patchmill verifies its ownership marker, closing reference, repository, branches, and head OID. Publication returns pr-created and leaves the Issue run in progress.

  6. After the PR merges, rerun the same explicit command. Patchmill verifies its merge against the fetched target base and resumes finish checkpoints without another agent or PR. Only verified merge and completed finish checkpoints produce merged and the done label.

Spec review Plan review Pull request sequence
Off Off Implementation contains spec, plan, and code.
On Off Spec planning pull request, then implementation with plan and code.
Off On Plan planning pull request contains spec and plan, then implementation.
On On Spec planning pull request, plan planning pull request, then implementation.

GitHub planning heads remain in the target repository. Forgejo may use the supported same-host head repository. Both providers use the same markers, gate rules, merge verification, and recovery behavior.

Progress is written to stderr. Redirect stdout for the compact one-line JSON result:

patchmill run-once --issue N > result.json

review-pending and stopped with reason plan-only are exit-zero nonfailure results. Patchmill no longer returns the public cleanup-pending result, even for saved records from older releases.

After a legacy workspace refresh, the result includes quarantinePaths. This array names the retained old checkouts. Recovery progress, final JSONL data, and the terminal’s Quarantine paths section name the same paths. A later blocked result also retains this field.

An open planning review takes precedence over a plan-only stop. A later invocation without the option resumes the saved phase and gate snapshot.

Every reason-bearing blocked, stopped, environment, and error result keeps its stable reason code and adds a diagnostic object. The object has a short summary, plain-language explanation, attributed details, safe actions, safety warnings, and retry guidance. Redirected stdout and the final JSONL result event contain the same object; terminal output renders the same fields as Reason, Explanation, Details, Recommended action, Safety, and Retry.

retry.kind states when another invocation can help: retry-now means the recommended normal command can make progress now; after-action means first complete the listed safe action; same-result means the same command will stop again until the recorded state changes; and inspect-first means inspect and reconcile preserved evidence before deciding whether to retry. For example, redirected output for a dirty phase workspace includes its stable code and the same structured diagnostic written to JSONL:

{
  "status": "blocked",
  "issueNumber": 242,
  "reason": "planning-workspace-dirty",
  "diagnostic": {
    "summary": "Phase workspace has local changes",
    "explanation": "The phase workspace contains local changes and cannot be resumed or published safely.",
    "details": [
      {
        "key": "issueNumber",
        "label": "Issue",
        "value": 242
      },
      {
        "key": "phase",
        "label": "Phase",
        "value": "plan"
      },
      {
        "key": "branch",
        "label": "Branch",
        "value": "agent/issue-242-plan"
      },
      {
        "key": "worktreePath",
        "label": "Worktree",
        "value": ".worktrees/patchmill-issue-242-plan"
      },
      {
        "key": "workspaceState",
        "label": "Workspace state",
        "value": "ready"
      },
      {
        "key": "statusEvidence",
        "label": "Status",
        "value": "dirty"
      }
    ],
    "actions": [
      {
        "description": "Inspect the reported phase, branch, path, state, and status evidence; preserve the work before retrying."
      }
    ],
    "safety": [
      "Do not run git clean, destructive reset, or delete the phase workspace."
    ],
    "retry": {
      "kind": "same-result",
      "guidance": "An immediate retry will give the same result."
    }
  },
  "questions": []
}

Selection rejection JSONL events also include a diagnostic beside their reason. A final no-issue result intentionally has no aggregate reason because its per-Issue rejection events are the source of that information.

Commands in diagnostics are limited to supported commands with a positive Issue number: patchmill run-once --issue N, and, only for Issue run lease/fence recovery, patchmill run lease repair --issue N. A planning-pr-v1 planning lock is not an Issue run lease: lock diagnostics never recommend lease repair.

For intentional resume, run patchmill run-once --issue N again. A process exit, cancellation, crash, or open PR does not complete the Issue run. A merged PR can close the host issue before local finish checkpoints complete. Explicit recovery still verifies that saved PR and finishes only its owned issue.

A busy shared Git transaction returns stopped with reason repository-busy. Retry the same explicit command after the other transaction ends. Patchmill preserves the selected checkpoint and lifecycle labels. Independent agent work does not hold the Git guard.

A timed-out or interrupted Git command can leave partial mutations. Preserve its state and command evidence. A dead owner PID alone cannot authorize recovery of an unverified active Git command.

A retry observes durable state, the remote, and the host before repeating an effect. It can adopt an exact pushed head or created pull request, complete a checkpointed cleanup, return the same open review, or verify a merge.

Patchmill uses ordinary Git status for verification, recovery, and cleanup. Ignored content never blocks these operations or requires an operator label or retry. This rule applies to every ignored path, including collapsed directories such as .pi/ and tool output such as node_modules/.

Staged changes, tracked changes, and non-ignored untracked files remain blockers. A force-added ignored file counts as staged or committed content. Existing status and artifact-diff guards still apply. Read-only artifact verification preserves HEAD, the index, artifact files, and ignored files.

Phase removal still requires valid ownership, path identity, registration, HEAD, and publication evidence.

CAUTION: Do not store unique data in ignored files inside disposable phase workspaces. Cleanup permanently deletes all ignored content, including .env, .pi/, unknown files, and operator-created files.

In-place recovery preserves ignored files. Legacy refresh and reset move the old checkout, with its ignored files, to a retained quarantine path. Patchmill never removes quarantine paths. The replacement checkout starts without those old ignored files. Reset reports the retained paths through its existing quarantinePaths result.

This preservation rule does not apply to final phase cleanup. Patchmill never force-cleans a phase workspace or force-updates a conflicting branch. It does not replace a missing, ambiguous, or closed-unmerged planning pull request.

A retry can recover one committed artifact that the agent completed before its checkpoint failed. Patchmill proves ownership, ordinary cleanliness, ancestry, and a single artifact-only delta from the saved HEAD. It selects the committed artifact by issue number and configured directory, not by the retry date. The checkpoint retains the original artifact path, commit, todo root, and todos. No second artifact agent runs for that artifact. Ambiguous candidates, unrelated commits, changed HEAD, or checkpoint errors stop before later agents or publication. Normal planning review and implementation PR gates still apply.

Saved legacy cleanup-pending records resume normal cleanup without a ready label. Patchmill does not publish new needs-info state for these records. Existing needs-info state still blocks selection until the configured ready state acknowledges it.

Situation Operator action
Open review Review or merge the same pull request, then rerun Run-once.
Legacy cleanup pending Rerun the same issue. No cleanup-specific ready label is necessary. Existing needs-info state still requires the normal ready acknowledgement.
Closed-unmerged, proven missing, or ambiguous review Repair the host state manually; Patchmill does not replace the pull request.
Dirty or uncheckpointed phase workspace Inspect and preserve local work before retrying.
Transient host failure Repair authentication or connectivity, then retry.
Active lock Wait for its owner; never remove it.
Stale same-host lock Run-once automatically archives exact stale bytes under planning-pr-v1/archive/issue-locks/ and continues after a same-host dead-PID proof. The console warning identifies the archive path; JSONL warning data includes the fingerprint and archive path. Archives are retained.
Unverifiable, malformed, or transition-conflict lock Coordinate with the recorded host/operator and inspect the preserved evidence before retrying; age alone proves nothing. Remote hosts, PID reuse that appears live, and infrastructure failures remain fail-closed.

patchmill run lease repair and patchmill run reset do not authorize deleting a planning-pr-v1 lock, state file, branch, or workspace. They retain their legacy recovery behavior only. Mutating reset and lease repair also use repository admission. Reset refuses planning state and published PR checkpoints. Use ordinary explicit resume for those checkpoints.

Read-only patchmill run lease repair --issue N reserves no ownership. For a known interrupted local repair, inspection prints exact primary and paired guard fingerprints. The confirmed repair command verifies shutdown and archives only those linked guards. It preserves the target lease and earlier interrupted evidence. Inspect again before any separate lease repair.

Unknown, foreign, malformed, unlinked, or unverified repair guards remain blocked. Interrupted reconciliation guards also remain blocked. Preserve this evidence for operator inspection. No lock-bypass flag exists.

set-spec, set-plan, approval labels, uploads, and --plan-only remain available for unfinished legacy Issue runs during the compatibility window. They are deprecated for fresh work. Use Run-once review gates for automated review stops, or the human-invoked patchmill-plan skill for local planning without automated implementation.

Legacy implementation also requires a PR. git.allowDirectLand: true is no longer supported. An old direct-land checkpoint without verifiable PR evidence blocks with migration guidance. Preserve that checkpoint instead of creating replacement work.