Skip to content

PR Recovery Readiness Reference

Home > Reference > PR Recovery Readiness

Field-level contract for recovering an existing pull request through default-workflow, proving Copilot hook readiness, proving additive-copy readiness, publishing only to the existing PR branch, and finalizing without a manual merge.

Contents

Workflow Context Inputs

default-workflow accepts these recovery fields:

Field Type Required Meaning
task_description string Yes Human-readable recovery task and bounded scope.
repo_path path string Yes Repository root or worktree root for the target PR.
pr_number integer or numeric string Yes Existing pull request number to recover.
existing_branch string Yes Existing PR head branch that must be reused.
expected_head_sha full Git SHA Required for exact-head no-op PR head that must match the local checkout before the workflow can emit a no-op readiness decision.
issue_requirements string Yes Acceptance criteria for the readiness surfaces under recovery.
expected_gh_account string Required for GitHub mutation Exact GitHub login allowed to publish or finalize.

Example using PR 579:

EXPECTED_HEAD_SHA="$(gh pr view 579 --json headRefOid --jq .headRefOid)"

amplihack recipe run default-workflow \
  -c "repo_path=/home/user/src/amplihack-rs" \
  -c "pr_number=579" \
  -c "existing_branch=fix/issues-577-578-copilot-hooks-and-additive-copy" \
  -c "expected_head_sha=${EXPECTED_HEAD_SHA}" \
  -c "task_description=Recover PR #579 after interrupted workflow; resolve Copilot hook readiness and additive-copy readiness only; do not manually merge" \
  -c "issue_requirements=#577: Copilot plugin and native hooks are staged, registered, idempotent, and verified. #578: mapped framework directories replace stale amplihack-owned trees safely, preserve rollback, and guard source/destination aliasing."

pr_number and existing_branch are identity fields. A mismatch is a blocker before publish or finalization.

expected_head_sha is a no-op guard field. When it is supplied, the workflow must prove this equality before reporting readiness without file changes:

local HEAD == PR headRefOid == expected_head_sha

A mismatch is blocked, not ambiguous.

Environment Inputs

Variable Required Meaning
NODE_OPTIONS No V8 heap setting inherited by Node-based agent tooling. Use --max-old-space-size=32768 for PR 579-sized recovery runs.
AMPLIHACK_HOME No Overrides the default ~/.amplihack staging root.
AMPLIHACK_AGENT_BINARY No Propagates the active agent binary to nested workflow agents.

The active process environment is authoritative for the current run. A saved preference in ~/.amplihack/config supplies the same value to nested workflow-owned agents when configured.

Recovery Output Contract

The workflow records PR identity under pr_recovery:

{
  "pr_recovery": {
    "repository": "rysweet/amplihack-rs",
    "pr_number": 579,
    "existing_branch": "fix/issues-577-578-copilot-hooks-and-additive-copy",
    "branch_reused": true,
    "replacement_branch_created": false,
    "manual_merge_performed": false,
    "issue_numbers": [577, 578]
  }
}

Rules:

  • branch_reused is true for normal recovery.
  • replacement_branch_created is false unless the workflow emits a concrete reason that the original branch is unusable.
  • The workflow does not publish to a branch that is not attached to pr_number.
  • manual_merge_performed is always false for workflow-owned recovery.

The human-readable readiness report ends with exactly one merge-readiness line:

MERGE_READY

or:

NOT_MERGE_READY

MERGE_READY is allowed only when every merge-readiness gate passes for the current remote PR head: exact head verification, completed workflow-owned recovery, green required GitHub Actions, runnable QA or documented scenario non-applicability, docs impact, three clean quality-audit cycles, focused diff scope, adequate PR description evidence, and no prohibited path. Any missing, failing, stale, pending, or unproven gate reports NOT_MERGE_READY with blockers.

Workflow and Merge Status Mapping

workflow-finalize states describe workflow-owned recovery. MERGE_READY and NOT_MERGE_READY describe whether the PR can be considered ready to merge at the inspected head. They are related, but they are not the same contract.

Workflow evidence Readiness report Meaning
final_status: "ready" with hook/additive-copy readiness proven WORKFLOW_READY The requested recovery surfaces are proven and the PR may continue through normal GitHub protection.
final_status: "ready" plus every merge-readiness gate green for the current head MERGE_READY Workflow recovery and all merge gates are satisfied.
final_status: "ready" with pending tests, blocked merge state, missing QA, stale PR body evidence, or any other unproven merge gate NOT_MERGE_READY Recovery may be workflow-ready, but the PR is not merge-ready.
final_status: "blocked" NOT_MERGE_READY A named workflow blocker remains.
final_status: "finalized" MERGE_READY or NOT_MERGE_READY based on gates Finalization completed its permitted path; merge readiness still depends on the current head and gate evidence.

Do not convert WORKFLOW_READY into MERGE_READY by assertion. GitHub Actions, branch protection, QA/scenario evidence, PR-body evidence, and quality-audit evidence remain independent merge gates.

Copilot Plugin Contract

crates/amplihack-cli/src/commands/install/copilot_plugin.rs registers amplihack as a local Copilot CLI plugin when Copilot CLI is present.

Install Module Entry Points

fn register_copilot_plugin(repo_root: &Path, hooks_bin: &Path) -> Result<bool>;

fn register_copilot_plugin_in(
    copilot_home: &Path,
    repo_root: &Path,
    hooks_bin: &Path,
) -> Result<bool>;

Return values:

Return Meaning
Ok(true) ~/.copilot/ exists and plugin files were created or refreshed. Current code can also return this after logging a warning and skipping installedPlugins update for malformed JSON.
Ok(false) copilot_home does not exist, so Copilot CLI is not installed on this host.
Err(_) Plugin staging failed, or config mutation failed for conditions such as a non-object config root, non-array installedPlugins, or write failure. amplihack install currently surfaces this as a warning and continues after Claude hook wiring succeeds.

Plugin Directory

When ~/.copilot/ exists, install writes:

~/.copilot/installed-plugins/amplihack@local/
|-- plugin.json
|-- hooks.json
`-- commands/

commands/ is staged only when command markdown files exist under one of the supported source locations:

  1. <repo>/docs/claude/commands/amplihack/
  2. <repo>/.claude/commands/amplihack/
  3. <repo>/../.claude/commands/amplihack/

Only *.md command files are staged.

plugin.json

Required fields:

{
  "name": "amplihack",
  "description": "amplihack framework - structured agentic development workflows, hooks, and commands",
  "version": "<current crate::VERSION>",
  "author": { "name": "amplihack" },
  "license": "MIT",
  "hooks": "./hooks.json"
}

When commands are staged, the manifest also contains:

{
  "commands": "./commands"
}

hooks.json

Required shape:

{
  "version": 1,
  "hooks": {
    "sessionStart": [
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" session-start", "timeoutSec": 10 }
    ],
    "sessionEnd": [
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" stop", "timeoutSec": 120 }
    ],
    "userPromptSubmitted": [
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" workflow-classification-reminder", "timeoutSec": 5 },
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" user-prompt-submit", "timeoutSec": 10 }
    ],
    "preToolUse": [
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" pre-tool-use", "timeoutSec": 30 }
    ],
    "postToolUse": [
      { "type": "command", "bash": "\"/home/dev/.local/bin/amplihack-hooks\" post-tool-use", "timeoutSec": 30 }
    ]
  }
}

The binary path is quoted so home directories with spaces remain valid shell commands.

~/.copilot/config.json

The plugin registration updates installedPlugins idempotently:

{
  "installedPlugins": [
    {
      "name": "amplihack",
      "marketplace": "local",
      "version": "<current crate::VERSION>",
      "enabled": true,
      "cache_path": "/home/dev/.copilot/installed-plugins/amplihack@local",
      "source": "local",
      "installed_at": "2026-05-09T10:25:08Z"
    }
  ]
}

Rules:

  • Existing non-amplihack config fields are preserved.
  • Existing installedPlugins entries for other plugins are preserved.
  • Existing installedPlugins entries named amplihack are replaced with one current entry.
  • Leading // JSONC comment lines are tolerated on read, then removed when the file is rewritten as strict JSON.
  • Block comments and trailing comments are not supported by this install path.
  • Malformed JSON logs a warning and skips the installedPlugins update.
  • A non-object root or non-array installedPlugins returns an error to the registration caller, but amplihack install reports that error as a warning and continues.
  • PR recovery treats any unproven required registration as blocked, even when the install command itself completed with warnings.

Copilot Config Schema Notes

There are two Copilot config surfaces in this codebase:

Path Config key JSONC behavior
commands/install/copilot_plugin.rs installedPlugins Strips leading // lines, rewrites strict JSON, and does not preserve comments.
copilot_setup/staging.rs and copilot_setup/hooks.rs plugins and user-level hooks Uses shared JSONC helpers that preserve leading comment prefixes and tolerate broader JSONC comments.

Use installedPlugins when validating amplihack install readiness.

Native Hook Contract

~/.claude/settings.json registers native hook subcommands in this order:

Event Matcher Command Timeout
SessionStart none amplihack-hooks session-start 10
Stop none amplihack-hooks stop 120
PreToolUse * amplihack-hooks pre-tool-use host default
PostToolUse * amplihack-hooks post-tool-use host default
UserPromptSubmit none amplihack-hooks workflow-classification-reminder 5
UserPromptSubmit none amplihack-hooks user-prompt-submit 10
PreCompact none amplihack-hooks pre-compact 30

Readiness requires:

  • user-owned hook entries are preserved
  • amplihack-owned entries are replaced in place
  • rerunning install does not duplicate amplihack entries
  • native hook contract drift is visible in install output and workflow evidence
  • the install manifest records the registered event names

Additive-Copy Contract

crates/amplihack-cli/src/commands/install/directories.rs owns framework asset directory staging.

Source Resolution

fn find_source_root(repo_root: &Path) -> Result<(PathBuf, SourceLayout)>;

Resolution order:

  1. <repo>/amplifier-bundle/ as SourceLayout::Bundle
  2. <repo>/.claude/ as SourceLayout::LegacyClaude
  3. <repo>/../.claude/ as SourceLayout::LegacyClaude

Symlinked source roots fail closed.

Mapped Directory Copy

fn copytree_manifest(
    source_root: &Path,
    layout: SourceLayout,
    claude_dir: &Path,
) -> Result<Vec<String>>;

For each mapped directory, install calls the mapped-directory replacement contract:

source_dir -> target_dir.staging -> target_dir
                         target_dir -> target_dir.old

Ready behavior:

Condition Behavior
Destination absent Copy to .staging, then rename into destination.
Destination present Rename destination to .old, rename .staging to destination, remove .old after success.
Final rename fails Restore .old to destination and remove .staging.
Stale .staging exists Remove stale staging directory before the new copy.
Source and destination canonicalize to the same path Skip destructive replacement and delegate to same-path-safe copy behavior.
Source is missing, a file, or a symlink Fail before modifying the destination.
Destination mapping contains .. Fail before copying.

This contract intentionally removes stale files from amplihack-owned mapped destinations. Files that are not part of mapped framework directories, such as user settings and runtime state, are handled by their own install phases.

Layout Marker

local_install writes ~/.amplihack/.claude/.layout atomically with the selected source layout:

Marker value Meaning
bundle Source assets came from <repo>/amplifier-bundle/.
legacy Source assets came from a legacy .claude/ tree.

missing_framework_paths uses this marker to avoid checking legacy-only destinations after a bundle-layout install. A missing marker defaults to legacy for pre-marker installs. A malformed marker is warned about and also treated as legacy.

Bundle Staging

copy_amplifier_bundle(repo_root, claude_dir) stages <repo>/amplifier-bundle/ to ~/.amplihack/amplifier-bundle/ with the same staging-and-rename rollback pattern. This keeps recipe execution available for smart-orchestrator, default-workflow, and investigation-workflow.

Install Verification Contract

verify_install_completeness(source_root, layout, claude_dir) fails when source and destination evidence disagree.

Checks:

Check Failure condition
Source mapping Required source directory is missing, not a directory, or a symlink.
Destination mapping Required destination directory is absent.
Nested directories Any real source subdirectory is missing in the staged destination.
Skill count Staged skill directories are fewer than source skill directories.
Bundle staging Bundle layout did not stage ~/.amplihack/amplifier-bundle/.
Manifest paths Manifest entries are absolute, contain .., or contain root/prefix components.

Successful verification prints source-derived framework manifest evidence and allows manifest generation to proceed.

Validation Evidence

default-workflow records command evidence under validation_evidence.

{
  "validation_evidence": [
    {
      "surface": "copilot-plugin",
      "command": "cargo test -p amplihack-cli writes_plugin_manifest_with_hooks_field --lib",
      "status": "passed",
      "classification": "fixed"
    },
    {
      "surface": "additive-copy",
      "command": "cargo test -p amplihack-cli copy_amplifier_bundle_replaces_existing_atomically --lib",
      "status": "passed",
      "classification": "fixed"
    }
  ]
}

Fields:

Field Type Meaning
surface string Readiness surface under validation.
command string Exact command or explicit workflow check.
status passed, failed, skipped, or blocked Raw outcome.
classification fixed, pre_existing, environmental, or blocked Workflow classification for non-trivial outcomes.
notes string Optional safe diagnostic text.

Classification rules:

Classification Required evidence
fixed Workflow-owned changes remediated an in-scope failure and the check passed after rerun.
pre_existing The failure is outside issues 577/578 and reproduces without PR 579 recovery changes.
environmental Credentials, rate limits, missing local tools, or unavailable services prevented the check.
blocked The failure is in scope and unresolved.

Publish Contract

workflow-publish owns commits and pushes for recovery changes.

{
  "workflow_publish": {
    "target_pr": 579,
    "target_branch": "fix/issues-577-578-copilot-hooks-and-additive-copy",
    "changes_required": true,
    "pushed": true,
    "replacement_branch_created": false,
    "commits": ["abc1234"]
  }
}

Rules:

  • Publish targets the existing PR branch.
  • Dirty readiness docs/evidence are either committed by the workflow or explicitly classified outside final readiness.
  • If files changed but cannot be pushed, final status is blocked.
  • Manual commits or pushes outside the workflow are not readiness evidence.

Finalization Contract

workflow-finalize emits the final PR state.

{
  "workflow_finalize": {
    "pr_number": 579,
    "final_status": "ready",
    "hook_readiness": "ready",
    "additive_copy_readiness": "ready",
    "manual_merge_performed": false,
    "branch_reused": true,
    "remaining_blockers": [],
    "evidence": [
      "branch reused",
      "Copilot plugin readiness ready",
      "additive-copy readiness ready",
      "publish complete or not required"
    ]
  }
}

When no workflow-owned changes are required, finalization records an explicit no-op decision instead of relying on the absence of a diff:

{
  "workflow_finalize": {
    "pr_number": 579,
    "head_sha": "8fb46865fb4412038b9313a62c02cc5aa0693132",
    "final_status": "ready",
    "changes_required": false,
    "files_modified": [],
    "hook_readiness": "ready",
    "additive_copy_readiness": "ready",
    "check_state": {
      "lint_format": "green",
      "builds": "green",
      "test": "in_progress",
      "merge_state": "blocked"
    },
    "no_op_justification": "No workflow-owned hook or additive-copy readiness changes are required at head 8fb46865fb4412038b9313a62c02cc5aa0693132. Lint/Format and build checks are green; Test is still naturally in progress and branch protection keeps the merge state blocked, so the PR is workflow-ready but not merge-ready.",
    "manual_merge_performed": false,
    "merge_bypass_performed": false,
    "nested_default_workflow_launched": false
  }
}

No-op finalization requires all of these fields:

Field Required value
head_sha Exact inspected PR head.
changes_required false.
files_modified Empty array.
hook_readiness ready.
additive_copy_readiness ready.
check_state.lint_format green.
check_state.builds green.
check_state.test in_progress or a completed result reported by GitHub.
check_state.merge_state Actual mergeability state reported by GitHub.
no_op_justification Human-readable explanation tied to the exact head and check state.
manual_merge_performed false.
merge_bypass_performed false.
nested_default_workflow_launched false.

The no-op decision is workflow readiness only. It does not imply merge readiness while a required Test check is still in progress or branch protection reports a blocked merge state.

No-op reporting fails closed when hook readiness, additive-copy readiness, exact head verification, lint/build checks, files-modified evidence, or prohibited path guards are missing or failing. Pending tests and non-merge-passing GitHub merge state do not invalidate an otherwise evidence-backed workflow no-op; they set merge readiness to false and therefore require NOT_MERGE_READY.

final_status Meaning
ready The PR is ready for normal workflow-managed completion.
blocked The workflow stopped on a named blocker.
finalized The workflow completed its permitted finalization path.

The final output includes enough evidence for a reviewer to understand why the PR is ready, blocked, or finalized.

Failure Semantics

These conditions fail closed for workflow-owned PR recovery:

  • The target PR is missing, closed unexpectedly, or not in the expected repository.
  • The target PR head branch does not match existing_branch.
  • The workflow cannot check out or reuse the existing branch.
  • Copilot plugin files are missing or invalid when Copilot CLI is installed.
  • Required Copilot plugin registration cannot be proven after install warnings or config update errors.
  • Native Claude hook contract drift remains after install.
  • Mapped directory replacement leaves stale files in amplihack-owned destinations.
  • Same-path source/destination aliasing would cause destructive replacement.
  • A failed staged swap cannot restore the previous destination tree.
  • Install verification reports missing source-derived assets.
  • An exact-head no-op is requested but local HEAD == PR headRefOid == expected_head_sha is not true.
  • No-op finalization omits files modified, check state, or a justification tied to the inspected head.
  • The install manifest is incomplete or contains unsafe path entries.
  • GitHub mutation is required but authentication or identity verification fails.
  • Publish or finalization is attempted outside the workflow-owned path.
  • A manual merge is detected.

Failure output names the failed surface and records the target PR as blocked.

See Also