Local and A2A dispatch
Harn trigger handlers can move between in-process execution and A2A dispatch
without changing the handler's input contract. Keep the trigger id, provider
match, retry policy, and handler source stable; change only the handler target
when the boundary changes:
# local
handler = "handlers::triage_issue"
# federated
handler = "a2a://reviewer.example/triage"
The dispatcher still records the same core lifecycle shape:
dispatch_startedattempt_recordeddispatch_succeeded,dispatch_failed,dispatch_waiting, ordlq_moved- a trust record for the terminal dispatch decision
- action-graph nodes and edges for replay comparison
Choosing a mode
Use a local handler when the workflow should run inside the current Harn process:
- development and deterministic test fixtures
- low-latency handlers with no cross-tenant boundary
- handlers that need direct access to local host capabilities already granted to the current orchestrator
Use A2A dispatch when execution crosses an agent or trust boundary:
- a managed Harn Cloud worker owns the handler
- a federated agent has separate credentials, quotas, or receipts
- an operator needs to see that the work ran remotely
- retries and DLQ movement should treat the remote endpoint as a destination
Trust and replay
Local and A2A dispatch share typed TriggerEvent input and JSON output
semantics. A2A wraps the event in a harn.trigger.dispatch envelope, sends it
with message/send, and unwraps the completed task's final text part as the
logical handler result.
The difference is recorded as metadata, not as a handler-source requirement. Action-graph nodes and trust records include:
trust_boundary:local_process,federated_a2a,event_log_worker_queue, orpersona_runtimeexecution_location:in_process,remote,queued, ormanaged_personaremote_identity: the A2A target agent label for remote dispatch- A2A task metadata such as
task_id,state,card_url,rpc_url, andremote_agent_idwhen the remote card exposes it
Replay comparisons should assert the logical output and event shape while
allowlisting location-specific metadata such as trust_boundary,
remote_identity, remote URLs, and generated task ids.
Failure modes
A2A dispatch fails closed for trust-boundary denials:
- cleartext HTTP endpoints require
allow_cleartext = true - agent-card authority mismatches are denied
- HTTP
401/403dispatch responses are denied - A2A
rejectedtask state becomes a permission-denied dispatch failure
Remote timeouts are classified as timeout failures and remain retryable under the trigger's retry policy. Incompatible A2A response schemas are protocol failures and move to DLQ when retries are exhausted.
Demo
The reference flow is in examples/triggers/local-a2a-dispatch/.
scripts/demo_local_a2a_dispatch.sh
The script starts a local harn serve A2A receiver on a random port, fires the
same deterministic issue event through a local handler and through A2A, and
prints whether the logical outputs match.