Skip to content

Workflow Publish and Finalize Resilience

Compatibility note: this page now uses the provider-neutral workflow contract. GitHub PR publication can be automated. Azure DevOps, local, and unsupported change-request publication return explicit manual or blocked provider states instead of success-shaped non-github output.

workflow-publish and workflow-finalize classify already-terminal states before taking PR actions. Re-running a workflow after a merge, after an already-created PR, or on a branch with no diff produces a successful terminal outcome instead of creating duplicate PRs or failing on already-finished work.

Finalization is agentic only where judgment is useful, and that judgment never drives control flow. Deterministic steps collect Git/PR/CI/implementation/ verification evidence into typed recipe state, classify the terminal state from that typed evidence, persist normalized terminal fields, and return the process exit code. The agentic finalizer contributes a human-readable narrative artifact only; it is never parsed. A reporting step that fails after a successful implementation classifies as FAILED_REPORTING with durable evidence preserved, and malformed typed evidence fails closed as FAILED_INVALID_EVIDENCE.

Observed failure modes

The resilience model exists because recent workflow runs and regression tests exposed these concrete failure modes:

Failure mode Typical symptom Resilient behavior
Brittle parsing A shell step inferred success from a PR URL, status string, or partial command output. Only structured JSON/key-value evidence can prove terminal success.
Missing or stale PR metadata pr_number or pr_url points to the wrong branch, stale head SHA, closed PR, or unavailable provider metadata. PR identity must match repo, branch, base, and head SHA before PR state is trusted.
Dirty worktree misclassification Generated artifacts or unstaged edits are treated as harmless no-diff work. Dirty worktree blocks success with FAILED_DIRTY_WORKTREE.
Closed-unmerged PR handling A closed PR without merge evidence is treated as completed while branch diff remains. Finalization returns FAILED_CLOSED_UNMERGED unless local obsolete/no-diff proof supports CLOSED_OBSOLETE.
Remaining meaningful diff Branch changes remain but no valid PR, merge, follow-up, or verified implementation path proves closure. Finalization returns FAILED_MEANINGFUL_DIFF rather than treating the branch as a no-op success.
Missing tooling Required git, jq, provider CLI, or provider auth is unavailable on a path that depends on it. Finalization returns FAILED_MISSING_TOOLING, FAILED_PR_METADATA_UNAVAILABLE, or BLOCKED_MANUAL_PROVIDER; it does not silently skip required proof.
Failed CI Open PR has failing required checks but final output looks complete. Finalization returns BLOCKED_CI with failing check evidence and nonzero exit.
Hollow success Workflow exits after setup, design, empty agent output, or inaccessible-codebase messages. Finalization returns HOLLOW_SUCCESS or FAILED_MISSING_TERMINAL_EVIDENCE.

Publish behavior

Before creating a pull request, workflow-publish must classify the repository and branch state.

State Publish result
Azure DevOps host ManualRequired with an Azure Repos pull-request action; no gh pr create or az repos pr create call.
Local or unsupported host ManualRequired with provider-neutral next action; no remote provider command.
No branch diff against base Success with state=no-diff; no PR is created.
Existing open PR for the same head branch Success with state=existing-open-pr; existing PR URL is returned.
Existing merged PR for the same head branch Success with state=already-merged; merged PR URL is returned.
Existing closed PR that was merged Success with state=closed-after-merge; merged PR URL is returned.
Existing closed, unmerged PR with branch diff Failure with a clear action message.
Branch has diff and no existing PR Create one draft PR and return its URL.

The recipe must check branch diff and PR state before publishing. It never creates an empty PR and never creates a second PR for the same live branch when an open or merged PR already exists.

Publishing is also collision-tolerant at create time. If gh pr create fails because a pull request already exists for the branch (a race with the pre-create check, or a stale local view), workflow-publish re-runs the scoped PR lookup; when an OPEN PR now exists it returns state=existing-open-pr success rather than FAILED_PR_CREATE. Only a genuinely absent PR combined with a failed create is a hard failure.

Closed-unmerged PRs with remaining branch diff are a hard failure. The workflow must not silently republish, mark them complete, or hide the fact that user action is required.

Finalize behavior

workflow-finalize uses the same terminal-state model and the structured agentic finalizer described in Default Workflow Agentic Finalization.

State Finalize result
No diff and no PR required Success with terminal_state=NO_DIFF_SUCCESS.
PR already merged Success with terminal_state=MERGED.
PR closed after merge Success with terminal_state=MERGED when merge evidence exists.
PR or branch obsolete Success with terminal_state=CLOSED_OBSOLETE only when local no-diff/obsolete proof exists.
Run superseded by a newer workflow-owned PR or issue Success with terminal_state=SUPERSEDED only when durable replacement metadata and reason text are present.
Open PR with green required checks Merge according to repository policy.
Open PR with pending or failed required checks Failure with terminal_state=BLOCKED_CI; CI gating remains active.
Closed unmerged PR with remaining branch diff Failure with terminal_state=FAILED_CLOSED_UNMERGED; user action is required.
Meaningful branch diff without terminal publication or verification proof Failure with terminal_state=FAILED_MEANINGFUL_DIFF.
Missing or ambiguous PR state Failure; ambiguity is not treated as success.
Implementation or verification evidence absent while work remains Failure with terminal_state=FAILED_IMPLEMENTATION.
Reporting step fails after successful implementation Failure with terminal_state=FAILED_REPORTING; durable pr_url/pr_number and implementation/verification evidence are preserved.
Success-looking run without completion evidence Failure with terminal_state=HOLLOW_SUCCESS or FAILED_MISSING_TERMINAL_EVIDENCE.

Already-terminal states are success only when verified through Git branch diff, GitHub PR metadata, or both. Pending or failed required checks on active PRs remain failures; terminal-state resilience must not bypass CI.

Recipe context reference

These context keys influence publish/finalize behavior:

Context key Used by Meaning
repo_path publish, finalize Repository root to inspect. Defaults to the recipe working directory.
provider_context publish, finalize Structured provider helper result. GitHub enables GitHub PR handling; AzureDevOps, Local, and Unsupported use manual or blocked publication states.
remote_host_type publish Legacy compatibility input. Implementations may accept github, azure-devops, azdo, or local, but must normalize to provider_context before provider operations.
branch_name publish, finalize Current workflow branch or explicit branch to inspect.
base_branch publish, finalize Base branch for diff checks. Defaults to the detected remote default branch.
pr_number finalize Existing PR to finalize when known.
pr_url finalize Existing PR URL to parse when pr_number is absent.
issue_number publish Issue/work item identifier used in PR title/body generation.
task_description publish Human-readable task text used in PR title/body generation.

Recipe outputs include the publish result plus one canonical finalization result object. In full recipe JSON, the finalization result is stored under workflow_result; shell steps may also expose the same fields as flattened key/value output.

Output key Meaning
pr_publish_result.pr_url Created or reused PR URL, when a GitHub PR exists.
pr_publish_result.pr_number Created or reused PR number, when available.
pr_publish_result.state Publish classifier result.
workflow_result.terminal_success Whether finalization proved successful closure.
workflow_result.terminal_state Stable terminal state from the shared vocabulary.
workflow_result.terminal_reason Validated human-readable reason.
workflow_result.required_next_action Validated action required after finalization, or why no further action is required.
workflow_result.hollow_success_detected Whether the run looked successful but lacked completion evidence.
workflow_result.reporting_failure Whether a reporting step failed after successful implementation (produces FAILED_REPORTING with durable evidence preserved).
branch_diff_status has_diff, no_diff, or unknown.

Usage examples

Re-run publish after a PR already exists

amplihack recipe run workflow-publish \
  -c repo_path=. \
  -c branch_name=feat/issue-723-hygiene-cleanup \
  -c task_description="Add conservative hygiene cleanup"

If the branch already has an open PR, the recipe returns the existing PR URL and does not call gh pr create.

Re-run finalize after merge

amplihack recipe run workflow-finalize \
  -c repo_path=. \
  -c pr_number=723

If PR 723 is already merged, the recipe exits successfully with terminal_state=MERGED.

Treat a no-diff branch as done

amplihack recipe run workflow-publish \
  -c repo_path=. \
  -c branch_name=feat/issue-723-noop

When branch_name has no diff against the detected base, publish exits successfully and does not create an empty PR.

Configuration

No feature flag is required. Resilience checks are always active for workflow-publish and workflow-finalize.

GitHub operations still require a working gh installation and authentication for GitHub repositories. The recipes must not print tokens, credential helper output, auth headers, or full environment dumps.

Large nested workflow runs should preserve the project memory preference:

export NODE_OPTIONS=--max-old-space-size=32768

This setting affects child Node tooling only. It does not relax terminal-state or finalizer validation.

Troubleshooting

Symptom Meaning Action
closed-unmerged-with-diff A prior PR for this branch was closed without merge and the branch still has changes. Reopen the PR, create a new branch intentionally, or re-run with an explicit branch after review.
FAILED_CLOSED_UNMERGED Agentic finalization found a closed PR without merge evidence and local meaningful diff remains. Reopen, supersede with a follow-up branch, or remove/merge the diff before rerunning.
FAILED_MEANINGFUL_DIFF Local branch diff remains without accepted terminal publication, follow-up, no-op, or implementation-plus-verification evidence. Publish the diff, create a durable follow-up, complete verification, or remove the unintended changes.
branch_diff_status=unknown Git could not determine a safe base/head diff. Check remotes, fetch state, and branch name.
MANUAL_REQUIRED The repository needs a provider action that this workflow does not automate, such as creating an Azure Repos pull request. Perform the named manual action, then rerun status/finalization with the durable change-request URL.
BLOCKED_MANUAL_PROVIDER Required provider tooling, auth, permissions, or metadata is unavailable. Fix the named blocker, then rerun the provider helper.
BLOCKED_CI Required checks are pending, failed, or unavailable when required. Fix CI or wait for checks; terminal-state resilience does not bypass CI.
FAILED_IMPLEMENTATION Durable implementation or verification evidence is absent or failed while meaningful work remains. Resume default-workflow from implementation and verification, or choose a more specific failure state.
FAILED_REPORTING Implementation succeeded but a reporting/finalization step failed. Durable pr_url/pr_number and implementation/verification evidence are preserved. Re-run the reporting step; the implementation is intact. Do not re-run the implementation.
HOLLOW_SUCCESS The run looked successful but produced no implementation, verification, publish, or valid no-op evidence. Resume default-workflow from the missing phase or emit a supported no-op state with evidence.