Files
PaperClipAI/doc/connections/GITHUB.md
T
DottaandClaude Opus 5.5 3c058c37e6 fix(retry): continue a connected task from saved work when its exact chat request cannot be repeated (#21038)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Tasks that come from a connected chat (for example GitHub pull
request reviews) show a Retry button when a run fails
> - The task projection offers Retry, but the Retry route sends a
connected task to the chat retry owner, which only replays the exact
failed chat request
> - When that exact request cannot be replayed (the attempt already
saved a result, its output was published, the latest run did not start
from a chat message, or the requester's access changed), the route
returns 409 and tells the person to check the connection by hand
> - A Retry button that is shown must work, and recovery must not ask
the person to inspect anything
> - This pull request makes a person's Retry continue the task from its
saved work when the exact chat replay is refused, the same way a new
message on the task would
> - The benefit is that the projection and the route agree, and blocked
connected tasks become retryable after deploy with no migration

## Linked Issues or Issue Description

No public issue exists. This is the bug description, in the fields of
`.github/ISSUE_TEMPLATE/bug_report.yml`.

**What happened?**

On a hosted deploy, five blocked tasks that came from a connected GitHub
conversation showed Retry (`interruption.canRetry: true`). Each click on
Retry returned 409 `chat_failed_run_retry_not_authorized`. The message
was "The exact failed chat request can no longer be retried safely.
Check its current connection, access, and recovery state." or "This
attempt already recorded a result. Send a new message on the task to
continue from the saved work." The latest runs were legacy sandbox setup
failures, native runs that needed reconciliation, a run that started
from an issue monitor instead of a chat message, and a native run that
already saved its result. Root cause: `admitTaskRetry` in
`server/src/services/task-retry.ts` called the chat retry owner for
every connected task and passed its exact-replay refusal straight to the
person. The projection cannot predict that refusal.

**Expected behavior**

A shown Retry works. If the exact chat request cannot be repeated, Retry
continues the task from its saved work, the same way a new message on
the task would. Users do not get operator instructions.

**Steps to reproduce**

1. Bind a task to a restricted chat conversation.
2. Make its latest run fail so that the chat retry owner refuses the
exact replay. For example, use a run whose source is not a chat message,
or a native run with an accepted result.
3. Click Retry (`POST /api/issues/:id/retry`).
4. The route returns 409 `chat_failed_run_retry_not_authorized`.

**Paperclip version or commit**

`master` at 5a7378af5 (the deployed revision), reproduced on 51d77648bc.

**Deployment mode**

Hosted (cloud) instance with Daytona remote sandboxes and a GitHub chat
connection.

**Agent adapter(s) involved**

Not adapter-specific (core bug). Seen with `paperclip_runner` legacy and
native runs.

## What Changed

- `server/src/services/task-retry.ts`: a person's Retry runs the exact
chat replay inside a savepoint. On a
`chat_failed_run_retry_not_authorized` refusal, the savepoint rolls back
and the same Retry receipt stays an ordinary board continuation. The
clicking person is the responsible user, the run starts a fresh session
(`forceFreshSession`) from saved work, and no chat action, chat
authority, or output replay is created. Retry that Paperclip requests by
itself (`initiatedBy: restart_recovery | provider_limit_reset`) keeps
the exact-replay requirement. Other errors (403, lock contention,
database errors) still propagate.
- `server/src/services/chat-channels/failed-run-retry.ts`: plain refusal
wording. Runs saved by an earlier release with the old diagnostic are
still recognized by the pre-provider check.
- Tests: new red/green cases in `server/src/services/task-retry.test.ts`
(four refusal reasons on a chat task whose latest run came from an issue
monitor, plus automatic Retry still refused). Updated two real-service
route cases in `server/src/__tests__/chat-channels.integration.test.ts`
(accepted native result on an inherited GitHub task, and an unknown chat
source) from expecting 409 to expecting a board continuation without a
chat replay.
- A person's Retry on that person's own saved board turn (`source:
issue.comment.reopen`) that cannot be certified for replay also
continues from saved work, without replaying the saved message.
Automatic Retry still gets 409 `saved_board_message_not_replayable`.
- A person's Retry when no chat retry owner is wired also continues from
saved work. Automatic Retry still gets 409.
- The fallback is not used while the conversation holds saved or
unadmitted chat input for the task. That input keeps its own admission
path and the existing refusal.
- Docs: `doc/execution-semantics.md` and `doc/connections/GITHUB.md`.

### 409 paths in the task Retry route after this change

| Path | Who sees it | Result |
| --- | --- | --- |
| `chat_failed_run_retry_not_authorized` from the chat retry owner |
Person | Continues from saved work (board continuation) |
| `saved_board_message_not_replayable` | Person | Continues from saved
work |
| No chat retry owner wired | Person | Continues from saved work |
| Any of the three above while saved or unadmitted chat input is pending
| Person | 409. The pending input continues through its own admission
and delivery. |
| Any of the three above | Automatic Retry (restart recovery, provider
limit reset) | 409, recorded by the hook. No button. |
| `task_retry_busy` (chat authorization lock held) | Person | 409 with
`retryable: true` after 10 internal attempts. A new click works. |
| Idempotency key reused for a different task or user | Client error |
409. The UI makes a new key for each click. |

## Verification

- Red before the fix: `npx vitest run src/services/task-retry.test.ts -t
"connected task"` failed 4/4 with `Error: refused` (409). The board-turn
and missing-owner cases failed with "This saved message cannot be
retried safely..." and "Retry is unavailable for this connected
conversation." `src/__tests__/chat-channels.integration.test.ts -t
"continues an inherited GitHub task"` failed with `expected 202
"Accepted", got 409 "Conflict"`.
- Green after the fix: `src/services/task-retry.test.ts`,
`src/__tests__/durable-chat-wakeup.test.ts`,
`src/services/budget-resume-continuation.test.ts`: 160/160 passed.
- Full `src/__tests__/chat-channels.integration.test.ts`: 1949/1951
passed. The 2 local failures (`binds Slack tools...`, `materializes
direct external-chat finals...`) read `.claude/skills/slack/SKILL.md`
from the parent checkout of a nested local worktree (ENOENT). They do
not touch Retry and depend on the local checkout layout.
- `npx tsc --noEmit -p server/tsconfig.json`: passed.
- `pnpm verify` exited 2 (a doc file is a global CI input), so required
CI supplies broad coverage.
- Staging was inspected with GET requests only. No POST was sent to
staging.

## Risks

- Behavior change: a person's Retry on a connected task can now start a
board continuation where it was refused before. It uses the same
authority as a new message from that person, and dispatch keeps all
normal gates (task access, executor containment, recovery holds,
unadmitted chat input).
- The continuation does not publish the earlier result again and does
not reuse the chat requester's authority.
- No migration. Existing blocked tasks become retryable at the next
click.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Claude Opus 5.5 (claude-opus-5-5), Anthropic, with tool use and code
execution in Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 01:27:54 -06:00

25 KiB

GitHub managed connection

GitHub is a Paperclip Cloud-managed GitHub App connection with an advanced PAT compatibility method. Cloud owns the fixed public OAuth callback and signed webhook inbox; provider tokens are sealed to the enrolled instance and stored only in its existing encrypted secret system.

Catalog entries

GitHub connects an account for repository tools, Git, and gh, and opens Access → Connect directly. GitHub Code Review Bot connects one agent to a GitHub App for pull-request reviews and mentions, and opens Choose agent directly. The bot entry follows Settings → Experimental → GitHub review bots (enableGitHubReviewBots), which defaults to off. It is independent of Chat connectors: enabling one does not enable the other. Turning off the GitHub setting hides setup and management; existing bots keep running.

Both entries reuse the existing GitHub integrations. Bot endpoints retain the github provider identity and existing setup, reconnect, and management URLs; saved bot connections and drafts appear under GitHub Code Review Bot. GitHub repository and MCP URLs still resolve to the ordinary GitHub tool connection.

Bot instructions and triggers

Bot Settings uses the shared Markdown editor for common and event-specific instructions. Type / and select a company skill to insert a saved skill link. Common instructions accompany every admitted GitHub task; event-specific instructions accompany that event. Selected skills are materialized for the run and explicitly invoked by the native Runner, subject to existing tool and isolation restrictions. The admitted configuration snapshot determines skill selection for each wake, including queued events. GitHub messages, repository content and other companies' skills cannot assign skills to the run.

Authorized @mentions remain available when Run automatically is on or off. The switch in Settings reveals the PR and issue event checkboxes. Access lists company members and external contributors separately. Author include/exclude filters in Settings control whose activity starts automatic work; there is no per-person automatic-run switch. Automatic events still require an authorized author and an enabled event. Legacy people[].automaticReviews values are accepted but ignored. External contributors additionally need Allow automatic runs for external contributors. Their restricted guest profile and sponsor requirements are unchanged.

The passing-score number field accepts a whole number from 1 to 5, defaulting to 5. Paperclip publishes a successful Paperclip Review check only after a complete assessment of the current commit meets the threshold; lower scores fail and incomplete assessments require action. To block merging, separately require this check from the dedicated App in GitHub branch protection or a ruleset. A new head commit needs its own check. Report-only mode publishes a neutral conclusion, which GitHub can accept for a required check; it does not enforce a score requirement. This includes incomplete assessments and failed runs: their checks remain neutral and explain the failure without presenting a passing assessment. A passing score does not formally approve a PR. Approvals and change requests contains those optional agent permissions. Scheduling filters, ignored files, and inline-comment options have separate disclosures. The header shows the App identity, copyable mention, avatar download, and GitHub branding settings.

Published review summaries use the GitHub bot's display name in their heading. The check name remains Paperclip Review. Multiple bots can review the same PR through separate GitHub Apps: each keeps its own task, summary, findings, and check. Check updates match both the App ID and the endpoint/PR/commit identity. Repository branch rules decide separately which App's check is required to merge.

Self-hosted setup

The setup screen states the default access in one line, with Change for other choices. Continue to GitHub on that screen starts the provider handoff.

A self-hosted instance needs one Paperclip Cloud approval before its first managed connection. After approval, setup returns to the connect screen and continues to GitHub without another instance approval or a service restart.

If an unapproved enrollment link expires, return to setup and select Continue. Paperclip asks the server for a valid link. The server reuses a live pending enrollment or replaces an expired one; this does not revoke or repeat an existing instance approval.

Identity resolution

Fresh setup does not reuse an archived personal connection found by name and provider. It creates a separate application and connection with an available name, such as GitHub (2). The archived identity, credentials, grants, and history stay unchanged. An explicit reconnect or application-targeted setup continues the existing identity and requires its personal owner.

Every MCP call, gh invocation, native Git operation, checkout, health check, and webhook binding uses the same order:

  1. An active dedicated GitHub grant for the current agent.
  2. The active personal GitHub grant owned by the run's responsibleUserId.
  3. For automated work without a responsible user, a personal grant only when an existing standing delegation names the agent.
  4. Legacy GH_TOKEN/GITHUB_TOKEN only when no managed GitHub connection is configured for the company.

An unavailable or ambiguous managed identity fails visibly. It never falls through to another person, an organization credential, or a legacy token. Agent grants are company-scoped, have exactly one subjectAgentId, cannot be organization defaults, and are installed only for that agent.

The connection installation is the credential owner's consent boundary. A personal setup may target every agent or a selected set, and runtime resolution considers only an enabled, active connection installed for the current agent. Within that boundary, Paperclip treats the run's server-resolved responsibleUserId as its credential principal, including for automated work; agents cannot choose or spoof this field. The owner must still be an active non-viewer company member at each use. A standing delegation is needed only when a run genuinely has no responsible user.

Credential lifecycle

The production, staging, and development GitHub Apps deliberately disable user-to-server token expiration. The resulting long-lived access token is checked with GitHub's /user endpoint every 30 days, together with installation and repository summary refresh. Routine continuity requires no browser visit.

If GitHub returns an expiring access token and rotating refresh token instead, Paperclip stores both encrypted and:

  • refreshes at least one hour before access expiry;
  • forces a rotation at least every 30 days while the instance is active;
  • serializes refresh through the existing database refresh lease and compare- and-swap update;
  • atomically advances both secret values before clearing the lease;
  • retries one forced refresh after a provider 401.

Only an unrecoverable provider invalidation marks a grant needs_reauthorization. Installation removal or suspension is reported as an installation-health failure, not as token expiry.

Repository access

GitHub can return 403 Resource not accessible by integration inside an MCP tool error even when the connection itself is healthy. A failed tool approval shows GitHub access needed and Manage GitHub access, which opens that connection's Permissions page. Check the connected account and the App's access to the intended account and repository there. The original provider error remains in the action's error details and diagnostic record. Paperclip does not replay the write or hide other working tools. Saved GitHub approval errors receive the same presentation. The agent's continuation wake carries server-written next steps and tells it not to retry until access is repaired. It must never automatically replay a stored approval. Deferred historical errors receive the same guidance as their saved cards. Direct MCP calls and connection tests receive the same method-specific repair steps. A GitHub provider denial is an MCP tool error result, not an HTTP authentication failure for the gateway. Invalid gateway credentials still fail authentication before any provider call. Repair steps are attached after provider output passes content checks, so model checks inspect the provider's text without treating Paperclip's own instructions as untrusted output. Unsafe provider responses still fail those checks.

For an App connection's create_repository, the agent checks the intended repository owner first. The authenticated GitHub username and the accounts where the App is installed are separate. An organization installation does not grant access to the user's personal repositories. Personal repository creation needs App access to that personal account; organization creation needs App access to that organization and the tool's organization argument. When the App is installed on exactly one account, and its access snapshot identifies that account as an organization, create_repository and fork_repository calls that omit organization use that organization, because the personal account is unavailable. Paperclip fills it in before policy, approval, and audit, so approval cards and test results show the owner. An explicit owner is unchanged, and any other installation set keeps GitHub's personal-account default. The agent must keep the owner requested by the user. The user can choose the intended account from Add More Repos on GitHub, then select Refresh access. If creation remains unavailable, the user can create the repository on GitHub and give the App access, then the agent continues with that repository. Token connections ask the user to check token permissions and repository selection, then update or replace that token. If the method is unavailable, the agent asks the user to check the connection's authentication method. GitHub's text stays in the untrusted result.

OAuth completion verifies /user, every page of /user/installations, and every page of each installation's accessible repositories. Setup remains incomplete until at least one installation and repository are available. Paperclip stores the authenticated username and a grant-scoped display snapshot containing only repository IDs, full names, installation IDs, and private-repository flags. GitHub stays authoritative: this snapshot never authorizes repository access.

The permissions page shows repositories across authorized accounts by default. Use the account filter and search to narrow the list. The list scrolls after about ten rows and marks known private repositories with a lock. Configure on GitHub opens the app account chooser so users can add or update organization access. Refresh access after changing the selection. Older snapshots omit the private flag until refreshed. If a legacy grant lacks its app chooser URL, Load GitHub configuration refreshes access and recovers the app slug from GitHub installation metadata. The page does not substitute a single-installation settings URL for the account chooser.

The permissions page shows the authenticated GitHub account and the complete accessible repository list. Refresh access reloads it from GitHub. Older grants and grants invalidated by newer installation lifecycle events prompt for a refresh instead of presenting a stale list. The page links to GitHub's installation management page. Selected repositories are recommended; all- repository access retains its warning.

Fresh local test-drives use production Paperclip Cloud. Instance enrollment and provider enablement are separate: enrollment alone does not enable GitHub OAuth. Production must advertise the github.code profile (see Cloud's docs/github-connector-deploy-bootstrap.md). If it is unavailable, setup preserves the sign-in intent and offers a retry instead of silently switching to a personal access token. A successful retry preserves the chosen audience.

GitHub Actions tools

Managed and PAT connections request X-MCP-Toolsets: default,actions for MCP discovery and invocation. GitHub's default catalog excludes Actions; granting Actions permissions alone does not expose workflow tools. Existing connections can use Refresh actions after upgrading to discover the added tools. The normal catalog, access, approval, and quarantine rules still apply.

To dispatch an existing workflow, use actions_run_trigger with method: "run_workflow", the repository owner and name, workflow_id, ref, and any workflow inputs. The workflow must declare workflow_dispatch. The GitHub App installation or fine-grained PAT needs Actions: Read and write for the repository. App owners set that permission on the GitHub App registration; installation owners must approve an increase before it takes effect. Paperclip's action controls do not grant GitHub permissions.

The tool also supports rerunning and cancelling runs and deleting run logs. It retains GitHub's destructive classification. Read tools include actions_list, actions_get, and get_job_logs.

Provider references: MCP toolset configuration and workflow dispatch permissions.

GitHub response reconciliation reads run status and request identity without loading prompts, runtime profiles, results, or log excerpts. Request ownership checks still use current company, agent, task, and accepted message provenance. The run context projection retains issueId, taskId, wakeCommentId, and wakeCommentIds; larger runtime data stays in its owning reads.

Webhook delivery recovery

The Cloud connector authenticates the outer event batch before it processes individual encrypted App deliveries. If an inner delivery cannot be decrypted or fails its route binding check, the instance reports a failed delivery and leaves it unacknowledged in Cloud. A local warning includes the authenticated delivery ID and a fixed failure reason, without event contents or raw errors. Other valid events in the batch can still finish and acknowledge independently. Failed ciphertext never reaches the GitHub webhook handler. The broker retains failed deliveries for retry under its normal lease and retention policy; this does not recover a lost old key.

Webhooks

For the shared GitHub account connector, Paperclip Cloud verifies X-Hub-Signature-256 against the exact bounded request body before parsing, deduplicates by X-GitHub-Delivery, and persists a minimal normalized event before returning 202. Raw webhook payloads are discarded. When registering an active binding, Paperclip sends the current user token only inside the signed, payload-bound broker request so Cloud can verify access to that exact installation; Cloud neither logs nor persists that proof token. Deliveries fan out independently to every enrolled instance bound to the GitHub installation and are sealed to each instance's public key.

The instance polls with backoff, stores a company-scoped idempotency receipt, and acknowledges only successful applications. A merged pull request updates its matching external-object snapshot and immediately runs the existing merge- confirmation resolver. It wakes the assignee only when that interaction's continuation policy requests it; unrelated Paperclip issues are not closed. The periodic GitHub merge sweep remains the reconciliation fallback.

Installation lifecycle events refresh or invalidate installation summaries and remove obsolete Cloud bindings. Activity records contain event identifiers and outcomes but no webhook content. GitHub webhook content is never first-party telemetry.

Dedicated agent Apps

The bot wizard uses Cloud gateway protocol version 2. The stack builds the private manifest for a personal account or organization. GitHub still asks the user to confirm creation and repository installation. The stack exchanges the returned manifest code directly with GitHub and stores every App secret in its vault, including the private key, client secret, and webhook verifier.

Cloud stores only opaque, instance-bound callback routes and encrypted callback claims. It does not exchange codes, track App or installation identity, store GitHub secrets, or interpret bot events. It forwards original webhook bytes, provider headers, and signature in an envelope sealed to the stack before writing the inbox. The stack decrypts and authenticates those bytes through its existing durable ingress. Cloud-managed Apps are excluded from the direct-tenant callback recovery scanner and its callback-mismatch warning; the Cloud inbox owns relay delivery retries. Only that local check can verify a connection or admit work. After a repair, a fresh authenticated event for the bound installation can verify webhook delivery even when the relay deduplicates a redelivered old ping. This also applies while the connection needs attention: the signed event verifies delivery without retaining its content, admitting work, or changing repository access, even when its repository is disabled or unavailable. Verification returns the connection to the provider-setup step; later installation events cannot activate it. The manager must still complete reconnect and activation before new review requests can run. Shared-App account connectors keep the behavior described above.

Unverified gateway traffic has request, byte, pending-queue, retention and wake limits. Its transport deduplication includes the body and signature, so a forged request cannot occupy a real delivery's identifier. A ping received before its verifier reaches the vault remains queued for retry. Malformed and forged traffic is discarded after local verification. Installation returns only request a state refresh; the stack queries GitHub to establish installation authority.

OAuth state, PKCE, consent, repository discovery, recovery and lifecycle changes also stay in the stack. Interrupted single-use manifest exchanges require recovery of the existing App instead of a second creation. Direct webhook and manual existing-App recovery remain available. Connecting does not prove that an agent runtime can execute a review.

One main response per pull request

A pull request task can retain both its main discussion and inline finding threads. Bot tool discovery lists each endpoint/catalog tool once across those threads. Publishing an inline finding therefore does not duplicate the tool catalog on the task's next run. Both conversation mappings remain available for authorized follow-up replies.

The main PR conversation shares one App-owned comment across pushes, mentions, and repeat reviews. A new request updates that comment to Review queued…; agent progress and the final assessment update the same comment. Its identity survives restarts and reconnects. Existing request comments can be adopted without creating another main comment. Issue conversations and inline-review threads retain one editable response per request. Description mentions and automatic tasks also receive an acknowledgement. No new eyes reaction is added; cleanup still handles receipts created by older versions.

The agent's update_comment tool edits the current request's working comment. It takes a body and a stable idempotency key, with no caller-selected comment ID or destination. Instructions encourage brief, factual progress updates during longer work. Distinct updates use distinct keys; retries reuse the same key. Progress does not complete an assessment or change a review check.

The comment tool replaces that same comment with the final answer. submit_review replaces it with the allowed review summary; checks, inline findings and explicitly permitted formal reviews remain separate. Corrected assessments can update the summary again. Delayed progress cannot overwrite a final answer. Native final text stays in Paperclip and creates no extra comment. Legacy connections without a saved review configuration retain automatic run progress and final replies until upgraded to this tool-owned response model.

Receipts are bound to the company, App, task, accepted request and original runtime generation. The existing publication lease serializes edits, and App-owned markers recover uncertain creation without posting duplicates. Deleted or no-longer-owned comments are not recreated or edited. Coalesced runs settle a deleted earlier working comment without blocking the current response, review findings, or check publication. Failed runs update the same comment when no final reply is confirmed or unresolved. Maintenance also reconciles acknowledged requests when recovery stops before a publication is staged. Requests saved on a blocked task report that work is paused and stay open for resumed progress or stopped-run cleanup. An older run cannot replace a newer run's response. A run that ends without a final reply clears a remaining working state honestly. Repository restrictions, person authorization and governed tool checks remain in effect for edits. Existing disabled or quarantined tools stay disabled. Existing native sessions refresh incompatible tool checkpoints so the agent can see update_comment. The same Paperclip task and saved history remain.

Explicit bot mentions and subscriptions

Issue and PR descriptions, discussion comments, inline comments, and review summaries retain uploaded attachment references when they mention the bot. Private images use the exact unchanged source's authenticated GitHub rendering; repository/thread identity and body hashes remain bound across restarts. Signed image URLs and credentials are never added to durable attachment descriptors.

A manual message beginning with another recipient's @mention does not wake this bot through its thread subscription, including Apps outside Paperclip such as @greptile review. Mentions of other connected GitHub bots are also filtered elsewhere in the body. Paperclip filters that delivery before creating task work or adding an acknowledgement. Explicitly mentioning both bots allows both to receive the request, subject to their normal authorization and repository checks.

Unaddressed follow-ups and incidental human or unknown mentions retain the existing subscription behavior. Explicitly mentioning this bot admits a request even when another recipient is named. The routing rule does not change automatic issue or PR event policies. Archived bots and identities from other companies or providers are excluded from the routing lookup.

Retrying a saved request

A normal task Retry can continue a newer saved GitHub message after an earlier attempt failed. Execution revalidates the exact admitted successor, its current linked principal and repository access, and the durable continuation receipt; it does not repeat the admission that already consumed the recovery hold. A failed native predecessor still requires its committed failed result and exact executor termination proof. Successful results and uncertain delivery do not grant a replay. A certified failed-chat Retry retains the exact accepted message identity when its predecessor stopped before identity initialization, so a published review can expose its authorized passive response wait. When the exact failed GitHub request cannot be repeated (for example, the attempt already recorded a result, its output was already published, the run did not start from a GitHub message, or the requester's GitHub access changed), a person's Retry continues the task from its saved work instead, the same way a new message on the task would. That continuation runs as the person who clicked Retry in a fresh session, never with the GitHub requester's authority, and never repeats the original request or its output. It still passes the normal current access and executor-containment checks. Automatic Retry on a person's behalf does not take this path; it continues only with the exact GitHub request.

If that successor failed at Retry authorization before runtime or provider startup, the next normal Retry follows its canonical source receipt. The exact system diagnostic, settlement events, empty execution evidence and absence of allocated resources establish that narrow recovery path. A generic setup failure or a claimed providerWorkStarted: false does not establish it.

The canonical task Retry also recognizes an isolated-directory binding failure at the workspace preparation boundary before allocation. Its structured reason and task binding, trusted failed settlement, dispatch-only profile and absence of execution resources must all agree. Other workspace validation failures do not inherit this permission. Current request authorization still runs at both admission and execution; a failed unadmitted Retry intent does not replace the saved request or prevent a later deliberate Retry.

Run projection

The resolved token is leased at run start as an audited class-3 secret and is projected only into the child process:

  • GH_TOKEN, GITHUB_TOKEN, and an internal credential-helper environment key;
  • GIT_TERMINAL_PROMPT=0;
  • process-scoped GIT_CONFIG_COUNT/KEY_n/VALUE_n entries that clear ambient helpers, install a github.com-only helper, and rewrite GitHub SSH remotes to HTTPS;
  • author and committer identity using <numeric-id>+<login>@users.noreply.github.com.

Tokens never appear in arguments, URLs, files, logs, events, or model context, and the projection never replaces HOME.

Cloud deployment and exact GitHub App registration settings live in paperclip-cloud/docs/github-connector-deploy-bootstrap.md.