Instruction file imported from softwaresalt/docline (
.github/instructions/github-pr-automation.instructions.md). Copyright stays with the author.
GitHub PR Automation Instructions
These instructions define the GitHub-specific automation protocols for
pull request review and CI check monitoring. They extend the general
pull-request.instructions.md and ci-security.instructions.md with
concrete GitHub API operations, polling cadences, and comment lifecycle
management.
Scope
These instructions apply when the target workspace is hosted on GitHub
and agents interact with pull requests via GitHub MCP tools or the gh
CLI. Agents MUST follow these protocols for all PR review polling,
comment addressing, and CI status monitoring.
Part 1: Optional Shadow Review Automation
1.1 Request Shadow Review
After creating or updating a PR, optionally request a Copilot shadow review during the migration period:
Tool: mcp_github_request_copilot_review
owner: softwaresalt
repo: docline
pullNumber: <pr_number>
If the MCP tool is unavailable, use a repository-approved GitHub API or CLI
wrapper only when one is explicitly configured for Copilot review requests. Do
not treat gh pr edit --add-reviewer as a required fallback for Copilot review:
GitHub CLI reviewer flags do not reliably support Copilot's special reviewer
identity across versions. When no supported fallback is configured, record
shadow review as unavailable/advisory and continue to the local readiness gate.
Shadow review is advisory by default. It can help catch residual issues after the local review gate, but it is not a required dependency for merge readiness unless the operator explicitly elevates it for the current PR.
Conditional hard gate when Copilot review is enabled (P-018). When Copilot
review IS enabled for this PR — Copilot is a requested reviewer, a Copilot review
already exists on the PR, or the workspace sets copilot_review.enforcement: required — shadow review stops being advisory and becomes a fail-closed
pre-merge gate. Merge (including --admin) is held until Copilot has completed a
review for the current headRefOid AND every Copilot-authored review thread is
resolved. This is enforced deterministically by
autoharness gate copilot-review <pr> --repo <owner/name> (§1.9.4, Check 5), not by
prose: a BLOCK verdict is a hard stop that --admin does not bypass. The gate is
not-applicable (PASS) only when enforcement is auto and Copilot never engaged
on the PR, or when enforcement is disabled — this is how "advisory by default"
is preserved for repositories where Copilot review is not in play.
When DARK_MODE_ACTIVE is present under P-017, keep the workflow local-review-first:
the current-HEAD local review readiness record is the authoritative merge gate,
and Copilot/GitHub-hosted review remains optional advisory shadow review unless
the dark-mode activation contract or operator explicitly elevates it. A clean
shadow review cannot compensate for stale or missing local readiness, and shadow
review timeout or unavailability does not block by default. However, once Copilot
review is actually engaged on the PR (or copilot_review.enforcement: required),
the P-018 copilot-review gate still applies in dark mode: an incomplete review for
the current HEAD or unresolved Copilot threads is a COPILOT_REVIEW_BLOCK that
admin fallback may never bypass (§1.9.4, §1.9.6).
1.2 Poll for Review Completion
Copilot shadow review typically completes within 2–5 minutes. Use a back-off polling strategy:
| Attempt | Wait before poll | Cumulative wait |
|---|---|---|
| 1 | 2 minutes | 2 min |
| 2 | 2 minutes | 4 min |
| 3 | 3 minutes | 7 min |
| 4 | 3 minutes | 10 min |
| 5 | 5 minutes | 15 min |
Poll mechanism — use the MCP tool to read review comments:
Tool: mcp_github_pull_request_read
owner: softwaresalt
repo: docline
pullNumber: <pr_number>
Inspect the returned reviews and review comments. Copilot shadow-review
comments are identified by the author login copilot-pull-request-reviewer[bot]
or similar bot author association.
Completion signal: Treat any Copilot-authored review with state != PENDING
as complete, including COMMENTED, CHANGES_REQUESTED, and APPROVED.
Review comments attached to a non-PENDING review also count as completion.
Timeout: If no Copilot review appears after 15 minutes (5 poll attempts), proceed without it. Log a warning and note in the PR description that shadow review was unavailable.
1.3 Categorize Shadow Review Comments
For each Copilot shadow-review comment, classify it:
| Category | Criteria | Action |
|---|---|---|
| Valid | Comment identifies a real issue confirmed by local analysis | Fix the code |
| Partial | Comment is partially correct or overly broad | Fix the valid part, reply with explanation |
| Invalid | Comment is a false positive or stylistic disagreement | Decline with rationale |
| Informational | Comment is a suggestion, not a defect | Acknowledge, apply if low-risk |
1.4 Address and Fix Comments
For each comment requiring a fix:
- Understand context: Read the file and surrounding code referenced
by the comment's
pathandline/start_linefields. - Apply the fix: Make the minimal targeted change that resolves the issue without introducing scope creep.
- Verify locally: Run the relevant quality gate
(
ruff format --check .,ruff check .,pytest) to confirm the fix doesn't break anything. - Commit: Use a
fix:conventional commit referencing the comment (e.g.,fix: address copilot review — null check on user input). - Full-build gate before PR update: If the fix adds, removes, or changes source code, run the full local build successfully before pushing. If the fix is documentation-only or backlog-only, record full-build non-applicability. Halt on missing or failed build evidence.
- Push: Push the fix commit before posting any "fixed" reply or resolving the review thread, so the reviewer and PR timeline can see the referenced fix.
1.5 Reply to Addressed Comments
After the fixing commit has been pushed, reply to the review thread:
Tool: mcp_github_add_reply_to_pull_request_comment
owner: softwaresalt
repo: docline
pullNumber: <pr_number>
commentId: <comment_id>
body: "Fixed in <commit_sha>. <brief description of the fix>"
For declined comments, reply with the rationale:
body: "Declined — <rationale>. The current implementation <explanation>."
For partial comments:
body: "Partially addressed in <commit_sha>. Applied: <what was fixed>.
Not applied: <what was declined and why>."
Shell-Safe Comment Body Construction
Reply and resolution bodies frequently contain markdown code spans and shell
metacharacters. On PowerShell, inlining such a body in a double-quoted
gh api ... -f body="..." argument corrupts the posted text — this is the root
cause of the "escape-character typos" seen in resolution comments. (PowerShell is
the observed root cause here; Bash/Zsh have the equivalent hazard — backticks
perform command substitution and $ expands variables inside double quotes.)
Why it corrupts (the shell pre-processes the string before gh sees it):
- The backtick (
`) is PowerShell's escape character. Review replies almost always contain code spans such as`types`or`--strict`; PowerShell consumes the backticks and may escape the following character, dropping or mangling the code-span text. $triggers variable expansion, so$resultor$PATHin a body expands to an empty or unexpected value.- An embedded double quote terminates the argument early.
The corruption happens the moment the body is placed in a double-quoted string, so do not even construct it that way. Author the body with a single-quoted here-string, a structured tool argument, or a pre-written file.
Required practice:
-
Prefer the structured tool.
mcp_github_add_reply_to_pull_request_commentpassesbodyas a structured argument with no shell parsing, so markdown is preserved verbatim. Use it whenever available. -
File-backed body (safe
gh apifallback). Write the exact body to a BOM-less UTF-8 file, then pass it by reference with--field/-F, whose@filevalue is read as a raw string (no$/backtick interpolation and notrue/false/null/number type coercion):# PS7+ accepts -Encoding utf8NoBOM; Windows PowerShell 5.1 -Encoding utf8 # writes a BOM (a stray U+FEFF at the start of the comment), so use the .NET # writer for a BOM-less file that works on both: [System.IO.File]::WriteAllText((Join-Path $PWD 'reply-body.md'), $bodyText, (New-Object System.Text.UTF8Encoding $false)) gh api repos/softwaresalt/docline/pulls/<pr>/comments/<id>/replies ` -X POST --field body=@reply-body.md Remove-Item reply-body.mdUse
--field/-F, not-f/--raw-field:-f body=@filesends the literal text@file. The same--field body=@filereference works for anygh api graphqlmutation that carries a markdown body. -
Single-quoted here-string (when authoring inline). Single-quoted PowerShell strings are literal — no backtick escaping and no
$expansion:$bodyText = @' Fixed in <commit_sha>. Removed the unused `types` import. '@The one caveat is a body line that is exactly
'@at column 0, which closes the here-string early; for arbitrary markdown, write it to a file (pattern 2).
Never hand-escape or strip markdown backticks to make a command run — that corrupts the very text being posted. Change the transport, not the content, and keep review-reply bodies verbatim.
1.6 Resolve Review Threads
After the fixing commit has been pushed and the explanatory reply has been
posted, resolve the review thread using the
GitHub GraphQL API. There is no REST endpoint or MCP tool for thread
resolution — use gh api graphql:
gh api graphql -f query='
mutation ResolveThread($threadId: ID!) {
resolveReviewThread(input: { threadId: $threadId }) {
thread { isResolved }
}
}
' -f threadId="<thread_node_id>"
Obtaining the thread node ID: When reading PR review comments via
MCP or REST, each review comment includes a node_id field (the
GraphQL global ID). For threaded review comments, query the thread:
gh api graphql -f query='
query GetThreads($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 1) {
nodes { body path line }
}
}
}
}
}
}
' -f owner="softwaresalt" -f repo="docline" -F pr=<pr_number>
Match threads to addressed comments by path and line, then resolve
each thread using its id.
Rules:
- Only resolve threads for comments that have been fixed or explicitly declined with a rationale reply.
- Never resolve threads without first posting a reply explaining the resolution.
- Never resolve threads authored by human reviewers — only bot-authored threads (Copilot, linters, etc.) may be auto-resolved.
1.7 Push Fixes and Re-request Shadow Review
After all addressable comments are handled:
-
Confirm the fix commits have been pushed to the branch. Code-changing fix commits must already have successful full local build evidence; documentation-only/backlog-only fixes must record non-applicability.
-
Re-request Copilot shadow review if new code was pushed:
Tool: mcp_github_request_copilot_review owner: softwaresalt repo: docline pullNumber: <pr_number> -
Poll again per Section 1.2 to verify the new review is clean.
When DARK_MODE_ACTIVE is present and the operator is AFK, continue this
comment-handling loop autonomously within the review-fix cycle limits. Every
actionable bot comment must receive a reply after the fix commit is pushed, and
every fixed or explicitly declined bot-authored thread must be resolved via
GraphQL before merge readiness is presented.
P-021 Scope Classification and Out-of-Scope Disposition: Before applying any fix in this autonomous comment-handling loop, classify EVERY comment against the P-021 C1 same-contract-surface test (see also the operational restatement in the circuit-breaker instruction's "Review-Fix Cycle Definition" section). Only a comment that passes C1 — fixing it requires ONLY completing the exact change already authorized — may be fixed directly. Every other comment is out of scope and MUST follow the disposition sequence below instead of being fixed.
Out-of-scope disposition is a REQUIRED ORDERED, capture-first sequence:
- (a) Capture per P-021 C2 with the full payload — the literal
DEFERRED SCOPE EXPANSIONtoken, a one-sentence expansion statement, the C1-cited out-of-scope rationale, source refs (PR number, review-thread ID, task ID, feature ID, shipment ID), arequires deliberationflag, and kind plus a PROVISIONAL priority — performed BEFORE any thread reply, because C2 makes capture a precondition for closing the finding. - (b) Post a substantive thread reply explaining the finding, why it is out of scope citing the C1 boundary, that no code change was made, and CITING THE GENERATED DEFERRED ENTRY ID returned by the (a) capture, per P-021 C3.
- (c) Resolve the thread via the review-thread resolution mechanism (Section 1.6), permitted only after the reply citing that ID is posted.
- (d) Add a residual-risk record entry in the PR body naming the SAME deferred entry ID.
Replying to or resolving an out-of-scope thread BEFORE the C2 capture exists is PROHIBITED, since the reply cannot cite an entry ID that has not been generated yet; a reply omitting the deferred entry ID does not satisfy C3. This ordering matches the Ship agent's defer-capture sequence (capture → reply citing the ID → resolve → residual-risk record) — the two surfaces MUST NOT diverge.
This loop sets only a PROVISIONAL priority at capture; re-prioritization and triage remain Stage-only, per the P-021 C5 capture-only carve-out.
This disposition satisfies P-018's zero-unresolved-threads requirement honestly — every thread receives a substantive disposition, not a bare acknowledgement — without fabricating unauthorized scope.
This classification gate does not change the autonomy language above: an autonomous, unattended loop may still resolve every actionable comment without operator intervention, it simply resolves out-of-scope comments through capture and reference rather than through a fix.
1.8 Stop Conditions for Shadow-Review Cycles
| Counter | Limit | Action |
|---|---|---|
| Review-fix-push cycles | 3 | For each remaining comment that FAILS P-021 C1: accept as a backlog follow-up via a full P-021 C2 capture (not an informal note). An in-scope comment unresolved solely because this cycle budget is exhausted is NOT captured this way — see the P-021 C4 annotation below. |
| Same comment re-raised after fix | 2 | Escalate to operator — likely a fundamental disagreement |
P-021 C4 annotation: reaching the review-fix-push cycle limit does not
authorize expanding into an out-of-scope comment, and neither does an operator
instruction to continue. "Accept remaining comments as backlog follow-ups"
means each remaining out-of-scope comment receives the full P-021 C2 capture
above, not an informal note. Operator authorization at the limit can only open
a SEPARATE work unit through P-021 C2 capture plus C6 Stage deliberation — it
never makes the expansion in-scope for the cycle already in flight (P-021 C4).
An in-scope comment (one that PASSES P-021 C1) left unresolved purely because
this cycle-count budget is exhausted is a different case: it is never captured
as a DEFERRED SCOPE EXPANSION entry (it was never out of scope), and per the
P-021 C3 symmetric guard it MUST NOT be silently closed as a backlog
follow-up either — halt this comment instead and surface it to the operator
for explicit disposition (extend the cycle-count limit, or explicitly accept
documented residual risk) before the PR is presented as merge-ready.
Cycle limits do not make shadow review merge-blocking by default — except when Copilot review is enabled (P-018). When the review-fix cycle limit is reached and Copilot review is NOT enabled for the PR, unresolved Copilot shadow-review comments must be surfaced explicitly in the readiness summary and converted into follow-up items or operator-visible residual-risk notes. They only remain merge-blocking if the operator explicitly elevated shadow review to blocking status for the current PR.
When Copilot review is enabled (P-018), exhausting the cycle limit with
unresolved Copilot-authored threads is a BLOCK, not an "accept as follow-up":
autoharness gate copilot-review returns UNRESOLVED_THREADS and the merge is
held. Escalate to the operator rather than merging. The bounded --max-wait
REVIEW_TIMEOUT outcome is likewise a BLOCK; only an audited --force overrides it.
1.9 Pre-Merge Review Readiness Verification (Defense in Depth)
This gate is a NON-NEGOTIABLE pre-merge verification that runs independently of shadow review. Even if the local review gate in the ship workflow reported success earlier, this step re-checks from scratch that the PR still reflects a current-HEAD local review result before any merge is presented as ready or executed.
This gate applies to all pull requests created or merged by the Ship agent: feature PRs, chore PRs, and post-merge closure PRs. There is no exception for "small" or "hygiene" PRs. Every merge requires a fresh local review readiness record covering the current HEAD. Shadow review is optional and advisory by default.
In dark mode, this gate is still local-review-first: unresolved local P0/P1
findings block merge, READY_WITH_FOLLOWUPS is allowed only when follow-up item
IDs or explicit residual-risk notes are recorded, and advisory shadow-review
comments are surfaced as follow-ups unless elevated by policy or operator.
1.9.1 Readiness Query
Run a single GraphQL query to fetch PR head SHA, PR body, review decision, shadow-review requests/reviews, and review threads:
gh api graphql -f query='
query PRReviewReadiness($owner: String!, $repo: String!, $pr: Int!, $threadCursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
headRefOid
body
reviewDecision
reviewRequests(first: 100) {
nodes {
requestedReviewer {
__typename
... on Bot { login }
... on User { login }
... on Team { name }
}
}
}
reviews(last: 50) {
nodes {
author { login }
state
submittedAt
commit { oid }
}
}
reviewThreads(first: 100, after: $threadCursor) {
nodes {
id
isResolved
comments(first: 1) {
nodes { author { login } body path line }
}
}
pageInfo { hasNextPage endCursor }
}
}
}
}
' -f owner="softwaresalt" -f repo="docline" -F pr=<pr_number> -f threadCursor=""
If pageInfo.hasNextPage is true, re-run the query with
-f threadCursor="{endCursor}" and merge the reviewThreads.nodes
results. Repeat until hasNextPage is false. Do not skip
pagination — a hard gate that misses operator-visible review data is unsafe. If
pagination cannot complete (API error, rate limit), fail closed and
halt rather than declaring readiness.
1.9.2 Local Readiness Record
The PR description or other operator-visible readiness summary MUST contain a local review block for the current HEAD. Use this format or an equivalent machine-readable variant:
## Local Review Readiness
- Reviewed HEAD: `<sha>`
- Outcome: `READY` | `READY_WITH_FOLLOWUPS` | `BLOCKED`
- Blocking findings: `P0=0, P1=0`
- Full local build: `<command and successful result>` | `not applicable — <docs/backlog-only rationale>`
- Follow-ups: `none` | `<item ids or residual-risk notes>`
- Shadow review: `not requested` | `requested` | `clean` | `comments pending`
If the workspace uses a different readiness artifact, the PR body must still contain the reviewed HEAD SHA, the outcome, successful full local build evidence (or explicit non-applicability), and the follow-up handling summary so the merge gate can confirm the current PR state without relying on hidden local state.
1.9.3 Advisory Bot Identity
The Copilot review bot appears under different login strings depending on the API surface:
| API context | Login string |
|---|---|
GraphQL Bot.login (reviews, reviewRequests) |
copilot-pull-request-reviewer (no [bot] suffix) |
REST review.user.login |
copilot-pull-request-reviewer[bot] |
REST timeline requested_reviewer.login |
Copilot (display form) |
When matching in GraphQL responses, use copilot-pull-request-reviewer
(without [bot]). When matching in REST responses, use
copilot-pull-request-reviewer[bot]. For review thread comments
returned via GraphQL, the author.login field uses the no-suffix form.
1.9.4 Gate Checks
Evaluate five checks in order. All five must pass for merge readiness.
Check 1 — Local review coverage (record covers current HEAD):
- Record
headRefOidfrom the query response. - Parse the PR
bodyfor the## Local Review Readinessblock (or the equivalent repository-approved marker). - Extract
Reviewed HEAD. - If the block is missing or
Reviewed HEADdoes not matchheadRefOid, the local review is stale or absent. Halt and require the caller to rerun local review for the current HEAD.
Check 2 — Local readiness outcome (no unresolved blocking findings):
- Extract
OutcomeandBlocking findingsfrom the local readiness block. - If
OutcomeisBLOCKED, GATE FAILS. Halt and report the blocking local review. - If
Blocking findingsreports any unresolved P0 or P1 findings, GATE FAILS even if the outcome string is malformed or overly optimistic. - If
OutcomeisREADYorREADY_WITH_FOLLOWUPSand blocking findings are clear, proceed to Check 3.
Check 3 — Follow-up handling is explicit:
- If
OutcomeisREADY, theFollow-upsfield may benone. - If
OutcomeisREADY_WITH_FOLLOWUPS, theFollow-upsfield must list follow-up item IDs, queued backlog work, or explicit residual-risk notes. - If the field is missing or empty for
READY_WITH_FOLLOWUPS, GATE FAILS. - Otherwise, proceed to Check 4.
Check 4 — Full local build evidence for code-changing PRs:
- If the PR adds, removes, or changes source code, the readiness block must list the full local build command and a successful result.
- If the PR is documentation-only or backlog-only, the readiness block may state
Full local build: not applicablewith a short rationale. - If build applicability is ambiguous, required evidence is missing, or the recorded full local build result failed, GATE FAILS.
- Otherwise, proceed to Check 5.
Check 5 — Copilot-review completion & thread resolution (P-018, fail-closed):
- Determine
copilot_review.enforcementfrom.autoharness/workspace-profile.yaml(auto|required|disabled, defaultauto) andcopilot_review.max_wait_seconds(integer ≥ 0, default0). - Run the deterministic gate:
autoharness gate copilot-review <pr_number> --repo softwaresalt/docline --enforcement <mode> [--max-wait <max_wait_seconds>]. - Interpret the verdict / exit code:
SATISFIEDorNOT_APPLICABLE(exit 0) — Copilot review is complete for the current HEAD with no open Copilot threads, or Copilot is not in play. Proceed.WAITING_FOR_REVIEW,UNRESOLVED_THREADS,REVIEW_TIMEOUT,DETECTION_AMBIGUOUS, orVERIFY_FAILED(non-zero) — GATE FAILS. Halt.--admindoes not bypass this. Wait for review completion, resolve every Copilot thread, then re-run.REVIEW_TIMEOUTstill blocks; only an explicit, auditedautoharness gate copilot-review ... --force(logged under.autoharness/gates/) may override, and only with operator authority.
- Only when the gate returns a PASS verdict (or an audited
--forceis recorded) does the readiness gate reach GATE PASSES. The PR is ready for merge presentation.
Human review threads (Check 5 precedence): Human review threads are surfaced
in the merge-readiness summary but do not block this local-readiness gate by
default. Copilot-authored threads are not advisory here: an existing
Copilot review is itself an engagement signal, so Check 5 (P-018) takes precedence
and every unresolved Copilot-authored thread BLOCKS the merge — the legacy
"advisory shadow-review does not block" rule never applies once Copilot is engaged.
However, if the repository has branch
protection rules requiring conversation resolution, approved reviews,
or if a human reviewer submitted a CHANGES_REQUESTED review, those
constraints may independently block the merge at the GitHub level. The
reviewDecision field from the query reflects the overall PR review
decision (APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED, or null)
and should be reported in the merge-readiness summary.
1.9.5 Terminal States
| Condition | Action |
|---|---|
| Local review block missing from PR body | Halt. Report that readiness evidence is absent. |
| Local review block references the wrong HEAD SHA | Halt. Report stale review and current HEAD SHA to operator. |
Local readiness outcome is BLOCKED or blocking findings remain |
Halt. List blocking findings. Do not proceed to merge. |
READY_WITH_FOLLOWUPS omits follow-up handling |
Halt. Report missing follow-up IDs or residual-risk notes. |
| Code-changing PR omits successful full local build evidence | Halt. Run the full local build successfully or explain non-applicability only for documentation-only/backlog-only work. |
Copilot review enabled but incomplete for HEAD, or Copilot threads unresolved (autoharness gate copilot-review returns non-zero) |
Halt (P-018). Wait for Copilot review completion and resolve every Copilot thread. --admin does not bypass. REVIEW_TIMEOUT blocks; only an audited --force overrides. |
Shadow review unavailable or still pending, Copilot not engaged (gate returns NOT_APPLICABLE) |
Warning. Note in PR summary. Shadow review remains advisory when Copilot is not in play. |
| All 5 checks pass | Ready. Present PR for merge approval. |
Shadow-review timeout does not fail this gate when Copilot is not engaged; the required dependency is local review coverage for the current HEAD. If the operator wants shadow review to become merge-blocking for a specific PR, that escalation must be explicit. When Copilot review IS engaged, the §1.9.4 Check 5 copilot-review gate is an additional fail-closed dependency (P-018).
1.9.6 Dark-Mode Merge Authorization and Admin Fallback
When DARK_MODE_ACTIVE is present under P-017, the activation record may satisfy
the P-014 operator approval signal only when all of these are true:
- The PR is inside the recorded dark-mode
scope. merge_approval_pre_authorizedistrue.- The §1.9 local readiness gate passed for the current
headRefOid. - Required CI/checks are green or explicitly marked non-applicable.
- P-009 merge-commit-only and P-016 worktree topology checks passed.
- The §1.9.4 Check 5 copilot-review gate returned a PASS verdict for the current
headRefOid(or an audited--forceoverride is recorded). ACOPILOT_REVIEW_BLOCKis never satisfied by the activation record.
If any condition is false or ambiguous, fail closed and wait for an explicit operator approval signal.
Before any admin fallback, attempt the normal merge path first and classify the result:
| State | Meaning | Dark-mode action |
|---|---|---|
NORMAL_MERGE_READY |
Normal merge can proceed with merge commit strategy | Merge normally; record DARK_MODE_MERGE_AUTHORIZED when approval came from the activation record |
REVIEW_REQUIRED_BLOCK |
Branch protection rejected merge for required review approval | Admin fallback may be attempted only if admin_fallback_pre_authorized is true |
CONVERSATION_RESOLUTION_BLOCK |
Branch protection requires unresolved conversations to be resolved | Admin fallback may be attempted only if explicitly covered by admin_fallback_pre_authorized; otherwise halt |
CHECKS_BLOCK |
Required checks are failed, pending, missing, or not explicitly non-applicable | Halt; admin fallback is forbidden |
MERGE_STRATEGY_BLOCK |
Merge commit strategy is unavailable or squash/rebase is selected | Halt under P-009; admin fallback is forbidden |
MISSING_ADMIN_RIGHTS |
Admin fallback was authorized but credentials lack bypass rights | Halt with an operator-visible reason |
UNKNOWN_MERGE_BLOCK |
The merge rejection cannot be classified confidently | Halt; do not guess or bypass |
COPILOT_REVIEW_BLOCK |
The P-018 copilot-review gate returned a BLOCK verdict (review incomplete for HEAD, unresolved Copilot threads, timeout, or unverifiable) | Halt; admin fallback may never bypass this. Resolve via review completion + thread resolution, not --admin. |
Admin fallback cannot bypass stale local readiness, unresolved local P0/P1
findings, failed required CI/checks, a P-018 COPILOT_REVIEW_BLOCK, P-009, P-016,
secrets-safety concerns, or scope mismatch. Every normal merge attempt and admin
fallback attempt must be recorded in the PR readiness/merge summary with the state,
decision, command/API used, and result.
1.10 Post-Merge Closure PR Shadow Review Surveillance
When the Ship agent creates a dedicated post-merge closure branch and PR:
- Optionally request Copilot shadow review per §1.1 immediately after PR creation.
- Poll per §1.2 back-off cadence.
- Apply the full §1.3–§1.7 fix cycle for any comments raised.
- Run §1.9 local readiness gate before presenting the post-merge closure PR for merge.
- Obtain explicit operator approval before merging the post-merge closure PR.
Post-merge closure PRs are not exempt from the P-014 gate. The operator must approve each merge individually — approval for the main PR does not carry over to the post-merge closure PR.
Part 2: CI Check Monitoring
2.1 Wait for CI Checks to Start
After pushing commits or creating a PR, CI checks may take 10–30 seconds to initialize. Wait at least 30 seconds before the first status poll.
2.2 Poll CI Check Status
Use the MCP tool to read check run status:
Tool: mcp_github_pull_request_read
owner: softwaresalt
repo: docline
pullNumber: <pr_number>
Alternatively, use the gh CLI for more granular check-run data:
gh pr checks <pr_number> --watch --fail-fast
Or query check runs directly:
gh api repos/softwaresalt/docline/commits/<head_sha>/check-runs \
--jq '.check_runs[] | {name, status, conclusion}'
2.3 Polling Cadence for CI
| Attempt | Wait before poll | Cumulative wait |
|---|---|---|
| 1 | 30 seconds | 30 sec |
| 2 | 1 minute | 1.5 min |
| 3 | 2 minutes | 3.5 min |
| 4 | 2 minutes | 5.5 min |
| 5 | 3 minutes | 8.5 min |
| 6 | 3 minutes | 11.5 min |
| 7 | 5 minutes | 16.5 min |
| 8+ | 5 minutes | +5 min each |
Timeout: If checks have not completed after 30 minutes, halt polling and report to the operator. Do not wait indefinitely.
2.4 Interpret Check Results
Parse check run results into actionable categories:
| Conclusion | Meaning | Action |
|---|---|---|
success |
Check passed | No action needed |
failure |
Check failed with actionable errors | Invoke fix-ci protocol |
cancelled |
Check was cancelled (often by a newer push) | Re-trigger if needed |
timed_out |
Check exceeded its time limit | Investigate resource issues, re-trigger once |
action_required |
Check needs manual intervention (e.g., security review) | Report to operator |
skipped |
Check was skipped by condition | Verify the skip was expected |
neutral |
Informational check | Log and continue |
stale |
Check is outdated (superseded by newer commit) | Ignore, newer checks are authoritative |
2.5 Extract Failure Details
When a check fails, extract the failure details for diagnosis:
gh api repos/softwaresalt/docline/check-runs/<check_run_id>/annotations \
--jq '.[] | {path, start_line, end_line, annotation_level, message}'
Check annotations provide file paths, line numbers, and error messages that map directly to code locations — use these for targeted fixes.
For checks without annotations, retrieve the log output:
gh run view <run_id> --log-failed
2.6 Fix-Push-Poll Loop
After diagnosing and fixing CI failures:
- Run the failing checks locally first (per fix-ci skill protocol).
- If the fix adds, removes, or changes source code, run the full local build successfully before pushing. If the fix is documentation-only or backlog-only, record full-build non-applicability. Halt on missing or failed build evidence.
- Commit and push the fix.
- Wait for CI to re-trigger (Section 2.1 timing).
- Poll for new check results (Section 2.3 cadence).
- Repeat until all checks pass or circuit breaker triggers.
2.7 CI Circuit Breakers
| Counter | Limit | Action |
|---|---|---|
| Fix-push-poll iterations | 5 | Halt, leave PR for manual intervention |
| Same check fails 3 times consecutively | 3 | Halt that check's fix loop, report systematic failure |
| Total CI wait time | 30 minutes per cycle | Halt polling, report timeout |
Part 3: Combined Review + CI Workflow
When optional shadow review and CI checks are active on the same PR, follow this sequencing:
- Push code → triggers both CI and optional Copilot shadow review.
- Poll CI status (Section 2.2) — CI results usually arrive first.
- Poll shadow review (Section 1.2) — review typically takes 2–5 min.
- Fix CI failures first — CI failures are typically more mechanical and faster to resolve.
- Address review comments — may overlap with CI fixes. If a review comment targets the same code as a CI failure, fix once and reference both in the commit message.
- Push combined fixes → re-triggers both CI and review.
- Resolve addressed threads (Section 1.6) — only after fixes are pushed and replies posted.
- Final verification poll — confirm CI is green and shadow review, if requested, is summarized.
- Pre-merge readiness gate (Section 1.9) — run the defense-in-depth GraphQL verification to confirm the local review readiness record covers the current HEAD and that any residual follow-up handling is explicit. This gate runs even if step 8 reported clean status.
Interaction with fix-ci Skill
When the pr-lifecycle skill delegates to fix-ci, the fix-ci skill SHOULD follow the CI polling protocol in Part 2 of this document rather than ad-hoc polling. The review comment handling in fix-ci Step 3/Step 6 SHOULD follow Part 1 of this document for GitHub-hosted repositories.
Interaction with pr-lifecycle Skill
The pr-lifecycle skill's Step 3 (handle review feedback) SHOULD follow Part 1 of this document for the optional shadow-review workflow on GitHub-hosted repositories. The pr-lifecycle skill's Step 4 (handle CI failures) SHOULD reference Part 2 for GitHub-specific polling and failure extraction.
Environment Detection
These instructions apply when the repository is hosted on GitHub. Agents detect this via:
- Git remote URL containing
github.com - Presence of GitHub repository metadata or tooling under
.github/
For GitHub-hosted repositories:
- Part 1 (optional shadow-review polling, Copilot Review handling, and comment
lifecycle management) applies whenever agents interact with pull
requests via GitHub MCP tools or the
ghCLI. - Part 2 (CI polling and check monitoring) applies when the workspace
CI platform is GitHub Actions. Agents MAY detect that via:
- Presence of
.github/workflows/directory GitHub Actionsresolving toGitHub Actions
- Presence of
When the repository is not on GitHub, these instructions do not apply.
Fall back to the generic CI and PR protocols in ci-security.instructions.md
and pull-request.instructions.md.
Generated by autoharness | Template: github-pr-automation.instructions.md.tmpl