Workflow Terminal-State Provider Safety¶
workflow-terminal-state is provider-neutral. It uses provider adapters for
change-request metadata and falls back to local Git evidence only when that is
safe for the requested terminal state. GitHub pull-request metadata is used only
by the GitHub adapter. Azure Repos, visualstudio.com, ssh.dev.azure.com, and
unknown remotes never trigger GitHub CLI calls.
What This Feature Does¶
The terminal-state probe decides whether default-workflow can safely stop
before creating, updating, waiting on, or merging a pull request. It preserves
the meaningful-diff safety gate across all Git providers while avoiding
GitHub-only assumptions on non-GitHub repositories.
The feature has three guarantees:
| Guarantee | Behavior |
|---|---|
| Provider-aware metadata | Provider metadata commands run only through the detected provider adapter. A bare provider-specific number never enables a different provider by itself. |
| Local safety everywhere | Clean worktree, resolvable base ref, branch/base diff, and commits-ahead checks run for every provider. |
| Provider fail-closed semantics | Provider remotes and provider change-request URLs require trustworthy matching metadata when metadata is needed to prove a change-request state or to decide whether meaningful local work is safe to close. |
gh is optional for Azure Repos and unknown remotes. Missing GitHub PR
metadata is not a terminal-state failure merely because a probe is running in a
GitHub repository. A clean no-diff GitHub branch can still return
NO_DIFF_SUCCESS from local Git evidence. Metadata absence fails closed only
when GitHub PR evidence is required: for example, proving MERGED, validating
a supplied GitHub PR URL, or deciding that a meaningful local diff is safe to
treat as terminal.
Provider Detection¶
Provider detection starts with the most explicit signal and falls back to
origin:
- If
pr_urlis present, classify that URL. - Otherwise classify
git config --get remote.origin.url. - Enable provider metadata only after a positive provider match.
- Treat every unsupported remote as local/unknown.
- Use provider-specific IDs only after provider metadata is enabled; a bare number is not provider evidence.
| Input example | Provider classification | GitHub PR metadata |
|---|---|---|
https://github.com/OWNER/REPO/pull/123 |
GitHub explicit PR URL | Enabled |
https://github.com/OWNER/REPO.git |
GitHub remote | Enabled |
git@github.com:OWNER/REPO.git |
GitHub remote | Enabled |
ssh://git@github.com/OWNER/REPO.git |
GitHub remote | Enabled |
https://dev.azure.com/ORG/PROJECT/_git/REPO |
Azure Repos | Disabled |
https://ORG.visualstudio.com/PROJECT/_git/REPO |
Azure Repos legacy host | Disabled |
git@ssh.dev.azure.com:v3/ORG/PROJECT/REPO |
Azure Repos SSH | Disabled |
ssh://git@ssh.dev.azure.com/v3/ORG/PROJECT/REPO |
Azure Repos SSH | Disabled |
https://gitlab.com/OWNER/REPO.git |
Unknown/non-GitHub | Disabled |
No origin remote |
Unknown/non-GitHub | Disabled |
Non-GitHub change-request URLs are recognized as non-GitHub provider signals and
never trigger gh.
Safety Model¶
Provider-independent local Git gates¶
These checks always run:
- The target path is a Git worktree.
branch_nameis valid and matches the checked-out branch.base_refresolves to a commit, either from input or from the default-base search order.- The worktree is clean before terminal success can be claimed.
- The branch/base diff is inspected.
- Commits ahead of base are counted.
Terminal success is allowed only when local Git evidence proves one of the safe states:
| State | Local proof |
|---|---|
NO_DIFF_SUCCESS |
Clean branch, no meaningful diff, and no commits ahead of base. |
CLOSED_OBSOLETE |
Clean branch with no meaningful diff remaining, even if historical local commits exist. |
Meaningful local diffs are never converted into terminal success. On non-GitHub remotes, a meaningful diff keeps the workflow active or blocked until the diff is published by the applicable workflow path, removed, merged upstream, or explicitly superseded.
GitHub metadata gates¶
When GitHub metadata is enabled, workflow-terminal-state uses GitHub CLI
metadata to prove PR-specific states:
| State | Required metadata |
|---|---|
MERGED |
PR state is merged, or closed with merge evidence such as mergedAt. |
CLOSED_OBSOLETE |
PR is closed without merge evidence and local Git proves no meaningful diff remains. |
BLOCKED_CI |
GitHub status-check metadata contains real failing checks. |
GitHub metadata must match the local repository and branch:
- PR head branch equals
branch_name. - PR base branch equals normalized
base_ref. - PR head SHA equals local
HEAD. - PR head and base repositories match the current GitHub remote.
- Cross-repository PRs are rejected by the terminal-state proof.
If a GitHub metadata command fails, returns ambiguous data, or cannot prove the
requested GitHub PR identity, terminal-state validation fails closed when the
current decision depends on that metadata. Clean no-diff proof can still return
NO_DIFF_SUCCESS from local Git evidence without PR metadata.
Non-GitHub fallback¶
For Azure Repos and unknown remotes without configured change-request automation, terminal-state validation intentionally does not inspect provider PR metadata. It does not call:
The local Git gates remain authoritative:
| Local condition | Terminal-state behavior |
|---|---|
| Dirty worktree | Terminal success is denied so pending local work can be handled. |
| Missing or unresolved base ref | Terminal-state validation fails with an actionable base-ref error. |
| Clean no-diff branch | NO_DIFF_SUCCESS is allowed without GitHub metadata. |
| Clean branch with no meaningful diff but commits ahead | CLOSED_OBSOLETE is allowed as superseded/no-diff evidence. |
| Meaningful branch/base diff | Terminal success is denied; the workflow must publish, remove, merge, supersede the diff, or return ManualRequired with a next action. |
Usage¶
workflow-terminal-state normally runs inside default-workflow. Direct
invocation is useful for diagnosing terminal-state decisions:
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c branch_name="$(git branch --show-current)" \
-c base_ref=origin/main
For large nested workflow runs, keep the supported Node heap setting in the
environment or in ~/.amplihack/config:
Recipe API¶
Inputs¶
| Input | Required | Description |
|---|---|---|
worktree_setup.worktree_path |
No | Preferred workflow checkout. When present, terminal-state checks run here instead of repo_path. |
repo_path |
Usually | Repository path used when no worktree path is supplied. Defaults to .. |
branch_name |
No | Expected current branch. Defaults to the checked-out branch. |
base_ref |
No | Intended comparison base. Defaults to origin/HEAD, origin/main, origin/master, origin/develop, main, master, or develop, whichever resolves first. |
pr_number |
No | GitHub PR number. Used only after the PR URL or remote has enabled GitHub metadata; a bare number does not imply GitHub support. |
pr_url |
No | PR URL. GitHub URLs enable GitHub metadata; non-GitHub URLs classify the provider and otherwise remain opaque. |
goal_already_met |
No | Legacy design signal. It can support a no-diff proof but never overrides dirty work, failing checks, or meaningful diffs. |
Outputs¶
| Output | Meaning |
|---|---|
terminal_success |
true only when the workflow can safely stop without publishing or merging more work. |
terminal_state |
Stable state such as MERGED, CLOSED_OBSOLETE, NO_DIFF_SUCCESS, FOLLOWUP_CREATED, SUPERSEDED, FAILED_MEANINGFUL_DIFF, FAILED_IMPLEMENTATION, FAILED_REPORTING, or BLOCKED_CI. |
terminal_reason |
Human-readable evidence for the decision. |
publish_status |
Publish-facing status using the same vocabulary as terminal_state. |
should_publish |
true only when the workflow should continue through a publish path for meaningful work. |
should_finalize |
true when finalization should emit a non-mutating final decision. |
should_run_ci_wait |
true only when CI waiting remains valid for the active provider path. |
should_merge |
true only when merge remains valid for green, active work. |
change_request_url |
Provider change-request URL when metadata identified one; empty for local-only probes. |
change_request_id |
Provider change-request identifier when metadata identified one. |
pr_url |
GitHub compatibility URL when GitHub metadata identified one; empty for non-GitHub local-only probes. |
pr_number |
GitHub compatibility number when GitHub metadata identified one; empty for non-GitHub local-only probes. |
branch_diff_status |
no-diff, has-diff, or unknown. |
commits_ahead |
Number of commits ahead of base_ref, when countable. |
Configuration¶
No provider configuration is required.
| Environment or tool | Required when | Notes |
|---|---|---|
git |
Always | Used for all provider-independent safety checks. |
jq |
Always | Used by the recipe to emit structured output. |
gh |
GitHub remotes or explicit GitHub PR URLs | Required only for GitHub PR metadata. Not required for Azure Repos or unknown remotes. |
GH_TOKEN or GitHub auth |
GitHub metadata path | Missing or invalid auth fails closed for GitHub-backed terminal-state proofs. |
NODE_OPTIONS=--max-old-space-size=32768 |
Large nested workflow runs | Recommended saved preference for this project; unrelated to provider detection. |
If a non-GitHub repository has no resolvable default base, pass base_ref
explicitly:
Examples¶
Azure Repos HTTPS remote, no GitHub CLI installed¶
git remote set-url origin https://dev.azure.com/acme/platform/_git/service
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c branch_name=fix/provider-safe-terminal-state \
-c base_ref=origin/main
Expected behavior:
- No
gh pr listorgh pr viewcommand is run. - A clean no-diff branch can return
NO_DIFF_SUCCESS. - A meaningful diff returns a non-terminal decision and cannot be treated as workflow closure.
Azure Repos SSH remote¶
git remote set-url origin git@ssh.dev.azure.com:v3/acme/platform/service
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c base_ref=origin/main
Expected behavior is the same as Azure HTTPS: local Git evidence is sufficient for no-diff closure and sufficient to block unsafe meaningful-diff closure.
Unknown remote with a meaningful diff¶
git remote set-url origin https://git.example.invalid/acme/service.git
echo "change" >> README.md
git add README.md
git commit -m "Document provider-safe terminal state"
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c base_ref=origin/main
Expected behavior:
- No GitHub metadata command is run.
branch_diff_statusreportshas-diff.terminal_successisfalse.- The workflow does not claim terminal closure until the diff is resolved, published by an applicable provider path, merged upstream, or superseded.
GitHub remote with same-branch PR discovery¶
git remote set-url origin git@github.com:rysweet/amplihack-rs.git
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c branch_name=fix/provider-safe-terminal-state \
-c base_ref=origin/main
Expected behavior:
gh pr list --head fix/provider-safe-terminal-state --state all ...is allowed.- PR metadata failures fail closed when a meaningful diff remains.
- A merged matching PR can return
MERGED.
Explicit GitHub PR URL¶
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c pr_url=https://github.com/rysweet/amplihack-rs/pull/751 \
-c base_ref=origin/main
Expected behavior:
- GitHub metadata is enabled even if the current remote is missing or ambiguous.
- The PR URL must identify the same repository and branch as the local checkout.
- Stale head SHA, mismatched base, cross-repository identity, or failing checks block terminal success.
Explicit Azure Repos PR URL¶
amplihack recipe run workflow-terminal-state \
-c repo_path=. \
-c pr_url=https://dev.azure.com/acme/platform/_git/service/pullrequest/42 \
-c base_ref=origin/main
Expected behavior:
- The URL classifies the provider as non-GitHub.
- The URL is not parsed as a GitHub PR.
ghis not required.- Local Git safety checks decide whether terminal closure is safe.
Troubleshooting¶
| Symptom | Meaning | Fix |
|---|---|---|
gh is required on an Azure Repos remote |
Provider detection regressed or failed to classify the remote as non-GitHub. | Treat this as a bug: capture the remote URL form and add or fix the provider-detection regression coverage. |
base_ref does not resolve |
Local Git cannot compare branch to base. | Fetch the base branch or pass a resolvable base_ref. |
terminal_success=false with branch_diff_status=has-diff |
Meaningful work remains. | Continue the workflow, publish through the applicable provider path, or explicitly supersede/remove the diff. |
| GitHub PR metadata mismatch | GitHub PR identity does not match local branch/base/HEAD. | Fetch, checkout the correct branch, or pass the correct PR URL/number. |
Regression Expectations¶
Provider-safety tests cover these scenarios:
- Azure HTTPS remotes do not invoke
gh pr list. - Azure SSH remotes do not invoke
gh pr list. - Unknown non-GitHub remotes do not invoke
gh pr list. - Non-GitHub meaningful diffs are detected and cannot produce terminal success.
- GitHub remotes still invoke GitHub PR discovery when no explicit PR target is supplied.
- Explicit GitHub PR URLs preserve fail-closed metadata validation.
- A bare
pr_numberon Azure Repos or unknown remotes does not invokegh. - A clean no-diff GitHub branch can return
NO_DIFF_SUCCESSeven when PR metadata is unavailable.