Skip to content

Promotion pipeline

Every scheduled run ends in an explicit promotion decision. Nothing an agent does reaches a branch without passing this pipeline.

agent finishes
├─ validations run (lint · test · typecheck, whichever are configured)
│ └─ failing or missing commands shape the decision below
├─ risk assessment (task risk class × policy.yaml riskClasses)
├─ review (optional, deterministic): scope drift + secret detection
└─ decision ──► auto-merge · pull-request · manual-review

Artifacts are recorded under .openloop/promotions/; each decision also lands in the event log (promotion-auto-merge-queued, promotion-review-queued, promotion-blocked, promotion-ci-failed).

The default policy (policy.yaml) encodes three rules:

Risk Validations Decision
low-risk all configured commands pass auto-merge
medium-risk , pull request
high-risk , pull request (and human approval before running)

Plus the hard preconditions for auto-merge; it is refused when:

  • No validation commands are configured for the project. An unvalidated repo never merges autonomously.
  • The deterministic review produced blocking findings (scope drift, secrets); the decision is downgraded to manual review.
  • A lifecycle hook responded with {"requireManualReview": true} (see hooks).
  • Policy denies the touched paths; see policy & risk.

Auto-merge applies the change on an openloop/… branch and merges it. Pull requests are created with gh pr create by default (override with runtime.prCommand).

With review.enabled: true in project.json, a deterministic post-implementation review runs before the decision:

  • Scope drift: compares the actual diff against the task’s allowed scope and the policy globs.
  • Secret detection: scans the diff for leaked credentials.

Blocking findings downgrade auto-merge to manual review and are recorded with the artifact.

With verification.enabled: true (the default), an independent verifier pass double-checks the implementation against the task’s acceptance criteria. Verdicts are written to .openloop/verifications/.

Terminal window
openloop promotion list -p api # all artifacts
openloop promotion list -p api --status pending
openloop promotion show -p api --task add-rate-limit-headers
openloop promotion history -p api --task add-rate-limit-headers
# resolve pending items yourself
openloop promotion apply -p api --task add-rate-limit-headers --dry-run
openloop promotion apply -p api --task add-rate-limit-headers --note "LGTM"
openloop promotion reject -p api --task add-rate-limit-headers --note "wrong layer"
# refresh live PR state (checks, merge status) via gh
openloop promotion refresh -p api --task add-rate-limit-headers

promotion refresh shells out to gh pr view, so it needs an authenticated GitHub CLI and is not polled automatically by the daemon.

A project with runtime.maxPendingReviewsPerProject (default 3) or more pending promotions is skipped by the scheduler until its review queue drains, autonomy pauses where human judgment is expected.