Skip to content

spec(idempotency): declare capabilities.idempotency.in_flight_max_seconds#4409

Merged
bokelley merged 1 commit into
mainfrom
bokelley/idempotency-in-flight-max-seconds
May 11, 2026
Merged

spec(idempotency): declare capabilities.idempotency.in_flight_max_seconds#4409
bokelley merged 1 commit into
mainfrom
bokelley/idempotency-in-flight-max-seconds

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

Closes #4406. Follow-up to #4402 (rules 9 + 10 + IDEMPOTENCY_IN_FLIGHT).

Rule 9 bounds in-flight row lifetime to the seller's per-task handler timeout — but that bound is not buyer-observable today. The existing capability surface declares replay_ttl_seconds only (1h–7d), much wider than a realistic handler timeout. Buyers retrying on IDEMPOTENCY_IN_FLIGHT have no way to compute a tight retry budget.

This adds an optional in_flight_max_seconds field to the IdempotencySupported branch of adcp.idempotency:

  • Optional in 3.1 — additive; SDKs that don't see it fall back to rule 9's order-of-magnitude SHOULD heuristic.
  • Required when supported: true in 4.0 — same migration path replay_ttl_seconds followed across 2.x → 3.x.
  • Bounded integer ≥ 1, ≤ 604800 at the schema layer; cross-field bound (≤ replay_ttl_seconds) enforced by the composed-schema test suite since JSON Schema cannot express field-relative bounds.
  • Forbidden on the IdempotencyUnsupported branch — no replay window means no in-flight bound (mirrors the existing replay_ttl_seconds treatment).

Spec text

Rule 9 in security.mdx updated to point at the new capability field as the primary retry-budget bound when declared (Option A per the triage design pass) — the order-of-magnitude heuristic remains the fallback when the field is absent.

Schema changes

  • New optional property on IdempotencySupported: in_flight_max_seconds: integer ≥ 1, ≤ 604800
  • IdempotencyUnsupported branch's not clause widened from required: [replay_ttl_seconds] to anyOf: [{required: [replay_ttl_seconds]}, {required: [in_flight_max_seconds]}] — both fields forbidden on the unsupported branch.

Tests added

Composed-schema validation suite gains 5 new tests:

  • IdempotencySupported with in_flight_max_seconds: {supported: true, replay_ttl_seconds: 86400, in_flight_max_seconds: 60} accepts
  • Rejects in_flight_max_seconds: 0 (below minimum 1)
  • Rejects in_flight_max_seconds on unsupported branch
  • Schema accepts in_flight_max_seconds > replay_ttl_seconds at the schema layer (programmatic cross-field assertion catches it separately, mirroring how other field-relative bounds are tested in this suite)
  • Cross-field assertion verifies the violating shape is detectable

40/40 composed schema tests pass.

Design choices (from the issue triage)

  1. Optional in 3.1, required in 4.0 — same path replay_ttl_seconds followed. Optional now avoids breaking every existing seller's capability response on the 3.1 upgrade.
  2. Option A — heuristic stays primary, new field is "use when declared" — keeps the SHOULD load-bearing during adoption; explicit field wins when present.

Test plan

  • node tests/composed-schema-validation.test.cjs — 40/40 pass
  • node scripts/build-schemas.cjs — clean
  • node scripts/lint-storyboard-check-enum.cjs — clean
  • Schema changes are additive (no break to existing capability responses that omit the field)

🤖 Generated with Claude Code

…onds

Closes #4406. Follow-up to #4402 (rules 9 + 10 + IDEMPOTENCY_IN_FLIGHT).

Rule 9 bounds in-flight row lifetime to the seller's per-task handler
timeout but the bound is not buyer-observable today — the existing
capabilities surface declares replay_ttl_seconds only (1h–7d), much
wider than a realistic handler timeout. Buyers retrying on
IDEMPOTENCY_IN_FLIGHT have no way to compute a tight retry budget.

This adds an optional in_flight_max_seconds field to the
IdempotencySupported branch of adcp.idempotency:

- Optional in 3.1 (additive; SDKs that don't see it fall back to
  rule 9's order-of-magnitude SHOULD heuristic).
- Required when supported: true in 4.0 (same migration path
  replay_ttl_seconds followed across 2.x → 3.x).
- Bounded integer >= 1, <= 604800 at the schema layer; cross-field
  bound (<= replay_ttl_seconds) enforced by composed-schema test
  suite since JSON Schema cannot express field-relative bounds.
- Forbidden on IdempotencyUnsupported branch (mirrors the existing
  replay_ttl_seconds treatment — no replay window means no in-flight
  bound).

Rule 9 in security.mdx updated to point at the new capability field
as the primary retry-budget bound when declared (Option A per the
triage design pass); the order-of-magnitude heuristic remains the
fallback when the field is absent.

Composed-schema validation suite gains 5 new tests:
- IdempotencySupported with in_flight_max_seconds accepts
- Rejects in_flight_max_seconds: 0 (below minimum 1)
- Rejects in_flight_max_seconds on unsupported branch
- Schema accepts in_flight_max_seconds > replay_ttl_seconds at the
  schema layer (programmatic cross-field assertion catches it
  separately)
- Cross-field assertion: in_flight_max_seconds > replay_ttl_seconds
  is detected

40/40 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 2578146 into main May 11, 2026
18 checks passed
@bokelley bokelley deleted the bokelley/idempotency-in-flight-max-seconds branch May 11, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

capabilities.idempotency.in_flight_max_seconds: declare the in-flight bound so buyers can compute key lifetime

1 participant