Skip to content

feat(wcp): retry_context + idempotency_key (#1673 P1+P2)#144

Merged
saurabhjain1592 merged 7 commits intomainfrom
feat/1673-retry-context-and-idempotency-key
Apr 21, 2026
Merged

feat(wcp): retry_context + idempotency_key (#1673 P1+P2)#144
saurabhjain1592 merged 7 commits intomainfrom
feat/1673-retry-context-and-idempotency-key

Conversation

@saurabhjain1592
Copy link
Copy Markdown
Member

Summary

Implements the Python SDK side of the WCP retry_context + idempotency_key wire contract for axonflow-enterprise#1673 (Phase 1 + Phase 2). Contract: WCP_RETRY_IDEMPOTENCY_WIRE_CONTRACT.md.

  • RetryContext pydantic model + PriorCompletionStatus enum (on StepGateResponse.retry_context).
  • cached / decision_source preserved, marked [DEPRECATED] in field descriptions.
  • step_gate(..., include_prior_output=False) — new keyword-only arg sends ?include_prior_output=true.
  • idempotency_key on StepGateRequest and MarkStepCompletedRequest (max_length=255).
  • IdempotencyKeyMismatchError exported from axonflow, raised from both step_gate and mark_step_completed on 409 IDEMPOTENCY_KEY_MISMATCH.
  • Unit tests for all six shapes from §6.8 of the contract.
  • [Unreleased] CHANGELOG entry. No version bump until platform v7.3.0 tag is cut.

Coordination

Test plan

  • New suite tests/test_retry_context_idempotency.py — 7/7 green
  • Full suite (excluding integration/): 916 passed, 22 skipped, 0 failed
  • E2E against merged platform — deferred until platform PR merges
  • Version bump commit — deferred until platform release tag is cut

Implements the Python SDK side of the WCP retry_context + idempotency_key
wire contract (#1673 Phase 1 + Phase 2):

- RetryContext pydantic model + PriorCompletionStatus enum mirroring §3 of
  the contract. Exposed on StepGateResponse.retry_context.
- StepGateResponse.cached / .decision_source kept, marked [DEPRECATED] in
  field descriptions.
- step_gate() takes a keyword-only `include_prior_output: bool = False`
  argument. When True, the SDK sends ?include_prior_output=true.
- idempotency_key on StepGateRequest and MarkStepCompletedRequest (both
  max_length=255).
- IdempotencyKeyMismatchError exception, re-exported from axonflow top-level.
  Raised by step_gate and mark_step_completed when the platform returns 409
  with error.code == "IDEMPOTENCY_KEY_MISMATCH". Exposes workflow_id,
  step_id, expected_idempotency_key, received_idempotency_key.
- Unit tests for all six shapes in §6.8 of the contract, plus a second 409
  test on the gate call path.

CHANGELOG entry under [Unreleased]; no version bump (version bumps land in
the same PR once platform v7.3.0 is tagged).

Refs: technical-docs/WCP_RETRY_IDEMPOTENCY_WIRE_CONTRACT.md on
feat/1673-retry-context-and-idempotency-key in axonflow-enterprise.
Contract §3 specifies the field as "string or null" — platform returns null
when the caller never supplied a key. The pydantic model was typed as
`str = Field(default="")`, which would raise ValidationError on null from
the platform.

- Change type to `str | None = Field(default=None)` to accept null.
- Add a new unit test that exercises the null-from-platform case.
Contract §3 was updated (platform session): idempotency_key is always
emitted as a string, "" when the caller never supplied a key. Not null,
not omitted.

- Revert RetryContext.idempotency_key from `str | None = None` to
  `str = Field(default="")` so the pydantic model matches the wire.
- Drop the now-invalid null-from-platform test.
- Existing null-preservation test from the earlier commit is retired since
  the platform no longer emits null on this field.

Cross-referenced against TS SDK fix `a31e770` which applied the same
contract alignment.
Raw-SDK example exercising both phases end-to-end against a running
v7.3.0 enterprise stack:

- retry_context invariants on first gate (counters, status, first==last,
  last_decision first-call invariant)
- re-gate post-complete (gate_count=2, completion_count=1,
  prior_completion_status="completed", prior_output_available=True)
- agent-crash simulation (gate without complete → "gated_not_completed")
- include_prior_output=True opt-in populates retry_context.prior_output
- retry_context.idempotency_key echo on gate with key
- IdempotencyKeyMismatchError typed raise with workflow_id / step_id /
  expected / received all populated
- Complete with matching key succeeds

Every assertion fails the process on mismatch. Ran green against platform
on feat/1673-retry-context-and-idempotency-key.
…_idempotency example

ruff's default S101 (asserts) and E501 (line length) reject the E2E-validation
example file the platform session landed in 8d9aa78. These are intentional
for a self-verifying script — asserts are the verification mechanism, and
the long assertion labels are the diagnostics printed on failure.

- Per-file-ignore entry in pyproject.toml for S101/E501 scoped to just
  examples/wcp_retry_idempotency.py (not all examples — other example
  files should keep the stricter defaults).
- Also ran ruff format on the file for consistency.
@saurabhjain1592 saurabhjain1592 merged commit 2f2e8e7 into main Apr 21, 2026
12 checks passed
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.

1 participant