Skip to content

Add human handoff delivery mode for policy-compliant upstream contributions #273

Description

@eshulman2

Summary

Add a first-class human handoff delivery mode for projects whose contribution policies do not allow Forge (or another agent identity) to open pull requests directly against the canonical repository.

In this mode, Forge should implement, validate, and review changes through a staging repository/PR, then give a human contributor a reproducible handoff that lets them publish the changes upstream under their own GitHub identity. Forge remains the central workflow authority, and the behavior remains configured per Jira project.

Motivation

Many projects prohibit direct agent-authored contributions or require the person submitting an upstream PR to personally review and take responsibility for the changes. Users currently work around this by configuring Forge to target a personal repository or fork, reviewing Forge's PR there, and then manually recreating or pushing the change to the canonical repository.

That workaround has several problems:

  • Forge loses the distinction between the canonical repository and the temporary publication target.
  • The handoff is manual and error-prone.
  • Jira and Forge cannot reliably associate the staging PR with the eventual upstream PR.
  • Forge cannot verify that the human-submitted patch matches the reviewed and tested patch.
  • CI, review, and merge state become fragmented across two PRs.
  • Project administrators cannot express this contribution policy centrally.

GitHub attributes a PR to the credential that creates it. Forge therefore must not create the upstream PR when the policy requires the PR to be authored by the human. The system should facilitate and verify the handoff without impersonating the contributor.

Goals

  • Preserve Jira project properties as the source of truth for repository and delivery policy.
  • Separate the canonical repository being changed from the repository where Forge may publish a PR.
  • Allow delivery policy to be configured independently for every repository in a Jira project.
  • Keep planning, implementation, local review, CI handling, and audit records inside the existing Forge workflow.
  • Provide a low-friction, reproducible path for a human to publish the reviewed change upstream.
  • Associate the human-created upstream PR with the original Jira workflow.
  • Verify that the upstream patch corresponds to the patch approved in staging.
  • Support multi-repository tickets where different repositories use different delivery modes.
  • Preserve Forge's controlled-write boundary: agents modify only their workspace; integration nodes perform authorized external mutations.

Non-goals

  • Impersonating a human or creating an upstream PR using centrally stored personal credentials.
  • Rewriting authorship without an explicit action by the human contributor.
  • Automatically merging either staging or upstream PRs.
  • Replacing Jira project configuration with per-user or per-ticket configuration.
  • Treating an arbitrary mirror as equivalent to a GitHub fork without validating repository ancestry and permissions.

Proposed configuration

Continue using the Jira project property forge.repos. Existing string entries and existing {name, draft} objects must remain valid.

Example:

[
  "org/direct-repo",
  {
    "name": "upstream-org/project",
    "delivery": {
      "mode": "human_handoff",
      "staging_repo": "developer/project",
      "draft": true,
      "after_handoff": "observe_only"
    }
  }
]

Proposed semantics:

  • name: canonical repository used by planning, repository labels, cloning, base-branch resolution, and upstream validation.
  • delivery.mode:
    • direct: existing behavior; Forge opens a fork-based PR against name.
    • human_handoff: Forge opens a staging PR and waits for a human-created upstream PR.
  • delivery.staging_repo: repository against which Forge is authorized to open the staging PR. Required for human_handoff.
  • delivery.draft: whether the staging PR is initially a draft. If absent, preserve the existing repository-level draft/draft_pr behavior.
  • delivery.after_handoff:
    • observe_only: Forge only observes and synchronizes the upstream PR after association.
    • stage_updates: Forge may continue producing fixes in the staging PR, but never pushes to or mutates the human's upstream branch/PR.

The final names may change during implementation, but the model must keep canonical repository, publication target, and post-handoff authority explicit.

Required workflow

1. Resolve repository delivery policy

When resolving a repository from the approved plan, load and validate its complete repository descriptor from the Jira project property. Store a normalized delivery policy in workflow state rather than repeatedly interpreting raw Jira configuration.

For legacy entries, normalize to delivery.mode = direct.

2. Implement against the canonical repository

For human_handoff, Forge must clone and base its work on the configured canonical repository, not an independently diverged staging repository. The selected base branch and base SHA must be recorded.

Before publication, validate that staging_repo is suitable for the configured canonical repository (for example, a fork in the same GitHub network or another explicitly supported relationship). If it cannot safely receive the branch/PR, block with actionable Jira instructions.

3. Create and manage the staging PR

Push the Forge branch to an authorized fork/remote and open a PR against staging_repo, without opening a PR against the canonical repository.

The staging PR must:

  • Clearly state that it is a Forge-generated staging PR and is not the upstream contribution.
  • Link the Jira ticket and canonical repository.
  • Include the intended upstream base branch and base SHA.
  • Follow existing PR description, CI repair, local review, and human review behavior where applicable.
  • Be indexed so GitHub webhooks can resolve the correct Jira workflow.

4. Produce a human handoff

Once the staging change reaches the configured handoff gate, Forge should post a Jira comment and update the staging PR with:

  • The tested staging PR and commit SHA.
  • The canonical repository and intended base branch.
  • A GitHub compare URL when GitHub can construct a safe one.
  • Copyable git and gh commands for applying/publishing the change.
  • A generated upstream PR title and body, preferably available as downloadable/copyable content.
  • A patch or bundle fallback when a direct cross-fork comparison is not possible.
  • Optional squash/reset-author instructions, clearly explaining their authorship and traceability implications.
  • Instructions for associating the resulting upstream PR with Forge.

The handoff must not claim that commands preserve human authorship unless they actually do. Commit authorship and PR authorship should be described separately.

5. Associate the upstream PR

Support an explicit command such as:

/forge handoff https://github.com/upstream-org/project/pull/456

The command may be accepted from an authorized Jira comment and/or the staging PR. Authorization should follow existing Forge command/review authorization conventions.

Before accepting the association, Forge must verify:

  • The URL identifies an accessible GitHub pull request.
  • The PR base repository equals the configured canonical repository.
  • The PR targets the expected base branch, or the mismatch is explicitly surfaced for human confirmation.
  • The PR is associated with the correct Jira ticket/workflow and is not already assigned to another active workflow.
  • The upstream patch is equivalent to the approved staging patch, or any difference is reported.

Patch comparison should be content-based (for example, stable patch IDs or normalized diffs), not based only on commit SHA, because the human may squash or recreate commits. Document known limitations such as merge commits, binary changes, line-ending normalization, and rebases.

If patches differ, do not silently advance. Post a concise diff summary and keep the workflow at a human decision gate.

6. Track the upstream lifecycle

After successful association:

  • Store and index the upstream PR separately from the staging PR.
  • Add the upstream PR as a Jira remote link and add an audit comment.
  • Route CI, review, closed, and merged webhooks using the upstream PR association.
  • In observe_only, make no GitHub mutations against the upstream PR or its branch; only synchronize status to Jira.
  • In stage_updates, apply requested fixes only to the staging branch/PR and issue a new handoff revision for the human to apply.
  • Complete the Forge workflow only when the associated upstream PR is merged, or follow an explicit existing cancellation/closure policy.

State model

Avoid overloading the existing current_repo and current_pr_url fields. Introduce a per-repository delivery record conceptually similar to:

{
  "canonical_repo": "upstream-org/project",
  "staging_repo": "developer/project",
  "delivery_mode": "human_handoff",
  "after_handoff": "observe_only",
  "base_branch": "main",
  "base_sha": "abc123",
  "staging_pr_url": "https://github.com/developer/project/pull/12",
  "staging_pr_number": 12,
  "staging_head_sha": "def456",
  "approved_patch_id": "...",
  "upstream_pr_url": "https://github.com/upstream-org/project/pull/456",
  "upstream_pr_number": 456,
  "handoff_revision": 1,
  "handoff_status": "awaiting_upstream_pr"
}

Expected handoff states include at least:

  • not_required
  • preparing_staging_pr
  • staging_review
  • awaiting_upstream_pr
  • verification_required
  • tracking_upstream
  • upstream_merged
  • cancelled

The design must work with Forge's per-repository state and checkpoint/resume behavior, including multi-repository plans.

Idempotency and event routing

  • Retrying staging PR creation must find/reuse the existing PR as the direct workflow does today.
  • Retrying handoff generation must not create duplicate Jira remote links or spam comments.
  • Re-submitting the same upstream association must be idempotent.
  • Both staging and upstream PR URLs must be indexed to the same Jira workflow while retaining their roles.
  • Webhooks from an unrelated PR in either repository must not resume the workflow.
  • A restart between an external side effect and checkpoint write must be recoverable using GitHub/Jira discovery where practical.

Security and policy requirements

  • Never store or request a human's GitHub token solely to make the upstream PR appear human-authored.
  • Never push to the upstream PR branch in observe_only mode.
  • Validate repository names and PR URLs against project configuration before external writes or state association.
  • Record who requested/confirmed the handoff and the upstream association.
  • Make Forge's involvement visible in the staging PR and generated upstream PR body; do not encourage misleading attribution.
  • Keep container credentials and permissions unchanged: external GitHub/Jira operations remain orchestrator responsibilities.

User-facing failure behavior

Block with actionable Jira guidance when:

  • human_handoff is configured without staging_repo.
  • The canonical or staging repository is inaccessible.
  • The staging repository cannot receive the Forge branch/PR.
  • The canonical base branch cannot be resolved.
  • The upstream PR targets the wrong repository.
  • Patch equivalence cannot be established or fails.
  • Required webhook/indexing information is missing.

Errors should identify the relevant Jira project property and show a valid example configuration without exposing credentials.

CLI and documentation

Extend project configuration commands and effective-config output to:

  • Accept and validate the structured delivery configuration.
  • Display canonical and staging repositories separately.
  • Warn about unsupported or incomplete handoff configurations.
  • Preserve existing --repo and --default-repo behavior.

Document:

  • Direct versus human-handoff delivery.
  • Setup examples for personal forks and organization-owned staging repositories.
  • Required GitHub permissions and webhook coverage for both repositories.
  • The human publication procedure.
  • Authorship versus PR attribution.
  • Patch-verification behavior and limitations.
  • Behavior for review changes after handoff.

Compatibility and migration

  • Existing forge.repos string entries continue to use direct delivery with no behavior change.
  • Existing { "name": "org/repo", "draft": true } entries remain valid.
  • forge.default_repo continues to refer to the canonical repository name.
  • Existing active checkpoints without delivery records must deserialize and resume as direct delivery.
  • Do not reinterpret an existing configured repository as a staging repository automatically.

Acceptance criteria

  • A Jira project can configure direct and human-handoff repositories in the same forge.repos property.
  • Legacy repository configuration produces exactly the existing direct PR behavior.
  • In human-handoff mode, planning and implementation use the canonical repository and expected base branch.
  • Forge creates a staging PR but does not create an upstream PR.
  • The staging PR participates in the configured CI/review loop.
  • Forge produces a reproducible handoff containing commands, PR metadata, tested SHA, and a patch/bundle fallback.
  • An authorized user can associate a human-created upstream PR with the Jira workflow.
  • Forge rejects an upstream PR targeting an unconfigured repository.
  • Forge detects/report patch differences after squash/recreated commits using content-based comparison.
  • Equivalent patches advance to upstream tracking even when commit SHAs differ.
  • observe_only performs no mutations against the upstream PR or branch.
  • stage_updates publishes subsequent fixes only through the staging PR and creates a new handoff revision.
  • Jira shows distinct links and statuses for staging and upstream PRs.
  • GitHub webhooks for both PR roles resume the correct workflow and unrelated PRs do not.
  • Retry/restart behavior is idempotent for PR creation, handoff publication, and association.
  • Multi-repository workflows maintain independent delivery records and policies.
  • CLI validation, configuration reference, and workflow guides cover the feature.
  • Unit, workflow, and integration tests cover both delivery modes and backward compatibility.

Suggested implementation areas

  • Jira repository configuration parsing and normalization.
  • Repository resolution utilities and project CLI configuration.
  • PR target preparation and PR creation nodes.
  • Per-repository workflow/PR state representation.
  • PR-to-ticket indexing and GitHub webhook routing.
  • Jira/GitHub command handling for upstream association.
  • Patch generation and equivalence verification.
  • CI, review, teardown, and post-merge routing for distinct PR roles.
  • Configuration and workflow documentation.

Open design questions

  1. Must staging_repo always be in the canonical repository's GitHub fork network, or should independent mirrors be supported through patch application?
  2. Which existing authorization rule should govern /forge handoff from Jira and GitHub?
  3. Should patch differences permit an explicit human override, and how should that override be audited?
  4. Should upstream CI failure merely update Jira in observe_only, or introduce a dedicated “human update required” state?
  5. Should the staging PR remain open until the upstream PR merges, or close once a verified upstream PR is associated?
  6. How should binary patches and Git LFS changes be represented and verified?

Definition of done

The feature is complete when a project administrator can configure human handoff for a canonical repository, Forge can produce and validate a staging contribution without touching upstream, a human can publish and associate an equivalent upstream PR under their own identity, and Forge can audit and track that PR through merge without exceeding the configured authority boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions