Skip to content

fix: preserve unaccepted work while keeping active-turn controls reachable - #731

Merged
AlexanderZ-Band merged 7 commits into
mainfrom
fix/acp-session-busy-backpressure
Oct 11, 2026
Merged

AlexanderZ-Band merged 7 commits into
mainfrom
fix/acp-session-busy-backpressure

Conversation

@bandzalkin

@bandzalkin bandzalkin commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

A busy adapter currently consumes ordinary work without executing it, or treats a structured ACP rejection as a failed turn and tears down the session. This replacement keeps proven unaccepted work outstanding while allowing approval, status, and question controls to reach already accepted work.

Implements INT-1754. Rebuilt from current main; replaces the previous implementation of this PR.

Behavior

  • One narrow InputNotAccepted signal covers pre-send local occupancy and ACP's exact -32003 / reason=session_busy rejection. It bypasses turn judgment, failure acknowledgement, and execution-failure retry charges. Transport uncertainty keeps its existing failure behavior.
  • The existing room scheduler retries pending ordinary work in order. Startup and reconciliation yield when blocked; live and missed controls use one adapter-owned hook behind duplicate checks and the platform processing gate. Paginated control discovery preserves its cursor through ordinary traffic.
  • Accepted detached work retains its original tools. Controls acknowledge only themselves. Synthetic work survives rejection and STOP/PLAY without a second retry loop, clock, or cancellation subtype.
  • Bootstrap, roster snapshots, and pending system context commit after a non-rejected attempt. ACP sessions retain fresh-versus-restored bootstrap/replay obligations after rejection.
  • One-shot returns NOT_ACCEPTED without terminal acknowledgement or history drain. A control behind older work cannot drain that work or reopen a terminal duplicate.

No band-sdk-core API or dependency changes. TypeScript adapter migration remains a followup. This does not promise exactly-once execution across restarts or generic attribution of unsolicited provider work.

Verification

  • Full default unit/conformance run: 7,882 passed, 170 skipped, no pytest retries. Skips are reported separately and are not live acceptance.
  • Final runtime/platform/resync/interrupt/one-shot suite: 382 passed, including 15 deferral regressions. Coverage includes A awaiting approval, B pending, C behind 100 newer messages under sustained traffic, A retaining its tools, then B and D completing; startup/reconnect, STOP/missed PLAY, synthetic retention, genuine failure budgets, terminal duplicates, and control failures.
  • Shared deferral/context tests: 15 passed in each isolated CrewAI and Parlant environment. CrewAI-scoped conformance: 77 passed, 6 skipped. Broad cross-framework probes require their framework dependencies and were not treated as passing in these narrower environments.
  • Busy regressions fail behaviorally against main (compatibility-only test shims allow the new contracts to import). ACP regressions use a real ACP peer; Claude tests use the real SDK protocol against the existing fake CLI.
  • Ruff lint/format, Pyrefly, 28 executable documentation checks, and the isolated extras-free wheel smoke pass. CI for preceding head a86ede24fd6c867f36117b6bdfe964ac69e4d1df passed the full Ubuntu/Windows Python 3.11–3.13 matrix, including separate CrewAI/Parlant jobs, packaging, and the core coverage gate: 27 successful checks. Only the schedule-only weekly coverage report was skipped.

The review fixes add DEBUG diagnostics at the shared rejection transition and keep actionable-message lookup with its lifecycle page accessor. Both new real HTTP-boundary logging regressions failed before the fix and pass afterward. The preprocessing/Slack review fixes preserve configured filtering and transformations on control delivery without committing model context, and forward the same control hook through both Slack wrapper ingress paths. Normal delivery preprocesses once. Runtime regressions now use real Agent dispatch instead of duplicating it in a helper. All 9 new regressions fail on the preceding PR head and pass with these fixes; the focused preprocessing/Slack/Agent suite passes 342 tests. CI for that preceding head completed with 27 successful checks and only the schedule-only weekly coverage report skipped.

The latest review centralized one-shot failure-detail bounding and the empty-message fallback in its existing acknowledgement path. Preceding head 093161cdf8b5e22065c35faad18460f2c9d0fdd8 passes the full local gate: 7,882 passed, 170 skipped, Ruff lint/format, Pyrefly, 28 documentation checks, and isolated extras-free wheel smoke. The final cleanup at e614a405ac54af4d6f86774533bd20da54e0bb82 removes redundant preprocessing comments and leaves the Python AST unchanged. Its full local gate also passes: 7,882 passed, 170 skipped, Ruff lint/format, and Pyrefly. GitHub CI for this final head passes the full Ubuntu/Windows Python 3.11–3.13 matrix, isolated CrewAI/Parlant jobs, packaging, lint, and coverage. The schedule-only weekly coverage report is not a live acceptance gate.

Live evidence and blockers

Actual OMP 18.2.6 and released 18.8.9 naturally entered autonomous work through a supported extension. A real ACP prompt received the exact structured busy rejection, preserved its session without reporting failure, and a later prompt succeeded on that same session. This proves the provider/ACP recovery boundary; it is not platform scheduler acceptance.

Added live A/B/C/D acceptance cells for Claude SDK, Codex, and OpenCode. Their runs did not reach the scenario: Claude/Codex were blocked by platform 403 plan_required (Enterprise subscription required). After verifying an isolated OpenCode 1.18.4 server had Bash permission set to ask, provisioning failed with platform 500 and WebSocket handshake failures (request_id=7db65f61778980ab644c439b7500628b). All created resources were subsequently deleted and verified absent. Platform live acceptance remains blocked, not passed.

@bandzalkin bandzalkin changed the title fix(acp): preserve busy sessions and defer rejected turns fix(integrations): preserve busy ACP sessions and defer rejected turns Oct 5, 2026

@bandzalkin bandzalkin left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architectural review → fix round

Scope: Reviewed #731 from merge base 8921c8ce8e5b78d4264a5e286880df87c74dc8a9 through correction commit ef1964426990d55474dc1276fcbaae5390326897, including protocol, ACP adapter, execution, regression, documentation, and changelog changes. Outcome: structured busy rejection applies backpressure without terminating the borrowed autonomous session or exhausting the delivery.

Constraints: Only RequestError(-32003, data.reason=session_busy) proves non-acceptance. Unknown or accepted work keeps ordinary timeout/cancellation semantics. ACP owns that proof; ExecutionContext owns watchdog, delivery retry, stop/play, and shutdown. Local contact events have no REST row to recover. Bootstrap/restoration provenance is removed with session cleanup.

Decisions: Keep a single total adapter deadline, fresh collector/emitter per request attempt, and a typed CancelledError carrying proven non-acceptance. The runtime converts only its own watchdog cancellation into deferral and preserves shutdown/control priority. Retain deferred local events at their FIFO position instead of inventing durable rows or consuming them. This is smaller than duplicating ACP error recognition in the runtime or creating another retry queue.

Findings and proof: A1 rejected-attempt contamination, A2 watchdog budget loss, and local-event loss were confirmed and corrected; cleanup provenance and shutdown races were checked. No standalone blocker remains. Fresh gate: 6,315 passed / 684 skipped; focused ACP/control tests 80 passed; all pre-commit hooks and both lock checks passed; baseline E2E collection 526 items / 1 skipped; markdown snippets 23 passed. Standalone real ACP socketpair smoke demonstrated watchdog deferral, same-session recovery, and an accepted retry's visible answer after unrelated autonomous tool output.

Architectural verdict: APPROVED for #731 independently. Generic OMP 18.6.0 errors remain ordinary errors; structured 18.6.1 rejection is supported. Combined integration with #729 is HELD: its live external-effect recorder commits rejected-attempt effects to the shared ledger before prompt success. The minimal staging correction belongs in #729, not a second policy in #731. This is not remote CI or merge approval.

github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 8, 2026
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 8, 2026
Squashed from band-ai#754 at 3dc3a7a, resolved over band-ai#731's session-busy retry loop: the emitter drops the retired mention argument, and band-ai#731's busy-recovery tests post their reply through the Band tool (or assert the closing thought when no Band tool is injected).

Upstream-PR: band-ai#754
bandzalkin added a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 8, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 8, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
bandzalkin added a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 9, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 10, 2026
Squashed from band-ai#731 at a4d966f, resolved over upstream's band-ai#754: the busy-retry loop builds the emitter without the retired mention argument, the busy-recovery tests reply through the Band tool (FakeACPAgent.reply) or assert the closing thought when no Band tool is injected, and docs/acp.md keeps both the busy-session and the 4.x-to-5.x upgrade sections.

Upstream-PR: band-ai#731
@AlexanderZ-Band
AlexanderZ-Band force-pushed the fix/acp-session-busy-backpressure branch from a4d966f to 092bbfe Compare October 10, 2026 06:56
bandzalkin added a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 10, 2026
Squashed from band-ai#731 at 092bbfe; retains upstream auxiliary-claim test imports.

Upstream-PR: band-ai#731
github-actions Bot pushed a commit to bandzalkin/band-sdk-python that referenced this pull request Oct 10, 2026
Squashed from band-ai#731 at 092bbfe; retains upstream auxiliary-claim test imports.

Upstream-PR: band-ai#731
@linear-code

linear-code Bot commented Oct 11, 2026

Copy link
Copy Markdown

INT-1754

…hable

Rebuild INT-1754 from main with one rejection signal, shared control handling, and the existing room scheduler. Preserve context and ACP sessions across proven non-acceptance.
@AlexanderZ-Band
AlexanderZ-Band force-pushed the fix/acp-session-busy-backpressure branch from c121aab to 9856902 Compare October 11, 2026 07:21
@AlexanderZ-Band AlexanderZ-Band changed the title fix(integrations): preserve busy ACP sessions and defer rejected turns fix: preserve unaccepted work while keeping active-turn controls reachable Oct 11, 2026

@AlexanderZ-Band AlexanderZ-Band left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two findings: missing runtime rejection diagnostics and REST lookup ownership. The remaining four review groups and the independent Logical Bugs skeptic found no confirmed defects. The findings will be addressed through the verified fix workflow.

Comment thread src/band/runtime/execution.py
Comment thread src/band/platform/link.py
@AlexanderZ-Band
AlexanderZ-Band dismissed their stale review October 11, 2026 07:57

Both findings are fixed in 3511aac and 45953d1. Their threads are resolved. The real HTTP-boundary regressions and complete local suite pass; this review no longer requests changes.

@AlexanderZ-Band AlexanderZ-Band left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against INT-1754 and its finalized plan. One nonblocking hygiene nit; no confirmed design, runtime, test-quality, edge-flow, or logical-bug findings.

Comment thread src/band/runtime/oneshot.py Outdated
@AlexanderZ-Band
AlexanderZ-Band added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 1904541 Oct 11, 2026
30 checks passed
@AlexanderZ-Band
AlexanderZ-Band deleted the fix/acp-session-busy-backpressure branch October 11, 2026 09:30
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