Skip to content

docs(oseps): add OSEP-0016 resilient SDK transport - #1368

Merged
ninan-nn merged 2 commits into
opensandbox-group:mainfrom
Pangjiping:docs/osep-0016-resilient-sdk-transport
Jul 23, 2026
Merged

docs(oseps): add OSEP-0016 resilient SDK transport#1368
ninan-nn merged 2 commits into
opensandbox-group:mainfrom
Pangjiping:docs/osep-0016-resilient-sdk-transport

Conversation

@Pangjiping

@Pangjiping Pangjiping commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds OSEP-0016 proposing a client-only resilience layer for all five OpenSandbox SDKs (Python, JavaScript/TypeScript, Kotlin, Go, C#) to absorb routine network infrastructure blips without any server-side change.

Context

An audit of the five SDKs shows that, aside from Go, none has any HTTP-level retry. Even the Go SDK retries POST blindly on 429/502/503/504, which is unsafe once the network fault produces a "sent request but lost response" race.

Proposal in one paragraph

Every SDK ships a shared RetryPolicy contract with identical semantics across languages, an extended exception hierarchy with a machine-checkable is_retryable accessor, decorrelated jitter, and Retry-After handling with a bounded cap. The retry engine is injected at the same layer each SDK already uses for transport concerns (httpx transport, openapi-fetch middleware, OkHttp Interceptor, net/http.RoundTripper, DelegatingHandler). Generated OpenAPI code is not touched.

Safety invariant

The POST/PATCH retry set is deliberately narrow: only conditions that can be locally proven side-effect free. Specifically pre-send transport failures (DNS, TCP connect, TLS handshake, fresh-conn RST), plus 429 and 502. Read timeouts, 500, 503, 504 are not retried on POST/PATCH. This preserves at-most-once semantics without requiring any server-side idempotency store.

Idempotent methods (GET/HEAD/PUT/DELETE/OPTIONS) retry on the full transient set {408, 425, 429, 500, 502, 503, 504} plus all transport errors.

Zero cross-cutting cost

  • No spec change
  • No wire-format change
  • No server-side change
  • No new external dependency
  • Fresh-conn retry for idempotent methods works even when RetryPolicy.disabled()

Behavior change to call out

RetryPolicy is enabled by default. Callers that rely on fast-fail semantics must explicitly opt out via ConnectionConfig(retry_policy=RetryPolicy.disabled()). The Go SDK's existing RetryConfig is preserved as a deprecated alias for one minor version; its POST behavior on 503/504 narrows from "retry" to "fail-fast" as a safety fix.

What comes next (out of scope for this OSEP)

  • SSE stream resume with Last-Event-ID
  • Client-side circuit breaker / hedging / adaptive rate limiting
  • Idempotency-Key protocol with a server-side dedup store

The SSE Resume follow-up is unblocked by this OSEP's requirement that every SDK's SSE parser stop discarding id: and retry: fields.

Files

  • oseps/0016-resilient-sdk-transport.md — new proposal

The oseps/README.md index entry will be added in a follow-up once the proposal is accepted.

Reviewer notes

  • Non-Goals, Risks and Mitigations, and the Retry Decision Algorithm are the three sections most worth reviewing first.
  • Configuration Surface section includes six Python examples covering default, disabled, tuned attempts/backoff, tightened POST set, deadline caps, and per-request override.

@github-actions github-actions Bot added documentation Improvements or additions to documentation size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 22, 2026
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from ccf2c47 to 5dbbc2c Compare July 22, 2026 02:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccf2c4785e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from 5dbbc2c to 599950d Compare July 22, 2026 02:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 599950d456

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from 599950d to 1f75b28 Compare July 22, 2026 03:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f75b28bca

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from 1f75b28 to 3217cb7 Compare July 22, 2026 03:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3217cb7933

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from 3217cb7 to 624adf2 Compare July 22, 2026 03:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 624adf2239

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md Outdated

jwx0925 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

I suggest making status-code retries for non-idempotent methods opt-in rather than enabled by default:

retryable_status_codes_non_idempotent = {}

The default policy would then be:

  • Retry idempotent methods on the documented transient status set and transport failures.
  • Retry POST/PATCH only for transport failures that the implementation can reliably prove occurred before any request byte was sent.
  • Treat an opaque or unclassifiable transport failure as non-retryable for POST/PATCH.
  • Let callers explicitly opt in to {429, 502} (or a subset) when their operation is idempotent at the business layer or they knowingly accept the risk of duplicate execution.

A 502 alone cannot prove that upstream business logic did not run: the upstream may finish the mutation and fail while returning the response. Likewise, whether a 429 is guaranteed to be emitted before business processing depends on server implementation. Retrying either status by default makes the SDK accept duplicate-execution risk on behalf of every caller.

This default keeps the safety boundary simple: the SDK automatically recovers where replay is demonstrably safe, while availability-vs-duplication trade-offs for mutating operations require explicit caller consent. Once a server-side idempotency-key protocol exists, the default non-idempotent retry set can be expanded safely.

@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from 624adf2 to d91ed9e Compare July 22, 2026 04:45
@github-actions github-actions Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d91ed9ed87

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Comment thread oseps/0016-resilient-sdk-transport.md
Comment thread oseps/0016-resilient-sdk-transport.md Outdated
Propose a client-only resilience layer for all five SDKs (Python,
JavaScript, Kotlin, Go, C#) to survive routine infrastructure blips
without any server-side change.

The proposal defines a shared RetryPolicy contract, a retry decision
algorithm asymmetric across idempotent vs. non-idempotent methods,
decorrelated jitter, Retry-After handling with a bounded cap, and an
extended exception hierarchy with a machine-checkable is_retryable
accessor.

Non-idempotent methods (POST/PATCH) retry only on locally provable
side-effect-free conditions: pre-send transport failures (DNS, TCP
connect, TLS handshake, fresh-conn RST), 429, and 502. Idempotent
methods retry on the full transient set including 408, 425, 429, 500,
502, 503, 504 plus all transport errors.

No spec change, no wire-format change, no server change.
@Pangjiping
Pangjiping force-pushed the docs/osep-0016-resilient-sdk-transport branch from d91ed9e to 8fd7f41 Compare July 22, 2026 04:55
@github-actions github-actions Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Jul 22, 2026
@Pangjiping

Copy link
Copy Markdown
Collaborator Author

I suggest making status-code retries for non-idempotent methods opt-in rather than enabled by default:

retryable_status_codes_non_idempotent = {}

The default policy would then be:

  • Retry idempotent methods on the documented transient status set and transport failures.
  • Retry POST/PATCH only for transport failures that the implementation can reliably prove occurred before any request byte was sent.
  • Treat an opaque or unclassifiable transport failure as non-retryable for POST/PATCH.
  • Let callers explicitly opt in to {429, 502} (or a subset) when their operation is idempotent at the business layer or they knowingly accept the risk of duplicate execution.

A 502 alone cannot prove that upstream business logic did not run: the upstream may finish the mutation and fail while returning the response. Likewise, whether a 429 is guaranteed to be emitted before business processing depends on server implementation. Retrying either status by default makes the SDK accept duplicate-execution risk on behalf of every caller.

This default keeps the safety boundary simple: the SDK automatically recovers where replay is demonstrably safe, while availability-vs-duplication trade-offs for mutating operations require explicit caller consent. Once a server-side idempotency-key protocol exists, the default non-idempotent retry set can be expanded safely.

Thanks — this is exactly the framing the OSEP needed. Adopted in commit d91ed9ed.

The prior default of {429, 502} for retryable_status_codes_non_idempotent was justified only on a probabilistic argument ("most 502s are upstream connect failures") and left a genuine tail where the SDK would replay a mutation that had already begun on the upstream. That trade-off does not belong in the SDK's authority when we simultaneously advertise "no server-side idempotency store" as an explicit goal.

Concrete changes in commit d91ed9ed:

Contract:

  • retryable_status_codes_non_idempotent default changes from {429, 502} to {} (empty).
  • New paragraph immediately after the contract table spells out the rationale, using your two arguments: 502 cannot prove upstream did not run; whether 429 is emitted before business processing is server-implementation-dependent.

Retry Decision Algorithm & Matrix:

  • The decision matrix now has three columns: GET/HEAD/PUT/DELETE, POST/PATCH (default), and POST/PATCH (opt-in). Under default, no status code causes a POST/PATCH retry — the only automatic recovery is for pre-send transport failures.
  • Post-send transport failures (read timeout, write timeout after partial send, unexpected EOF) remain non-retryable for POST/PATCH regardless of the status-code opt-in, so the opt-in path can never lift the "safety cannot be locally proven" restriction.

Motivation / Goals / Non-Goals / Requirements:

  • Summary and Motivation reworded: the scope is "failures whose safety the SDK can locally prove", and any signal whose safety depends on server implementation details is opt-in.
  • New Goal: "Under the default policy, guarantee that POST/PATCH are never retried on any signal whose safety depends on server implementation details. This is a hard contract, not a best effort."
  • New Non-Goal: "Trading availability for duplicate-execution risk on the caller's behalf. When the trade-off is unavoidable, it is surfaced as an explicit RetryPolicy field the caller sets, not a hidden default."

Opt-in path (Configuration Surface):

  • Example 4 was previously "tighten POST retry set" (dropping 502 from a default that included it). It is now inverted: "opt in to POST retry on 502 and 429" with an explicit comment about when it is appropriate (business-layer idempotency or explicit acceptance of duplicate-execution risk).
  • Added example 5 showing a more aggressive opt-in {429, 502, 503, 504} with a stronger warning about the "server processed but response was lost" case.

Go migration:

  • Called out explicitly: Go's default POST retry set narrows from {429, 502, 503, 504} to {}. Two migration paths for Go users (opt in explicitly, or handle the surfaced exception in application code) are documented in Upgrade & Migration Strategy.

Risks & Alternatives:

  • Removed the "502 tail risk" defense entirely — it is no longer needed because we no longer take that trade-off by default.
  • Added a new Risks entry acknowledging the coverage gap on POST/PATCH for gateway blips, with mitigations pointing at pre-send retries (which still cover keep-alive RSTs) and the opt-in path.
  • Added a new Alternatives entry documenting the previously-considered "retry 502 by default" position and explicitly recording that this OSEP review adopted the stricter default after your comment.

Test Plan:

  • Split into "Default policy tests" and "Opt-in policy tests" sections with explicit assertions for the negative cases: POST on 429/502/503/504 under default → attempt count = 1; POST on 502 with opt-in → attempt count = 4; even with {502} opt-in, POST on read timeout → attempt count = 1.
  • Kind e2e integration test now runs two variants: the fault-injection sidecar that returns 502 on 5% of requests, asserting fail-fast under default and recovery under explicit opt-in, so both migration paths are validated.

This also happens to resolve a self-contradiction that persisted through earlier review rounds: the OSEP repeatedly claimed "SDK only retries on locally-provable-safe conditions" while carrying 502 in the default non-idempotent set. Aligning the default with the stated invariant is the cleaner position.

Future direction: if operational data (opensandbox.sdk.retry.attempts with cause=502 and idempotent=false) shows that a material fraction of workloads are hitting POST failures on 502, that becomes concrete signal for a future OSEP that adds a server-side idempotency-key protocol, at which point the default non-idempotent status set can be expanded safely. Until then, {} is the right default.

@Pangjiping Pangjiping assigned hittyt and ninan-nn and unassigned hittyt Jul 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8fd7f4124b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread oseps/0016-resilient-sdk-transport.md
Comment thread oseps/0016-resilient-sdk-transport.md
Comment thread oseps/0016-resilient-sdk-transport.md

@ninan-nn ninan-nn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ninan-nn
ninan-nn merged commit 924774e into opensandbox-group:main Jul 23, 2026
45 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 260d601c10

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +335 to +338
`Retry-After` handling (when `respect_retry_after=true`): if the
response carries `Retry-After` (delta-seconds or HTTP-date), the
next wait is `min(retry_after_value, retry_after_cap)`. Unparseable
values are ignored and the computed backoff is used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clamp stale Retry-After dates

When a server sends an HTTP-date Retry-After that is already in the past (or the client clock is ahead), retry_after_value becomes negative, but the rule passes it directly to min(...). Different SDK runtimes handle negative sleeps differently, so the promised shared behavior can diverge on stale gateway headers; clamp parsed dates to zero or ignore past dates before applying the cap.

AGENTS.md reference: AGENTS.md:L119-L122

Useful? React with 👍 / 👎.

Comment on lines +551 to +552
- Fresh-conn recovery triggers on `ECONNRESET` from a pooled
connection for `GET` and `POST` (pre-send): attempt count = 2.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reconcile fresh-conn attempt counts

This vector makes pooled ECONNRESET pre-send recovery stop after 2 attempts for both GET and POST, but the decision algorithm's PreSendFailure branch uses the normal retry budget and the earlier reset-before-first-byte vector expects 4 attempts. A shared retry-vector file cannot encode both outcomes, so SDKs will diverge on the same pooled pre-send reset unless fresh-conn recovery is explicitly a one-shot special case for all affected methods or this vector is removed.

AGENTS.md reference: AGENTS.md:L119-L122

Useful? React with 👍 / 👎.

Comment on lines +560 to +562
- With `{429, 502}`: retry on 429 for `POST`, attempt count = 2;
retry on 502 for `POST`, attempt count = 4, same body each
attempt.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Specify replay for Python/C# POST bodies

This shared opt-in test promises the same POST body on every 502 retry, but only the JS/Kotlin sections define replayability. The current Python filesystem upload path can send async-generator/file-stream bodies, and C# maps Stream uploads to StreamContent; a transport-level retry cannot reliably rebuild those consumed bodies, and C# also cannot resend the same HttpRequestMessage. Require buffering/body factories or single-attempt fallback for these SDKs too.

AGENTS.md reference: AGENTS.md:L119-L122

Useful? React with 👍 / 👎.

Comment on lines +335 to +338
`Retry-After` handling (when `respect_retry_after=true`): if the
response carries `Retry-After` (delta-seconds or HTTP-date), the
next wait is `min(retry_after_value, retry_after_cap)`. Unparseable
values are ignored and the computed backoff is used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clamp stale Retry-After dates

When a server sends an HTTP-date Retry-After that is already in the past (or the client clock is ahead), retry_after_value becomes negative, but the rule passes it directly to min(...). Different SDK runtimes handle negative sleeps differently, so the promised shared behavior can diverge on stale gateway headers; clamp parsed dates to zero or ignore past dates before applying the cap.

Useful? React with 👍 / 👎.

Comment on lines +551 to +552
- Fresh-conn recovery triggers on `ECONNRESET` from a pooled
connection for `GET` and `POST` (pre-send): attempt count = 2.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reconcile fresh-conn attempt counts

This vector makes pooled ECONNRESET pre-send recovery stop after 2 attempts for both GET and POST, but the decision algorithm's PreSendFailure branch uses the normal retry budget and the earlier reset-before-first-byte vector expects 4 attempts. A shared retry-vector file cannot encode both outcomes, so SDKs will diverge on the same pooled pre-send reset unless fresh-conn recovery is explicitly a one-shot special case for all affected methods or this vector is removed.

Useful? React with 👍 / 👎.

Comment on lines +458 to +461
**Kotlin** (`sdks/sandbox/kotlin`). A new `RetryInterceptor`
implementing `okhttp3.Interceptor` is installed on `httpClient` and
`authenticatedClient` in `HttpClientProvider`; the SSE `sseClient`
is deliberately excluded. OkHttp `RequestBody` is re-readable by

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable OkHttp's hidden connection retries

OkHttp enables retryOnConnectionFailure by default, so a stale pooled connection or alternate-route failure can be retried inside OkHttp before this RetryInterceptor observes a failure. In Kotlin, that bypasses RetryPolicy.disabled(), attempt counts, logs, and the shared status/transport decisions; explicitly set retryOnConnectionFailure(false) on the clients that the SDK retry engine owns.

Useful? React with 👍 / 👎.

Comment on lines +390 to +391
Go SDK's existing `APIError.IsTransient()` is preserved as the
Go-idiomatic accessor.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Go IsTransient semantics

The current Go SDK exposes APIError.IsTransient() as a status-only public method, but this proposal also requires the retryable accessor to reflect method, configured status sets, retry budget, deadline, and cancellation. Reusing IsTransient() for the new policy-derived value either breaks existing Go callers/tests that construct APIError{StatusCode: 503} or leaves Go inconsistent under disabled()/custom status sets; keep IsTransient() intrinsic and add a separate policy-aware accessor or stored field.

Useful? React with 👍 / 👎.

Comment on lines +418 to +419
identical defaults. `internal/lifecycle_metrics.py` is updated to
reuse the shared transport rather than constructing its own.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep lifecycle telemetry off retry wrappers

If internal/lifecycle_metrics.py reuses the caller's retry-wrapped transport, then a user who opts POST into status retries for business calls also makes the best-effort POST /metrics/events retry on 502/429. A lost response after the server recorded the event would double-count sandbox.create telemetry, so internal metrics reporting should use an unwrapped/no-retry transport regardless of the user's operation policy.

Useful? React with 👍 / 👎.

Comment on lines +480 to +482
- Enable retry by default (currently opt-in). Add
`per_attempt_timeout` and `overall_deadline`.
- Remove `withRetry` from `doStreamRequest`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable Go's built-in Transport retry path

Go's net/http.Transport can retry idempotent requests on a previously used connection before the SDK wrapper sees the failure, so a stale pooled GET may be issued twice even when RetryPolicy.disabled() is supposed to restore true fast-fail behavior. The Go landing plan needs to neutralize or account for that built-in path; otherwise disabled policy, attempt counts, and retry observability are not enforceable for Go.

Useful? React with 👍 / 👎.

Comment on lines +399 to +400
- Counter `opensandbox.sdk.retry.attempts` with dimensions
`{sdk_language, method, endpoint, cause, idempotent}`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Define low-cardinality endpoint labels

The retry metrics require an endpoint dimension but the event contract only carries a raw url, which for execd includes per-sandbox hosts/ports and for several APIs includes path parameters. If SDKs emit those values literally, retry metrics become high-cardinality and conflict with the existing OTel guidance to use route templates; define endpoint as the OpenAPI route/schema path instead.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants