Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
# Technical Spec: GitHub PR Prompt Chip Retryable Network Failures
|
||||
## Problem
|
||||
The GitHub PR prompt chip currently uses the generic prompt-chip `suppress_on_failure` runtime path. That path caches any failed shell command by fingerprint in `last_failure_fingerprint` and skips future executions until the fingerprint changes.
|
||||
For the GitHub PR chip, this is too broad. A no-network failure, DNS failure, GitHub outage, API/rate-limit failure, timeout, or other transient `gh pr view` failure can resolve without any change to the session, directory, branch, executable set, or invalidating command count. These failures should not hide the chip or block a later retry. Deterministic setup failures such as missing `gh` or unauthenticated `gh` should continue to suppress the default PR chip behavior introduced by APP-3908.
|
||||
## Relevant code
|
||||
- `specs/APP-3908/PRODUCT.md` — existing product behavior for default PR chip inclusion and deterministic readiness suppression.
|
||||
- `specs/APP-3908/TECH.md` — original technical plan for PR chip default validation.
|
||||
- `app/src/context_chips/context_chip.rs (207-309)` — `ChipRuntimePolicy`, `suppress_on_failure`, runtime availability, and disabled reasons.
|
||||
- `app/src/context_chips/mod.rs (236-263)` — `ContextChipKind::GithubPullRequest` runtime policy: required `gh`/`git`, local-only execution, 5s timeout, `suppress_on_failure`, and `git`/`gh`/`gt` invalidation.
|
||||
- `app/src/context_chips/current_prompt.rs (343-520)` — fingerprint construction and `maybe_skip_fetch_due_to_matching_fingerprint`.
|
||||
- `app/src/context_chips/current_prompt.rs (620-839)` — shell chip execution, timeout handling, PR validation transitions, and `last_failure_fingerprint` updates.
|
||||
- `app/src/context_chips/current_prompt.rs (1345-1518)` — `is_gh_auth_error`, default PR chip suppression, and validation state updates.
|
||||
- `app/src/context_chips/scripts/github_pull_request_prompt_chip.sh` — shell script that maps benign repo/no-PR states to successful empty output and forwards non-benign `gh pr view` failures to stderr.
|
||||
- `app/src/context_chips/current_prompt_test.rs (631-843)` — current PR chip suppression test that expects command failures to cache the failure fingerprint.
|
||||
- `app/src/context_chips/current_prompt_test.rs (1033-1184)` — `RecordingCommandExecutor` test helper for shell command outputs.
|
||||
- `app/src/terminal/session_settings.rs (96-113, 338-349)` — `GithubPrPromptChipDefaultValidation` and local-only persisted validation setting.
|
||||
## Current state
|
||||
`ContextChipKind::GithubPullRequest` is configured with `ChipRuntimePolicy::with_suppress_on_failure()`. In `CurrentPrompt::fetch_chip_value_once`, two paths cache failures:
|
||||
- If the shell command times out, `state.last_failure_fingerprint = current_fingerprint`.
|
||||
- If the command exits unsuccessfully and `suppress_on_failure` is true, `state.last_failure_fingerprint = current_fingerprint`.
|
||||
Before executing a chip, the runtime checks whether `last_failure_fingerprint` matches the current fingerprint. If it does, the chip value is cleared, status becomes `Cached`, and no command is run.
|
||||
The PR chip already has extra validation logic: missing `gh` from `ChipAvailability::Disabled(RequiresExecutable { command: "gh" })` suppresses default inclusion, `is_gh_auth_error(stderr)` suppresses default inclusion after an auth failure, and successful command execution validates the default.
|
||||
The gap is that validation state and per-fingerprint failure caching are not separated. A transient network error does not set `GithubPrPromptChipDefaultValidation::Suppressed`, but it still populates `last_failure_fingerprint`, which suppresses the chip for the same fingerprint and prevents the normal runtime retry.
|
||||
## Proposed changes
|
||||
### 1. Introduce a PR chip command outcome classifier
|
||||
Add a small classifier near the PR validation helpers in `current_prompt.rs`:
|
||||
```rust
|
||||
enum GithubPrPromptChipCommandOutcome {
|
||||
Validated,
|
||||
DeterministicAuthFailure,
|
||||
RetryableFailure,
|
||||
}
|
||||
```
|
||||
The classifier should take the command output and timeout flag from the shell execution completion path. It should only be used for `ContextChipKind::GithubPullRequest`.
|
||||
Classification:
|
||||
- `CommandExitStatus::Success` → `Validated`
|
||||
- stderr matching the existing narrow auth patterns in `is_gh_auth_error` → `DeterministicAuthFailure`
|
||||
- timeout → `RetryableFailure`
|
||||
- all other command failures → `RetryableFailure`
|
||||
Do not attempt to maintain a broad list of every possible network error string. Treat unknown `gh` command failures as retryable unless they are a known deterministic setup failure.
|
||||
### 2. Gate PR chip failure fingerprint updates by outcome
|
||||
Keep the generic `suppress_on_failure` behavior unchanged for other chips.
|
||||
For `GithubPullRequest`, replace the unconditional failed-command cache update with outcome-specific behavior:
|
||||
- `Validated`: call `maybe_validate_github_pr_default(ctx)`, clear `last_failure_fingerprint` if it matches the current fingerprint, and store the command output as today.
|
||||
- `DeterministicAuthFailure`: call `maybe_suppress_github_pr_default(ctx)` and set `last_failure_fingerprint = current_fingerprint`.
|
||||
- `RetryableFailure`: do not call `maybe_suppress_github_pr_default(ctx)` and do not set `last_failure_fingerprint`. If the current fingerprint is already cached from a previous retryable failure after partial execution, clear it.
|
||||
Timeout handling should follow the same PR-specific branch. Timeouts for non-PR chips can keep the existing generic behavior.
|
||||
### 3. Keep missing executable suppression unchanged
|
||||
The availability path should continue to suppress the default PR chip when `gh` is missing:
|
||||
- `ChipAvailability::Disabled(ChipDisabledReason::RequiresExecutable { command: "gh" })` → `maybe_suppress_github_pr_default(ctx)`
|
||||
This is not a transient command failure because the command cannot run until local setup changes. The existing `maybe_unsuppress_github_pr_default` path can continue to reset suppression when `gh` appears on `$PATH`.
|
||||
### 4. Preserve benign empty states
|
||||
Do not change `github_pull_request_prompt_chip.sh` for this work. Its current behavior is desirable:
|
||||
- not in a git repo, detached HEAD, missing origin, non-GitHub remote, and no open PR exit successfully with empty output
|
||||
- successful empty output does not render a PR chip value
|
||||
- successful empty output can still validate that the command path is healthy when `gh pr view` was reached
|
||||
### 5. Factor the failure-cache decision for testability
|
||||
To avoid embedding special cases throughout `fetch_chip_value_once`, add a helper that determines whether a failure should set `last_failure_fingerprint`:
|
||||
```rust
|
||||
fn should_cache_failure_fingerprint(
|
||||
chip_kind: &ContextChipKind,
|
||||
output: Option<&CommandOutput>,
|
||||
timed_out: bool,
|
||||
) -> bool
|
||||
```
|
||||
Expected behavior:
|
||||
- non-PR chips return the existing generic `suppress_on_failure && failed_or_timed_out` result
|
||||
- PR chip auth failures return true
|
||||
- PR chip retryable failures and timeouts return false
|
||||
An equivalent helper returning a richer enum is also acceptable if it keeps validation and cache decisions together.
|
||||
## End-to-end flow
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[GitHub PR chip scheduled] --> B{Availability}
|
||||
B -->|Missing gh| C[Set validation Suppressed]
|
||||
B -->|Enabled| D[Run github_pull_request_prompt_chip.sh]
|
||||
D --> E{Command result}
|
||||
E -->|Success URL or benign empty| F[Validate default and render/cache value as applicable]
|
||||
E -->|Auth failure| G[Set validation Suppressed and cache failure fingerprint]
|
||||
E -->|No network, timeout, rate limit, GitHub outage, unknown failure| H[Clear chip value without caching failure fingerprint]
|
||||
H --> I[Next runtime refresh or invalidation can run command again]
|
||||
```
|
||||
## Risks and mitigations
|
||||
- **Retrying too aggressively:** The PR chip already has a 30s periodic refresh and command-based invalidation. Avoid adding a new retry loop; simply avoid poisoning the existing cache on retryable failures.
|
||||
- **False positives for auth failures:** Keep `is_gh_auth_error` narrow. Unknown failures should be retryable rather than suppressing the chip.
|
||||
- **Behavior change for the existing suppression test:** Update the current test to distinguish auth failures from retryable failures instead of asserting all PR command failures cache a fingerprint.
|
||||
- **Repeated failures while offline:** The chip may retry periodically while offline. This is acceptable because the command has a 5s timeout and this behavior is limited to the PR chip. If needed later, add bounded backoff rather than suppression.
|
||||
- **Default validation state remains suppressed from previous deterministic failures:** This spec only changes transient command failure behavior. It does not require clearing already-suppressed validation state except through existing setup-change logic.
|
||||
## Testing and validation
|
||||
- Unit test PR chip auth failure:
|
||||
- command stderr contains an auth error
|
||||
- `github_pr_chip_default_validation` becomes `Suppressed`
|
||||
- `last_failure_fingerprint` is set
|
||||
- revisiting the same fingerprint skips execution
|
||||
- Unit test PR chip network failure:
|
||||
- command stderr is a representative network failure, such as `Post "https://api.github.com/graphql": dial tcp: lookup api.github.com: no such host`
|
||||
- validation state does not become `Suppressed`
|
||||
- `last_failure_fingerprint` remains `None`
|
||||
- a subsequent fetch with the same fingerprint runs the command again and can succeed
|
||||
- Unit test PR chip timeout:
|
||||
- timeout does not suppress validation
|
||||
- timeout does not set `last_failure_fingerprint`
|
||||
- Unit test generic suppress-on-failure behavior for another shell chip remains unchanged, or keep existing coverage if sufficient.
|
||||
- Manual validation:
|
||||
- disconnect network in a GitHub repo with an authenticated `gh`
|
||||
- trigger the PR chip and confirm the chip disappears or stays empty without suppressing the default
|
||||
- restore network and confirm the chip can re-run and show the PR without changing branches, directories, prompt settings, or restarting Warp
|
||||
## Follow-ups
|
||||
- Consider bounded retry backoff for repeated retryable PR chip failures if periodic command execution is too noisy while offline.
|
||||
- Consider surfacing a lightweight transient error state in debug logs or prompt-chip logs if this is hard to diagnose manually.
|
||||
Reference in New Issue
Block a user