Skip to content

feat: adopt band-sdk-core's one-shot delivery lifecycle (INT-1328) - #604

Open
AlexanderZ-Band wants to merge 7 commits into
mainfrom
core-sdk/integrate-band-sdk-core-one-shot-delivery-lifecycl-INT-1328
Open

feat: adopt band-sdk-core's one-shot delivery lifecycle (INT-1328)#604
AlexanderZ-Band wants to merge 7 commits into
mainfrom
core-sdk/integrate-band-sdk-core-one-shot-delivery-lifecycl-INT-1328

Conversation

@AlexanderZ-Band

@AlexanderZ-Band AlexanderZ-Band commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements INT-1328 (the plan's "Implementation plan" comment is the source of truth) — all phases now present on this branch, including the pin bump that was blocked on band-sdk-core 2.2.0 reaching PyPI (it's live now: band-sdk-core#64 → release-please → PyPI).

  • Phase 1 (chore: bump band-sdk-core to 2.2.0...): pin bump + uv.lock regenerated against the real PyPI wheel + CI wheel-smoke extension proving is_self_echo/evaluate_drain_candidate are callable from the isolated install.
  • Phase 2 (OneShotInvoker, src/band/runtime/oneshot.py): handle_event/_process_message_event now route through band_sdk_core's evaluate_delivery_event/evaluate_next_message/evaluate_drain_candidate/evaluate_adapter_result instead of hand-rolled ignore/cleanup/self-echo/invocation/drain logic. Two intentional behavior changes called out in that commit: a room-cleanup event with no resolvable room id now raises OneShotEnvelopeError (400) instead of a silent no-op, and a malformed self-echo payload raises before classification. Adds OneShotStatus (a StrEnum) replacing six re-typed magic-string literals in the producer.
  • Phase 3 (ExecutionContext, src/band/runtime/execution.py): both duplicated self-echo booleans (backlog path, live WS path) now call the new public band_sdk_core.is_self_echo — one shared definition. evaluate_next_message/evaluate_delivery_event are deliberately not adopted here; see the plan's "Recorded decisions" section for why (ExecutionContext's delivery_status//next-cursor model asks different questions than core's id-comparison model).
  • Phase 5 docs: docs/websocket-events.md, AGENTS.md, examples/agentcore/ARCHITECTURE.md.

Test plan

  • uv run pytest tests/runtime/test_oneshot.py tests/runtime/test_execution.py -v — 148 passed, including the five acceptance-named tests unchanged: test_deduplicates_messages, test_pending_next_message_present_in_context_still_executes, test_ack_pending_message_survives_lru_pressure_without_replay, test_sync_skips_permanently_failed, test_retry_saturation_skips_handler_on_next_delivery
  • uv run pytest tests/ --ignore=tests/integration/ --ignore=tests/e2e/ -q — 5275 passed, 123 skipped (re-run against the real PyPI 2.2.0 install)
  • uv run pytest --markdown-docs $(git ls-files '*.md' ':!:examples/*') --no-cov — 43 passed
  • uv run ruff check . / uv run ruff format --check . / uv run pyrefly check — clean
  • Phase 1 pin bump + CI wheel-smoke extension, verified locally against the real PyPI wheel (not the dev-time local editable override)
  • Manual live-bridge smoke (examples/agentcore/agentcore_llm_server.py behind band-bridge) — not run this session

🤖 Generated with Claude Code

https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY

AlexanderZ-Band and others added 3 commits September 1, 2026 14:06
…oker

OneShotInvoker's handle_event/_process_message_event hand-rolled the same
ignore/cleanup/self-echo/invocation routing, next-message comparison, drain
classification, and ack decision that band_sdk_core's evaluate_delivery_event/
evaluate_next_message/evaluate_drain_candidate/evaluate_adapter_result were
extracted from as the reference implementation. Route through those four
functions instead, so core's stricter payload validation and one definition
of each decision replace the local logic. Two intentional behavior changes:
a room-cleanup event with no resolvable room id now raises OneShotEnvelopeError
(400) instead of silently no-op'ing, and a malformed self-echo payload raises
before classification instead of returning skipped_self.

Also introduces OneShotStatus, an SDK-owned StrEnum for the response
"status" vocabulary hosts branch on (kept spelled exactly as it always has
been), replacing six re-typed magic-string literals in the producer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
…ore.is_self_echo

Both self-echo checks in ExecutionContext (the backlog path and the live
WebSocket path) hand-rolled the same sender_type == "Agent" and sender_id ==
agent_id comparison. Route both through band_sdk_core's new public
is_self_echo, the same predicate OneShotInvoker now uses via
evaluate_delivery_event/evaluate_drain_candidate, so the two SDKs share one
definition instead of two independently-maintained copies.

Adds regression coverage for both call sites -- self-echo had no test
anywhere in tests/runtime/ before this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
docs/websocket-events.md read as if band_sdk_core were only an
event-validation dependency; note the delivery-lifecycle decisions and that
OneShotInvoker's routing is core's, not the SDK's own logic. AGENTS.md gets
a short band-sdk-core pointer naming which decisions live in core and the
standing obligation to extend the CI wheel-smoke when a new core symbol
starts being used. ARCHITECTURE.md now references OneShotStatus instead of
a bare status-string literal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
@linear-code

linear-code Bot commented Sep 1, 2026

Copy link
Copy Markdown

INT-1328

2.2.0 adds the public is_self_echo used by ExecutionContext and
OneShotInvoker (band-sdk-core#64). Extends the wheel-smoke CI step to prove
is_self_echo and evaluate_drain_candidate are callable from the isolated
pinned install, not just importable -- the standing obligation whenever a
new core symbol starts being used.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
@AlexanderZ-Band
AlexanderZ-Band requested a review from a team September 1, 2026 12:12
AlexanderZ-Band and others added 2 commits September 1, 2026 15:13
… a new section

The previous commit added a standalone AGENTS.md heading duplicating the
existing WebSocket Channels & Events pointer to docs/websocket-events.md,
working against the AGENTS.md slim-down this branch built on (#601). Fold
the one durable fact (extend the CI wheel-smoke when a new core symbol
starts being used) into that existing section instead.

Also fixes ARCHITECTURE.md's OneShotStatus reference: the wire response
carries the string "no_pending", not Python enum syntax.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
oneshot.py's numbered inline comments ("# 1. Claim.", "# 4. Mark...")
duplicated _process_message_event's own docstring step list without adding
information; drop the ones that only restate the following line, keep the
ones stating an actual invariant (unnumbered).

test_execution.py re-imported PlatformMessage, MessageMetadata, and
datetime/timezone inside ~20 individual test methods despite each already
being available at module scope (or trivially promotable there) -- moves
them to the top-level import, one source of truth per name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018aLCfpQh5vzVzEneDe2kVY
@amit-gazal-band

Copy link
Copy Markdown
Collaborator

Please run it manually against AWS AgentCore to make sure it works well.
Other than that, looks great.

…e coverage

The band-sdk-core migration swapped ExecutionContext's null-safe `==`
self-echo comparisons for band_sdk_core.is_self_echo, a pyo3 extension with
non-Optional str params. The /next backlog path reads sender_id/sender_type
from a Fern REST model that can carry a backend null despite its str type
hint (the same hazard oneshot.py's _drain_candidate already guards against);
without the guard, a null sender crashes the drain with TypeError instead of
being treated as a no-match. Apply the same `or ""` guard on the live
WebSocket path for consistency with the one established pattern.

Also extend the CI wheel-smoke step to prove evaluate_delivery_event,
evaluate_next_message, and evaluate_adapter_result are callable from the
pinned wheel, per this PR's own AGENTS.md rule that new band_sdk_core
symbols get wheel-smoke coverage -- oneshot.py now calls all three but the
smoke step only checked evaluate_drain_candidate and is_self_echo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDYPGiaDSYNZnyC5mg18ch
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