원클릭으로
deliver-pr
Deliver GitHub pull requests or GitLab merge requests end to end through the released nils-cli `forge-cli pr deliver` macro.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Deliver GitHub pull requests or GitLab merge requests end to end through the released nils-cli `forge-cli pr deliver` macro.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Operate and test a local or SSH-reachable macOS desktop through nils-cli macos-agent, with AX-first actions, screenshots, scenarios, explicit postconditions, guarded replay, and privacy-preserving journals.
Review a code change through an internally selected quick, focused, specialist, follow-up, or pre-merge mode and return evidence-grounded findings.
Open or resume one shared dispatch plan issue, coordinate independently reviewed lane PRs, integrate approved work, and close through strict gates.
Open or resume one lightweight issue-backed plan tracker and carry it through implementation, review, PR delivery, strict closeout, and archive handoff.
Review a code change through an internally selected quick, focused, specialist, follow-up, or pre-merge mode and return evidence-grounded findings.
Capture screenshots or recordings from windows or displays through the nils-cli `screen-record` command.
SOC 직업 분류 기준
| name | deliver-pr |
| description | Deliver GitHub pull requests or GitLab merge requests end to end through the released nils-cli `forge-cli pr deliver` macro. |
Prereqs:
agent-runtime, forge-cli >=1.22.12, plan-issue >=1.1.0, and
review-specialists are installed from the released nils-cli package and
available on PATH. The generic code-review outcome uses
review-specialists in pre-merge mode; native review summaries and observed
convergence need forge-cli 1.21.34, guarded pending-review recovery needs
1.22.12, the review-thread merge gate needs 1.0.16, the task-list merge gate
needs 1.0.17, and
existing-PR adoption in pr deliver needs 1.1.0. Linked issue closeout
relies on the unified terminal task-row contract in plan-issue 1.1.0.references/pr-lifecycle.md are satisfied.semantic-commit; commit
mutation is an internal delivery prerequisite, not a user-selected workflow..agents/scripts/pre-pr.sh exists, run it through the repository dispatcher
before the first provider mutation. Abandon-close is a remote terminal route
and must not depend on unrelated local pre-PR validation.Inputs:
github or gitlab (let forge-cli detect it from the remote, or
pass --provider explicitly).feature, bug, chore, docs, ci, or refactor;
it must match the branch prefix.agent-runtime pr-body render.--no-merge when the workflow should stop after checks.--no-closeout to stop after delivery readiness checks and before
linked issue closeout.Refs #<issue>; provider auto-close keywords are refused.
Carry the references through pr-body render --issues-file — rendered as
## Issues after ## Summary for every kind (bug keeps its required
## Issues Found section) — instead of hand-placing them in the summary.role=session, validation, and review evidence must be present before merge.Outputs:
forge-cli pr wait-checks.testing and maintainability.COMMENT review events on GitHub via --submit-review, outcome notes
on GitLab). These use portable --decision comments-only and --lens
semantics and report findings and evidence only; the active environment owns
any optional identity mapping. On GitHub, actionable findings that require owner
changes are also passed through --thread-file so the owning agent can fix and
resolve them; no-finding reports omit --thread-file and stay summary-only.
If a linked tracking or dispatch issue is present, mirror the compact review
URL breadcrumb to that issue.forge-cli pr review; combined owner outcomes use the final --decision
plus repeated selected lenses and own final finding dispositions. GitHub uses
a native APPROVE / REQUEST_CHANGES review only when an environment-owned
router guarantees an identity independent from the PR author; other paths use
the outcome-note form.forge-cli pr reviews and semantically dispositioned before the final owner
outcome. Stale-head reviews remain informational. GitLab retains its outcome
note flow because native review snapshots are GitHub-only in v1.forge-cli pr merge. A typed gate failure routes to the
matching read/disposition/retry path instead of an agent-authored polling
loop.forge-cli pr merge, unless --no-merge is supplied.plan-issue record close posts closeout
evidence, repairs the dashboard, verifies linked records, and closes the
issue.git-cli worktree remove; retained
unsafe state includes its reason and recovery command.Failure modes:
github_pending_review_exists,
but data.pending_reviews[] does not
identify exactly one abandoned current-viewer review for this PR. Stop rather
than deleting ambiguous review state or falling back to an outcome note.forge-cli pr merge returns review_changes_requested,
review_convergence_activity_changed, review_convergence_head_changed,
review_convergence_timeout, review_snapshot_incomplete,
unresolved_review_threads, or unchecked_task_items. Read and disposition
the matching provider evidence before retrying; do not replace the CLI gate
with a custom timing loop.- [ ] task-list items remain in the PR/MR description at merge
time. The description is the delivery contract; forge-cli pr merge fails
closed with unchecked_task_items, and the task-list sweep is how the
workflow dispositions them before that gate trips.local_path_present: rewrite useful evidence paths in provider-visible PR
bodies, delivery outcome comments, or linked issue closeout records to
$HOME/... and omit remote-useless local artifact paths before retrying.deliver-plan-tracking-issue or deliver-dispatch-plan
instead of merging and backfilling after the fact.plan-issue record close rejects linked issue closeout.The user requests the PR/MR outcome, not a lifecycle helper.
forge-cli pr create, return its URL, and stop before checks,
review, merge, or linked-issue closeout.forge-cli pr merge.forge-cli pr close without
pretending delivery succeeded.Dispatch lane PR creation remains an internal L3 dispatch role because its plan-branch target and lane checkpoint authority belong to that outcome.
Use agent-runtime pr-body render as the canonical formatter. The shared
PR/MR lifecycle reference owns minimum headings, label selection, and
non-closing issue references.
Render the body with agent-runtime before calling the delivery macro:
agent-runtime pr-body render \
--kind feature \
--summary-file "$SUMMARY_FILE" \
--changes-file "$CHANGES_FILE" \
--test-first-file "$TEST_FIRST_FILE" \
--test-plan-file "$TEST_PLAN_FILE" \
--risk-file "$RISK_FILE" \
--out "$PR_BODY"
Add --issues-file "$ISSUES_FILE" when the PR references a linked issue: it is
required for --kind bug and optional for every other kind, rendering the
non-closing references as ## Issues. Kind-specific files passed with a
non-owning kind are rejected (--changes-file is feature-only;
--problem-file, --reproduction-file, and --fix-approach-file are
bug-only) instead of being silently dropped.
Use the released provider CLI directly. forge-cli detects the provider from
the remote; pass --provider "$PROVIDER" to pin it (github or gitlab):
forge-cli pr deliver \
--provider "$PROVIDER" \
--kind feature \
--title "$PR_TITLE" \
--body-file "$PR_BODY" \
--base main \
--method squash \
--label type::feature \
--label area::runtime \
--label size::m \
--label-catalog manifests/forge-labels.yaml \
--strict-labels \
--test-first-evidence "$EVIDENCE_DIR" \
--no-merge
When the test-first gate is enabled — [test_first].require = true in a repo
.forge-cli.toml or the user-global
${XDG_CONFIG_HOME:-~/.config}/forge-cli/config.toml — a --kind feature /
bug deliver (the create, adopt, and --dry-run preflight steps) also requires
--test-first-evidence "$EVIDENCE_DIR", pointing at the verify-clean directory
the policy-owned test-first-evidence CLI flow produces. Omit it for the exempt kinds (docs /
chore / ci / refactor); without it delivery fails closed with
test_first_evidence_required.
Run the generic code-review outcome in pre-merge mode before merge. Its minimum underlying scope is:
review-specialists scope \
--base "$BASE_REF" \
--testing \
--maintainability \
--format json
# Read native review bodies after specialist posting and repair. Current-head
# summaries are semantic evidence; stale-head summaries are informational.
PRE_SUBMIT_PR="$(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" \
--format json pr view "$PR_NUMBER"
)"
EXPECTED_REVIEW_HEAD="$(
printf '%s\n' "$PRE_SUBMIT_PR" |
jq -er 'select(.ok == true) | .data.head_sha'
)" || exit $?
readonly EXPECTED_REVIEW_HEAD
if [ "$PROVIDER" = github ]; then
PRE_SUBMIT_REVIEWS="$(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" \
--format json pr reviews "$PR_NUMBER"
)"
printf '%s\n' "$PRE_SUBMIT_REVIEWS"
printf '%s\n' "$PRE_SUBMIT_REVIEWS" |
jq -e --arg head "$EXPECTED_REVIEW_HEAD" \
'.ok == true and .data.head_sha == $head' >/dev/null
fi
# Reuse the complete selected lens set for the final provider outcome.
SELECTED_REVIEW_LENSES=(testing maintainability)
# Append every risk lens selected by review-specialists scope.
REVIEW_LENS_ARGS=()
for selected_lens in "${SELECTED_REVIEW_LENSES[@]}"; do
REVIEW_LENS_ARGS+=(--lens "$selected_lens")
done
# Native combined approval requires an environment-owned router that guarantees
# a GitHub review identity independent from the PR author. Otherwise post an
# outcome note with the same semantic decision and lenses.
FINAL_SUBMIT_REVIEW=()
case "${AGENT_RUNTIME_FORGE_IDENTITY_ROUTER_REQUIRED:-}" in
1|[Tt][Rr][Uu][Ee]|[Yy][Ee][Ss])
[ "$PROVIDER" = github ] &&
FINAL_SUBMIT_REVIEW=(--submit-review --expected-head "$EXPECTED_REVIEW_HEAD")
;;
esac
# Observed convergence is GitHub-only in v1. Preserve GitLab delivery even when
# the user's global forge-cli config enables it.
REVIEW_CONVERGENCE_ARGS=()
[ "$PROVIDER" = gitlab ] && REVIEW_CONVERGENCE_ARGS=(--review-convergence=false)
# Capture the outcome bytes once. Initial submission, guarded recovery, and
# the single retry must all use this immutable value rather than rereading a
# mutable file path. Preserve capture failures before freezing the value, and
# use `--option=value` below so hyphen-leading Markdown remains one argv value.
EXPECTED_REVIEW_BODY="$(cat "$DELIVERY_REVIEW_OUTCOME")" || exit $?
readonly EXPECTED_REVIEW_BODY
NATIVE_REVIEW_CMD=(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" --format json
pr review "$PR_NUMBER"
--decision "$REVIEW_DECISION"
"${FINAL_SUBMIT_REVIEW[@]}"
--comment="$EXPECTED_REVIEW_BODY"
"${REVIEW_LENS_ARGS[@]}"
)
# Clear stale selector state, then preserve the failed command status and JSON.
unset PENDING_REVIEW_ID
set +e
NATIVE_REVIEW_JSON="$("${NATIVE_REVIEW_CMD[@]}" 2>&1)"
NATIVE_REVIEW_STATUS=$?
set -e
if [ "$NATIVE_REVIEW_STATUS" -ne 0 ]; then
if [ "$PROVIDER" != github ] || ! printf '%s\n' "$NATIVE_REVIEW_JSON" |
jq -e '.ok == false and .error.code == "github_pending_review_exists"' \
>/dev/null; then
printf '%s\n' "$NATIVE_REVIEW_JSON" >&2
exit "$NATIVE_REVIEW_STATUS"
fi
# Fetch a fresh post-conflict pr reviews snapshot.
if [ "$PROVIDER" = github ]; then
POST_CONFLICT_REVIEWS="$(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" \
--format json pr reviews "$PR_NUMBER"
)"
PENDING_REVIEW_ID="$(
printf '%s\n' "$POST_CONFLICT_REVIEWS" |
jq -er --arg head "$EXPECTED_REVIEW_HEAD" \
--arg body "$EXPECTED_REVIEW_BODY" '
select(.ok == true and .data.head_sha == $head)
| [.data.pending_reviews[]
| select(.state == "PENDING")
| select(.commit_sha == $head)
| select(.summary_truncated == false)
| select((.summary | rtrimstr("\n")) == ($body | rtrimstr("\n")))]
| select(length == 1)
| .[0].id
'
)"
if [ -n "${PENDING_REVIEW_ID:-}" ]; then
DELETE_REVIEW_JSON="$(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" \
--format json pr pending-review delete "$PR_NUMBER" \
--review "$PENDING_REVIEW_ID" \
--expected-head "$EXPECTED_REVIEW_HEAD" \
--expected-commit "$EXPECTED_REVIEW_HEAD" \
--expected-body="$EXPECTED_REVIEW_BODY" \
--confirm-abandoned
)"
POST_DELETE_REVIEWS="$(
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" \
--format json pr reviews "$PR_NUMBER"
)"
printf '%s\n' "$POST_DELETE_REVIEWS" |
jq -e --arg head "$EXPECTED_REVIEW_HEAD" \
--arg id "$PENDING_REVIEW_ID" '
.ok == true
and .data.head_sha == $head
and (.data.pending_reviews | map(.id) | index($id) | not)
' >/dev/null
set +e
NATIVE_REVIEW_RETRY_JSON="$("${NATIVE_REVIEW_CMD[@]}" 2>&1)"
NATIVE_REVIEW_RETRY_STATUS=$?
set -e
if [ "$NATIVE_REVIEW_RETRY_STATUS" -ne 0 ]; then
printf '%s\n' "$NATIVE_REVIEW_RETRY_JSON" >&2
exit "$NATIVE_REVIEW_RETRY_STATUS"
fi
NATIVE_REVIEW_JSON="$NATIVE_REVIEW_RETRY_JSON"
fi
fi
fi
printf '%s\n' "$NATIVE_REVIEW_JSON"
# Keep merge on the same provider head that was inspected and reviewed.
forge-cli --provider "$PROVIDER" pr merge "$PR_NUMBER" --method squash \
--expected-head "$EXPECTED_REVIEW_HEAD" \
"${REVIEW_CONVERGENCE_ARGS[@]}"
If a GitHub pr review --submit-review call returns
github_pending_review_exists, preserve the failed command status and JSON,
then fetch a fresh post-conflict pr reviews result.
The command binds the native review to the inspected head with
--expected-head. From
data.pending_reviews[], recover only when exactly one current-viewer node is
the abandoned attempt for this PR and the intended body, decision, and head
are still current. Never choose a node from submitted reviews, delete multiple
nodes, or use recovery for an unrelated rejection. The executable state machine
uses only that exact node id.
The delete primitive independently verifies exact PR membership, pending state, current-viewer authorship, and delete permission. After the refreshed snapshot confirms the pending node is gone, retry the unchanged failed review once; if the guard, refresh, retry, or a second rejection fails, stop and preserve the provider error.
Map the final delivery review outcome to approve when delivery may merge and
request-changes when the review blocks. Use comments-only only for
specialist review comments or other non-decisional notes, not for the final
combined delivery-owner outcome. On GitHub, --submit-review makes this a native
pull request review event (approve→APPROVE, request-changes→REQUEST_CHANGES)
authored by the adapter-selected independent identity. Without the capability,
GitHub uses the same outcome-note path as GitLab, records the semantic decision,
and does not mutate native approval state.
For identity and issue mirroring: post a compact specialist review comment
after each reviewer lens returns and after each focused follow-up rerun. Pass
only the portable --provider, --decision, and --lens semantics; do not
set private identity-profile environment variables in this public workflow.
The active provider CLI uses ambient identity unless an environment-owned
adapter maps those semantic flags. When the PR/MR is linked to a tracking or
dispatch issue and the issue number is available, add
--issue "$ISSUE" --mirror-issue so the issue activity shows review progress
without duplicating full outcome bodies.
When the specialist report has actionable GitHub findings, include
--thread-file "$REVIEW_THREAD_FILE" on the first specialist review post that
surfaces those findings. Omit --thread-file for clean reviews, informational
notes, follow-up pass summaries, and the final combined approval outcome.
Before the final owner outcome, read native provider reviews once:
if [ "$PROVIDER" = github ]; then
forge-cli --provider "$PROVIDER" --format json pr reviews "$PR_NUMBER"
fi
On GitHub, treat data.current_head_reviews[].summary as evidence, never as a
machine verdict: repair actionable feedback, accept it with rationale, or move
it to a follow-up before posting the final combined owner outcome. Stale-head
reviews are informational. GitLab has no native snapshot in v1 and keeps the
existing specialist/outcome-note flow. If summary_truncated is true, retrieve
the full review body through provider read tooling before disposition; stop if
the full body cannot be read. Do not poll or sleep in agent instructions; the
released forge-cli pr merge owns the configured observed-bot quiet period,
timeout, complete snapshot, final recheck, native CHANGES_REQUESTED,
unresolved-thread, unchecked-task, and provider-head gates.
Observed convergence is GitHub-only in v1. On GitLab, pass
--review-convergence=false explicitly so a user-global GitHub policy does not
turn a supported MR delivery into provider_unsupported.
If merge returns review_convergence_activity_changed, read pr reviews
again, disposition the new current-head evidence, refresh the final owner
outcome, and retry. For review_changes_requested or
review_snapshot_incomplete, inspect pr reviews and stop until the native
state is cleared or complete. For unresolved_review_threads, use
forge-cli pr review-threads list, then repair, reply and resolve as accepted,
or create a follow-up and resolve with its link per
core/policies/review-thread-convergence.md. For unchecked_task_items, use
forge-cli pr tasks, then finish/check the item or rewrite it as deferred with
a follow-up ref. review_convergence_head_changed requires rebinding delivery
evidence to the new head, then re-run validation and affected review lenses,
read current-head summaries again, and post a new owner outcome before retrying.
Timeout failures require a stable provider state before retry. Bypass flags
remain exceptional and their rationale belongs in the delivery review outcome.
For linked tracking or dispatch issues, run a pre-merge lifecycle audit before
the merge. This is not closeout yet, because record close verifies the merged
PR/MR after merge:
forge-cli --provider "$PROVIDER" --repo "$OWNER_REPO" --format json \
issue view "$ISSUE" --with-comments >"$ISSUE_VIEW_JSON"
jq '{body:.data.body, comments:(.data.comments // [])}' \
"$ISSUE_VIEW_JSON" >"$ISSUE_JSON"
jq -r .body "$ISSUE_JSON" >"$ISSUE_BODY"
plan-issue --format json record audit \
--profile "$PROFILE" \
--body-file "$ISSUE_BODY" \
--comments-json "$ISSUE_JSON"
Stop if the audit lacks session evidence, if the latest state is not
complete, or if the dashboard still shows Latest session: pending.
Run linked issue closeout after merge when the body references a tracking or
dispatch issue via Refs #<issue> and --no-closeout was not supplied. Use the
provider-correct linked record ref: $OWNER_REPO#$PR_NUMBER on GitHub,
$OWNER_REPO!$MR_NUMBER on GitLab:
plan-issue --repo "$OWNER_REPO" --format json record close \
--issue "$ISSUE" \
--profile "$PROFILE" \
--linked-pr "$LINKED_RECORD_REF" \
--approval "$APPROVAL" \
--bundle "$PLAN_BUNDLE" \
--add-label state::closed \
--remove-label state::needs-triage
Use profile=tracking for lightweight plan-tracking issues and
profile=dispatch for dispatch plan records.
semantic-commit.forge-cli pr close and stop before delivery or local pre-PR
validation..agents/scripts/pre-pr.sh is executable, run it through the repository
dispatcher and stop on failure.Refs #<issue> until record close has passed.agent-runtime pr-body render.references/pr-lifecycle.md for the shared taxonomy rule.manifests/forge-labels.yaml exists, validate labels with the
appropriate forge-cli label surface before the first live delivery.forge-cli pr create, return the provider URL, and
stop. Otherwise run forge-cli pr deliver with selected --label flags,
--label-catalog manifests/forge-labels.yaml when present, and
--no-merge so checks / pipelines complete before the mandatory review gate.forge-cli pr review
(a native COMMENT review event via --submit-review on GitHub)
with --decision comments-only and that semantic --lens. The parent
delivery workflow posts; reviewer
subagents never call the provider. Post the moment each lens returns — before
the repair in step 11, never batched after it; the comment is the finding the
step-11 fix responds to, so it must exist first (see
REVIEW_OUTCOME_POSTING_CONTRACT.md, posting order). On GitHub, attach
--thread-file for actionable findings so the fix can close a native review
thread; summary-only reviews omit it.forge-cli pr reviews once after specialist repairs and
semantically disposition every actionable current-head summary; stale-head
reviews are informational. When summary_truncated is true, obtain the full
review body and stop if it is unavailable. On GitLab, retain the outcome-note
path and do not invoke the unsupported snapshot. Do not implement a polling
or sleep loop in the workflow.forge-cli pr review before merge. Use
the final --decision and repeat every selected --lens; add native GitHub
approval only through the declared independent-identity capability, and keep
identity selection outside the public skill. If native submission
returns github_pending_review_exists, use the exact-node
pending_reviews recovery above and retry the unchanged outcome once; do
not delete ambiguous drafts or downgrade the outcome to a note.role=session, validation, review, and dashboard links are
present. If not, stop and route to the matching plan delivery workflow.forge-cli --provider "$PROVIDER" pr merge "$PR_NUMBER" unless
--no-merge is the requested final stop. The CLI owns observed quiet
timing, complete/final native-review reads, native change requests,
thread/task gates, and head CAS. On
review_convergence_activity_changed, re-read pr reviews, disposition
the new evidence, refresh the final owner outcome, and retry. Route other
typed review/thread/task failures through the matching read surface and the
same repair/accept/follow-up discipline before retrying.
review_convergence_head_changed additionally requires delivery-evidence
rebinding, validation and affected-review reruns, and a new owner outcome on
the new head before retry.--no-closeout was not supplied, run plan-issue record close with
the correct profile. On gate fail, leave the issue open with the blocked
code surfaced by plan-issue and route to the matching closeout skill.core/policies/git-delivery.md terminal cleanup. Recheck
status and provider merge/head truth. Restore a clean primary checkout to
base, or invoke git-cli worktree remove <path-or-slug> --format json from
the primary checkout through the supported hooked shell; the target-aware
lease guard must confirm no live foreign owner before removal. If that proof
or hook is unavailable, retain the worktree. Delete the local
branch only when its tip equals the provider-confirmed delivered head;
otherwise retain and report it. If an outer L2/L3 workflow remains, hand it
the captured identity and defer this step.forge-cli owns provider create, checks/pipeline wait, ready, native-review
convergence, thread/task enforcement, provider-head binding, and merge calls.
plan-issue record owns linked issue lifecycle closeout. The workflow owner
owns scope judgment, code changes, local validation, pre-merge gate decisions,
repair loops, delivery outcome comments, and any temporary provider fallback
decision. The outermost workflow also owns terminal local cleanup after all
downstream duties; child delivery workflows hand off rather than clean early.
Provider auto-close keywords against issue-backed plan records remain banned.