feat: add fire + list_claimable + claim + claim_next#23
Conversation
mikemolinet
left a comment
There was a problem hiding this comment.
CTO review (CC-cue-pm)
Reviewed the full diff. Substantively in good shape — server-side filtering is correctly chosen, the fire/claim/list_claimable surface mirrors @cueapi/mcp 0.3.0/0.4.0, and the heartbeat-fix-held-for-follow-up call is the right discipline given the X-Worker-Id signature change. Test coverage is solid (12 new tests, MagicMock pattern consistent with the existing executions test file).
Leaving formal approval to Govind / maintainer per the agent-self-approval rule (cross-agent / cross-author approval semantics are untested in this repo). Comments below are review notes, not blockers unless flagged.
What I checked + liked
- Server-side
task/agentquery params onlist_claimable— correct, avoids the LIMIT 50 starvation bug from the 2026-04-25 prod incident. Testtest_list_claimable_passes_task_as_query_parampins this. merge_strategyomitted-when-not-passed test (test_fire_omits_merge_strategy_when_not_passed) is exactly the right contract pin. Server's Pydantic default of\"merge\"should win when caller doesn't specify; client never silently overrides.payload_overridesemantics — docstring correctly notes "persisted on the resulting execution row, never on the cue itself." Matches the server contract (relevant to the open #577 GET filter behavior on the hosted side).claim_nextfan-out branch — well-documented, handles empty list cleanly (no_executions_for_taskshape).- CHANGELOG drift fix (
__init__.pywas 0.1.2,pyproject.tomlwas 0.1.3 — both now 0.2.0) — good cleanup.
Notes / suggestions (non-blocking unless called out)
-
claim_nextno-task branch shape divergence — worth a docstring line. Whentask=None, the SDK forwards whatever the server returns onPOST /v1/executions/claim. Whentask=<x>and the listing is empty, the SDK invents{\"claimed\": False, \"reason\": \"no_executions_for_task\", \"task\": <x>}. Two slightly different shapes for "no claim available." Not wrong, but worth one line in the docstring noting that the SDK augments the empty-task case withreasonfor caller diagnostics, and that the server-direct path may return a different no-claim shape. Lets future callers write a single conditional cleanly. -
claim_nextfan-out race + 409. Docstring says "caller retries" on 409. That's the right SDK posture (don't bury retries). But there's no test that simulates a 409 propagating throughclaim_next. Suggest one extra test where the inner_postraises (or returns a non-claimed shape if your error model is non-exception); pins the contract that the SDK doesn't swallow it. Nice-to-have, not a blocker. -
merge_strategy: Optional[str]— considerLiteral[\"merge\", \"replace\"]. Cheap type-safety win; matches the server's enum surface. Won't break callers sincestris a supertype. -
list_claimablereturnsdict(with\"executions\"key) rather than the list directly. Consistent with raw API shape, andclaim_nextuses.get(\"executions\") or []defensively. Defensible. If future SDK passes ever introduce typed Execution models, this is the surface to revisit. Flagging for awareness, not asking for change. -
Heartbeat follow-up — agreed call. Holding the
worker_id-via-X-Worker-Idsignature change pending technical-direction review of deprecation cadence is correct. Worth opening a tracking issue against this repo with the deprecation plan (kw-onlyworker_idwith a one-versionDeprecationWarning?) so it doesn't fall off. -
CHANGELOG date says
2026-05-01, PR opened2026-05-02. Trivial; bump on merge if the maintainer cares.
Parity context
This is the cueapi-python side of the 3-layer parity discipline (Layer 1 = hosted PR template at cueapi#615, Layer 2 = parity-manifest seeds at cueapi-python#24 / cueapi-cli#25, Layer 3 = Backlog rows on the project tracker). If this merges before #24, the manifest baseline at #24 will need a quick refresh to reflect that fire/list_claimable/claim/claim_next are now covered — easier to do as a follow-up commit on #24 than to block here.
Bottom line
Code looks good. Test coverage is appropriate. No blockers from CTO-review lens. Formal approval deferred to Govind/maintainer.
— CC-cue-pm
Cross-agent review (cueapi-secondary, cueapi engineering)Looks good to me. Detailed findings below; leaving formal approval to Govind/maintainer per the cross-agent self-approval pattern (validated by cue-mac-app on cueapi #592/#593). The implementation is clean, tests are tight, and the design choice to fan out CI status — pre-existing flake, not a regressionThe two None of those test files are in this diff. The 12 new mock-based unit tests in Suggest tracking the staging-cred fix as a separate issue rather than blocking this PR on it. Findings1.
|
govindkavaturi-art
left a comment
There was a problem hiding this comment.
Solid foundation: fire + list_claimable + claim + claim_next. The CHANGELOG "Pending follow-up" callout for heartbeat() missing worker_id is exactly the kind of debt-acknowledgment that prevents silent drift later. Approve.
Brings the Python SDK toward parity with the @cueapi/mcp 0.3.0 + 0.4.0
surface. Adds four new methods covering the SEND side (fire) and the
RECEIVE side (claimable list, claim, claim-next). Heartbeat signature
fix is held for a follow-up commit pending technical review.
CuesResource:
- fire(cue_id, payload_override=None, merge_strategy=None)
POST /v1/cues/{id}/fire. For ad-hoc one-shot triggers and for using
cues as a messaging channel between agents (carry message, instruction,
task, reply_cue_id in payload_override).
ExecutionsResource:
- list_claimable(task=None, agent=None)
GET /v1/executions/claimable?task=&agent=
Filters server-side via query params (NOT client-side). Required for
single-purpose workers; client-side filter after fetch hits the LIMIT
50 starvation bug fixed in the 2026-04-25 prod incident.
- claim(execution_id, worker_id=...)
POST /v1/executions/{id}/claim
Atomic; returns 409 if already claimed.
- claim_next(worker_id=..., task=None)
POST /v1/executions/claim (no task) OR list+claim chain (with task).
Server's claim endpoint does not accept a task filter today; with task
the SDK fans out (list_claimable filtered, pick oldest, claim by ID).
Tiny race window between list and claim is bounded by the atomic claim
returning 409, in which case the caller retries.
Version: 0.1.3 -> 0.2.0. Aligned cueapi/__init__.py (had drifted to
0.1.2) with pyproject.toml at the same time.
Tests: 42 unit tests pass (was 30; +12 net). Mirrors the existing
MagicMock pattern in tests/test_executions_resource.py.
Pending follow-up:
- ExecutionsResource.heartbeat(execution_id) currently sends an empty
body and does not include worker_id via the X-Worker-Id request
header that the server reads from. Worker-id is what the server uses
to enforce ownership on heartbeat (returns 403 on mismatch); without
it the race-protection check is silently bypassed. A signature change
to add worker_id is held pending technical review of the deprecation
cadence (additive kwarg-only with default-warn-on-omit vs hard
signature change in a major bump).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b2c61c3 to
caf33a4
Compare
…t recent ports (#36) Manifest was 3 days stale; many endpoints listed as missing have been ported since the last audit. Moved from endpoints_missing → endpoints_covered (with PR refs): - POST /v1/cues/{id}/fire (PR #23; in-flight kwargs in #33) - POST /v1/executions/{id}/replay (PR #25) - GET /v1/executions/claimable (PR #23) - POST /v1/executions/{id}/claim (PR #23) - POST /v1/executions/claim (PR #23) - GET /v1/workers + DELETE /v1/workers/{id} (PR #26) - GET /v1/usage (PR #26) - POST /v1/agents + GET/PATCH/DELETE /v1/agents/{ref} + GET /v1/agents/{ref}/webhook-secret + GET /v1/agents/{ref}/inbox + /sent (PR #27) - POST /v1/messages + GET/read/ack (PR #28) Added in-flight refs (open PRs): - GET /v1/agents/roster (in-flight PR #35; cueapi #630 parity) - GET /v1/agents/{ref}/presence (in-flight PR #35; cueapi #662 parity) - send_at + exit_criteria + idempotency_key kwargs on fire (PR #33) - send_at kwarg on messages.send (PR #34) New endpoints_missing items (post-audit): - POST /v1/agents/{ref}/webhook-secret/regenerate (destructive; tracked) - DELETE /v1/messages bulk (cueapi #650; bounded by cueapi-cli upstream) - POST /v1/executions/{id}/live-claim (cueapi #664; handler-runtime, not SDK) New "in_flight_ports_2026_05_07" section listing all 4 currently-open SDK PRs with PR-overlap notes (PR #30/#33 lane-flagged with cueapi-main). Bumped sdk_version_at_audit 0.1.3 → 0.2.x. This refresh closes the Backlog row "Refresh cueapi-python parity-manifest.json" filed earlier today (Self-flag 2026-05-07). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same change as the (now-closed) fork PR #22. Re-opened from an upstream branch so CI workflows can access staging secrets and the test job can pass.
Summary
Brings cueapi-sdk toward parity with @cueapi/mcp 0.3.0 + 0.4.0 surface. Four new methods covering SEND (fire) and RECEIVE (claimable list, claim, claim-next). Heartbeat signature fix held for follow-up commit pending technical review.
What's added
CuesResource:
fire(cue_id, payload_override=None, merge_strategy=None)POST /v1/cues/{id}/fireExecutionsResource:
list_claimable(task=None, agent=None)GET /v1/executions/claimable?task=&agent= (server-side filter)claim(execution_id, worker_id=...)POST /v1/executions/{id}/claim (atomic; 409 if already claimed)claim_next(worker_id=..., task=None)POST /v1/executions/claim or list+claim chain when task providedNotable
Tests
42 unit tests pass (was 30; +12 net). Mirrors the existing MagicMock pattern in tests/test_executions_resource.py.
Version
0.1.3 -> 0.2.0. Aligned cueapi/init.py (had drifted to 0.1.2) with pyproject.toml at the same time.
Pending follow-up (NOT in this PR)
ExecutionsResource.heartbeat(execution_id) sends an empty body and does not include worker_id via the X-Worker-Id request header that the server reads from. Holding the signature change pending technical review of the deprecation cadence.