molecule-core/workspace
Hongming Wang 146c0e7c60 fix(a2a-client): recognize poll-mode 'queued' envelope (#2967)
workspace-server's a2a_proxy poll-mode short-circuit returns

    {status: "queued", delivery_mode: "poll", method: <a2a_method>}

when the peer has no URL to dispatch to (poll-mode peers, including
every external molecule-mcp standalone runtime). The bare
send_a2a_message parser only knew about JSON-RPC {result, error}
keys, so this envelope fell through to the "unexpected response shape"
error path. Two production symptoms on the reno-stars tenant traced
to it:

1. File transfer logged as failed when it actually succeeded —
   operator-facing logs showed an A2A_ERROR but the receiving
   workspace did get the chunked file via the agent's fallback path.
2. delegate_task retried after the false failure → peer received
   duplicate delegations → conversation got confused, the second
   peer self-diagnosed in a notify ("⚠️ Peer 二次请求 — 我先不执行").

Add a third branch to the parser, BETWEEN the existing JSON-RPC
{result, error} cases and the catch-all "unexpected" fallback. The
queued envelope is delivery-acknowledged-but-pending-consumption —
not an error — so it returns a clean success string the agent can
render as a normal outcome. The success string includes "queued"
and "poll" so an operator scanning logs sees the routing path
without parsing JSON.

Defensive: the new branch only fires when BOTH status="queued" AND
delivery_mode="poll" are present. A partial envelope (one key
missing) still falls through to the catch-all, so a future server
bug that emits a malformed shape gets surfaced instead of silently
swallowed.

Tests:
- test_poll_queued_envelope_returns_success_string — pins the canonical
  envelope returns a non-error string. Discriminating: verified to FAIL
  on old code (returned [A2A_ERROR] string), PASS on new.
- test_poll_queued_envelope_with_other_method — pins the parser doesn't
  hardcode message/send. Discriminating: also FAILS on old code.
- test_status_queued_without_poll_mode_still_falls_through — pins both
  keys are required (defensive against future server bugs).

12 existing tests in TestSendA2AMessage still pass — no regression.

Scope: hotfix for the bare send_a2a_message path. The full SSOT
typed-A2AResponse refactor (#158-#163, parents under #2967) covers the
broader vocabulary alignment between Go server and Python client. This
PR ends the production symptoms now without preempting that work.
2026-05-05 16:58:48 -07:00
..
adapters fix: comprehensive a2a-sdk 1.x migration sweep across workspace/ 2026-04-27 09:42:57 -07:00
builtin_tools feat(harness): coordinator phase-boundary instrumentation for RFC #2251 2026-04-28 20:11:46 -07:00
lib feat(workspace): pre-stop serialization for pause/resume (closes #1386) 2026-04-21 12:40:44 +00:00
molecule_audit chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
platform_tools feat(mcp): multi-workspace routing for memory + chat_history + workspace_info 2026-05-04 14:17:58 -07:00
plugins_registry feat(plugin): implement MCPServerAdaptor (issue #847) 2026-04-24 01:42:13 +00:00
policies feat(platform): single-source-of-truth tool registry — adapters consume, no drift 2026-04-28 17:11:36 -07:00
scripts fix(git-token-helper): close TOCTOU window + stop swallowing chmod errors (closes #1552) 2026-04-26 08:22:29 -07:00
skill_loader feat(skills): per-skill runtime compatibility (#119, hermes pattern) 2026-04-27 01:57:43 -07:00
tests fix(a2a-client): recognize poll-mode 'queued' envelope (#2967) 2026-05-05 16:58:48 -07:00
.coveragerc test(workspace): centralize pytest-cov config + 92% floor (closes #1817) 2026-04-26 06:21:22 -07:00
a2a_cli.py fix(runtime): use lowercase wire role for v0.3 JSON-RPC compat layer 2026-04-27 12:40:11 -07:00
a2a_client.py fix(a2a-client): recognize poll-mode 'queued' envelope (#2967) 2026-05-05 16:58:48 -07:00
a2a_executor.py fix(a2a): route terminal Message via TaskUpdater.complete/failed in task mode 2026-05-03 04:06:45 -07:00
a2a_mcp_server.py fix(onboarding): address Claude Code MCP onboarding friction (#2934) 2026-05-05 14:19:09 -07:00
a2a_tools_delegation.py refactor(workspace): extract delegation handlers from a2a_tools.py to a2a_tools_delegation.py (RFC #2873 iter 4b) 2026-05-05 05:00:52 -07:00
a2a_tools_inbox.py refactor(workspace): extract inbox tools from a2a_tools.py (RFC #2873 iter 4e) 2026-05-05 14:28:58 -07:00
a2a_tools_memory.py refactor(workspace): extract memory tools from a2a_tools.py to a2a_tools_memory.py (RFC #2873 iter 4c) 2026-05-05 09:50:39 -07:00
a2a_tools_messaging.py refactor(workspace): extract messaging tools from a2a_tools.py to a2a_tools_messaging.py (RFC #2873 iter 4d) 2026-05-05 09:50:47 -07:00
a2a_tools_rbac.py refactor(workspace): extract RBAC helpers from a2a_tools.py to a2a_tools_rbac.py (RFC #2873 iter 4a) 2026-05-05 04:43:16 -07:00
a2a_tools.py refactor(workspace): extract inbox tools from a2a_tools.py (RFC #2873 iter 4e) 2026-05-05 14:28:58 -07:00
adapter_base.py feat: drop shared_context — use memory v2 team namespace instead 2026-05-04 16:30:26 -07:00
agent.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
agents_md.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
boot_routes.py test(runtime): pin PR #2756's card-vs-setup decoupling with build_routes helper 2026-05-04 14:59:56 -07:00
build-all.sh fix: update workspace script comments for workspace-template → workspace rename 2026-04-18 01:48:05 -07:00
card_helpers.py fix(runtime): isolate card-skill enrichment + transcript handler from adapter shape mismatch 2026-05-04 14:15:27 -07:00
config.py feat: drop shared_context — use memory v2 team namespace instead 2026-05-04 16:30:26 -07:00
configs_dir.py fix(runtime): auto-fallback CONFIGS_DIR for non-container hosts (closes #2458) 2026-05-01 13:07:55 -07:00
consolidation.py fix: apply #1124 env-var defaults + scrub F1088 credentials from INCIDENT_LOG.md (#1347) 2026-04-21 08:11:44 +00:00
coordinator.py feat: drop shared_context — use memory v2 team namespace instead 2026-05-04 16:30:26 -07:00
Dockerfile feat(workspace): 45-min gh-token refresh daemon + credential helper cache 2026-04-22 19:52:46 -07:00
entrypoint.sh fix(workspace): credential helper security hardening (#1797) 2026-04-23 18:14:55 +00:00
event_log.py feat(workspace): event_log module + EventLogConfig (#119 PR-2) 2026-05-03 00:17:12 -07:00
events.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
executor_helpers.py docs(a2a): correct misleading v1-tolerance comments 2026-05-02 02:33:00 -07:00
heartbeat.py feat(workspace): wire observability config into heartbeat + uvicorn (#119 PR-3a) 2026-05-03 01:01:57 -07:00
inbox_uploads.py fix(inbox-uploads): cancel BatchFetcher futures on wait_all timeout 2026-05-05 12:34:41 -07:00
inbox.py fix(inbox): drop unused batch_fetcher = None after end-of-batch drain 2026-05-05 11:56:54 -07:00
initial_prompt.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
internal_chat_uploads.py fix(workspace): surface errno + path on chat-upload mkdir failure 2026-05-01 11:47:53 -07:00
internal_file_read.py feat(chat_files): rewrite Download as HTTP-forward (RFC #2312, PR-D) 2026-04-29 15:19:02 -07:00
main.py test(runtime): pin PR #2756's card-vs-setup decoupling with build_routes helper 2026-05-04 14:59:56 -07:00
mcp_cli.py feat(mcp): add molecule-mcp doctor onboarding diagnostic 2026-05-05 15:44:36 -07:00
mcp_doctor.py fix(mcp-doctor): heartbeat (idempotent) instead of register (UPSERT) 2026-05-05 16:11:08 -07:00
mcp_heartbeat.py refactor(workspace): split mcp_cli.py (626 LOC) into focused modules (RFC #2873 iter 3) 2026-05-05 04:33:06 -07:00
mcp_inbox_pollers.py refactor(workspace): split mcp_cli.py (626 LOC) into focused modules (RFC #2873 iter 3) 2026-05-05 04:33:06 -07:00
mcp_workspace_resolver.py mcp: surface specific TOKEN_FILE errors + link follow-ups (#2934) 2026-05-05 15:07:15 -07:00
molecule_ai_status.py fix(runtime): replace remaining /app/ legacy paths in agent prompts + docstrings 2026-04-27 11:22:00 -07:00
not_configured_handler.py fix(runtime): redact secret-shaped tokens from JSON-RPC error.data 2026-05-04 15:07:53 -07:00
platform_auth.py feat(mcp): cross-workspace delegation routing (multi-ws PR-2) 2026-05-04 08:32:24 -07:00
platform_inbound_auth.py fix(runtime): auto-fallback CONFIGS_DIR for non-container hosts (closes #2458) 2026-05-01 13:07:55 -07:00
plugins.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
preflight.py fix(preflight): downgrade required_env + auth_token failures to warnings 2026-05-04 12:20:34 -07:00
prompt.py feat: drop shared_context — use memory v2 team namespace instead 2026-05-04 16:30:26 -07:00
pytest.ini feat(preflight): replace SUPPORTED_RUNTIMES static list with adapter discovery 2026-04-27 00:44:51 -07:00
rebuild-runtime-images.sh fix: update workspace script comments for workspace-template → workspace rename 2026-04-18 01:48:05 -07:00
requirements.txt chore(deps)(deps): update starlette requirement in /workspace 2026-05-03 01:36:45 +00:00
runtime_wedge.py chore(workspace): drop claude_sdk_executor — Phase 2 of #87 2026-04-27 00:52:55 -07:00
secret_redactor.py fix(runtime): redact secret-shaped tokens from JSON-RPC error.data 2026-05-04 15:07:53 -07:00
shared_runtime.py feat(platform): single-source-of-truth tool registry — adapters consume, no drift 2026-04-28 17:11:36 -07:00
smoke_mode.py chore(smoke): runtime_wedge follow-ups from PR #2473 review 2026-05-01 18:01:51 -07:00
transcript_auth.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00
watcher.py chore: open-source restructure — rename dirs, remove internal files, scrub secrets 2026-04-18 00:24:44 -07:00