Architecture

Issue-to-PR workflow

Issue-to-PR workflow

A scoped GitHub issue can become a verified, review-ready pull request without ever widening the runtime's authority. The entry point is the same authenticated webhook path: an issues event is parsed in packages/source-control and produces a task only when repository policy has opted in (issues.enabled) and the issue carries the issues.requireLabel label, so arbitrary issue text can never start a run. The issue's title and body travel to the runtime as bounded, untrusted feedback with trigger issue — data to validate, never instructions — and the run mode comes only from repository policy, never from the wire.

The run itself is the ordinary lifecycle. During planning the model records verifiable acceptance criteria for the issue alongside its plan; the runtime bounds them and carries them into the task result and evidence bundle. Writes still require an explicit write mode, autofix.enabled, confidence, an allowed change-risk class, and — by default, like proactive work — an isolated runner. High-impact changes stop at needs-human exactly as before.

The validation verdict is reported back where the work was requested. Unless issues.validationComment is disabled, a finished run posts one comment on the issue, composed by prepareIssueValidationComment from the persisted evidence alone: confirmed when repository evidence supports the report, not confirmed with every rejection reason when it does not, inconclusive when a human should decide. A run that failed before reaching a verdict posts nothing rather than something misleading, and the GitHubIssueComments adapter can only add a comment — it has no path to label, edit, or close an issue. The comment claims a fix exists only when the run was actually verified.

Publication has a single home: prepareIssuePullRequest in packages/source-control decides whether a finished run has earned a pull request, and composes it when it has. It refuses any run that is not completed, not accepted, not verified by the repository's own checks, changed no files, or proposes a high-impact change, so a pull request can never claim success its evidence does not support — the body is the rendered evidence, including the acceptance criteria. The composition root in apps/dashboard then reads the verified file contents through a read-only runner and hands them to the GitHubPullRequests adapter, which publishes them as a commit on a fresh issues.branchPrefix branch through the Git data API and opens the pull request against the default branch. The branch name is assembled only from operator policy, the issue number, and the task identifier; an existing ref is never force-updated; and the default branch is never committed to. A failed publication never fails the run — the evidence is already persisted — it is reported as the reason no pull request exists.

Copyright © 2026