Skip to content

feat: Generate a status-discriminated action attempt union - #632

Merged
razor-x merged 3 commits into
mainfrom
claude/action-attempt-status-annotations-f216kn
Aug 27, 2026
Merged

feat: Generate a status-discriminated action attempt union#632
razor-x merged 3 commits into
mainfrom
claude/action-attempt-status-annotations-f216kn

Conversation

@razor-x

@razor-x razor-x commented Aug 27, 2026

Copy link
Copy Markdown
Member

What this means for SDK users

Every action attempt now parses to a class for its exact (action_type, status) pair, so mypy knows when error and result are real and when they are None:

attempt = seam.locks.lock_door(device_id=device_id, wait_for_action_attempt=False)

# Before: this typechecked and crashed at runtime (result is None while pending).
# Now: mypy rejects it.
attempt.result.was_confirmed_by_device

# Narrow on status — no None checks needed:
if attempt.status == "success":
    attempt.result.was_confirmed_by_device  # ok: result is not Optional here
if attempt.status == "error":
    attempt.error.message  # ok: error is not Optional here
if attempt.status == "pending":
    attempt.error   # typed None
    attempt.result  # typed None

# isinstance works too:
if isinstance(attempt, LockDoorSuccessActionAttempt):
    attempt.result.was_confirmed_by_device

Waiting for completion is typed as the success union, no cast needed:

resolved = seam.locks.lock_door(device_id=device_id)  # waits by default
# poll_until_ready returns SuccessActionAttempt: result is not Optional

Runtime behavior is unchanged — the API always sent null; the types just stopped lying about it.

How

Bumps @seamapi/blueprint to ^1.10.0, which annotates each action-attempt property with the statuses that populate it. The existing generated-union mechanism (per-variant dataclasses, Literal discriminators, dispatch dict) gains a second axis: 21 action types × 3 statuses = 63 classes, dispatched on the (action_type, status) tuple, with the same DeepAttrDict fallback for unknown values. Union aliases keep old names importable (LockDoorActionAttempt) and add PendingActionAttempt / SuccessActionAttempt / ErrorActionAttempt. Nothing is keyed off the names error/result.

Verification

mypy-checked tests cover every snippet above; pytest covers parsing pending/success/error payloads and unknown-status fallback. 185 tests passed, mypy clean (120 files), pylint 10/10. Regeneration from a clean install reproduces the diff exactly; only seam/resources/action_attempt.py and seam/resources/__init__.py changed among generated files.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EdWS7o3htQ9cNxhCWL5Frp

Bump @seamapi/blueprint to 1.10.0 and consume the new
actionAttemptStatuses property annotation. Each action attempt now
generates one dataclass per status from its status enum: the status
field is typed as the status literal, a property whose annotation lists
the status keeps its schema-declared requiredness (so the success class
exposes a non-optional result and the error class a non-optional
error), and a property whose annotation does not list the status is
typed None. action_attempt_from_dict dispatches on the
(action_type, status) pair, and per-action-type and per-status Union
aliases (e.g. LockDoorActionAttempt, SuccessActionAttempt,
PendingActionAttempt, ErrorActionAttempt) cover each single
discriminator value.

poll_until_ready returns the success union, and the action attempt
failed and timeout errors take the error and pending unions. Code that
dereferences error or result without narrowing on status stops
typechecking; runtime API behavior is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdWS7o3htQ9cNxhCWL5Frp
@razor-x
razor-x requested a review from a team as a code owner August 27, 2026 01:47
claude added 2 commits August 27, 2026 02:58
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdWS7o3htQ9cNxhCWL5Frp
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdWS7o3htQ9cNxhCWL5Frp
@razor-x
razor-x merged commit 48b9d9c into main Aug 27, 2026
23 checks passed
@razor-x
razor-x deleted the claude/action-attempt-status-annotations-f216kn branch August 27, 2026 03:15
@razor-x
razor-x restored the claude/action-attempt-status-annotations-f216kn branch August 27, 2026 16:02
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.

2 participants