Skip to content

Preserve cross-app task routes across discovery loss#2369

Draft
3mdistal wants to merge 2 commits into
mainfrom
codex/a2a-route-continuity-pr1
Draft

Preserve cross-app task routes across discovery loss#2369
3mdistal wants to merge 2 commits into
mainfrom
codex/a2a-route-continuity-pr1

Conversation

@3mdistal

@3mdistal 3mdistal commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Problem

Cross-app ask_app submissions can succeed while later ask_app_status calls lose the receiver. The status tool currently rediscovers the destination on every poll, so a cold start or temporary directory failure can turn a valid Content task into “No reachable ask_app task route.” Separately, standalone serverless bundles can identify themselves from the generated package name (traced-node-modules) instead of the configured app ID.

For mutation-capable work, that ambiguity is dangerous: a caller may submit a replacement task even though the original still exists.

Approach

Use discovery only for the initial submission. When a durable task is accepted, return an encrypted, authenticated task handle that records the exact receiver route and task ID. Polling verifies that handle and reuses the recorded route without rediscovery.

The handle is routing metadata, not authorization. The receiver’s existing bearer authentication, task-owner check, and not-found behavior remain authoritative.

What changed

  • Make the configured MCP appId authoritative for standalone list_apps identity.
  • Return a one-hour opaque task handle for durable ask_app submissions, bound to the serving app and authenticated caller identity.
  • Let ask_app_status poll the recorded route across discovery loss while preserving legacy { app, taskId } polling during migration.
  • Reject unknown named destinations before invoking the local agent.
  • Cover route continuity, no duplicate submission, tampering, expiry, caller/connector binding, canonical identity, legacy polling, and same-app behavior.
  • Document the handle-first contract in English and all ten localized documentation sets, with a Core patch changeset.

Safety and operations

  • Task handles use authenticated encryption with a purpose-derived key from existing server secrets. Modified, expired, wrong-user, and wrong-connector handles return the same opaque error.
  • A verified handle does not bypass A2A authentication or receiver-side ownership checks.
  • The recorded endpoint is validated as credential-free HTTP(S), and the A2A client retains its normal SSRF-safe transport.
  • No schema, migration, database, credential, or user-data changes are included.
  • Rollback removes handle issuance/verification and restores discovery-based legacy polling; it cannot undo tasks already accepted by receivers.

Verification

Passed on commit 2a900855373c5202a511e3779cf1e83eeb8ad766:

  • Core MCP suite: 33 files / 516 tests.
  • Focused task-handle and cross-app suites: 2 files / 51 tests.
  • Core TypeScript check.
  • Localized documentation guard across 17 catalog directories.
  • Formatting, diff integrity, and changeset validation.
  • Independent final-diff review found no implementation, security, authorization, or compatibility blocker; a follow-up documentation re-review also passed.
  • All GitHub Actions CI jobs passed. Additional unrelated monorepo Netlify contexts for starter/design sites were still pending at handoff and are not counted as Calendar/Content acceptance. The broad Fast tests job passed on its single rerun after an unrelated 5,000 ms boundary test hit Vitest’s own 5,000 ms timeout under load.
  • Exact Calendar preview 6a6273dd5e9dc200085c607a and Content preview 6a6273ddf691d50007c2b924 are ready at this commit.

Still required before this PR is ready:

  • Re-authorize the installed Calendar MCP connector, then run the real authenticated Calendar → Content acceptance story against the exact previews, including same-handle polling after discovery loss and proof that no replacement task is submitted. The current saved refresh grant returns invalid_grant; the harness therefore could not authenticate the Calendar preview. No credential state was changed during this Work pass.

Review focus

  • Does the handle remain strictly routing metadata while preserving all target-side authentication and owner checks?
  • Are key derivation, expiry, identity binding, and generic failure behavior sufficient for a serverless cross-app route receipt?
  • Is fail-closed handling for unknown named destinations the right compatibility boundary alongside legacy status polling?

Follow-up boundary

This PR does not change async processor lifetime or Netlify background execution. A separate PR owns the observed five-minute caller timeout / stuck processor path.

@github-actions

Copy link
Copy Markdown
Contributor

Visual recap — skipped

The visual recap job did not run for this pull request. This is informational only and does not block the PR.

Recap skipped for 2a90085: draft PR.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

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.

1 participant