Skip to content

[Task]: reverse-lookup a peer route before substituting a host sub-agent #5067

Description

@JunZ-Leo

Part of #5039 — the reverse-lookup / route-preview slice. The display-projection half landed separately (#5065, #5066).

Problem / 问题

A coordinator that is asked to route work to a named peer can currently do two things and neither is the right one: call resolve-agent-thread, which answers "which Agent owns this exact link" and therefore requires the link the coordinator does not have; or read agent-directory, which projects identity, current work and observation/delivery references but says nothing about how many bindings address that peer. With no published answer, the cheapest move is to substitute whatever worker is convenient — the behaviour #5039 reports, where the user had to supply the task link by hand.

loopx/thread_agent_binding.py already refuses to guess in the forward direction: resolve_thread_agent_binding is documented as resolving "without guessing from registry order". Nothing offered the same discipline backwards.

Proposed slice

Add the reverse lookup to the module that owns bindings, and publish its answer on the directory row:

  • summarize_agent_binding_routes(goals, *, agent_id) returns {schema_version, agent_id, outcome, address_shared, candidate_count, candidates, scope, provenance} where outcome is single_candidate (exactly one accepted binding addresses the Agent within the goals supplied), multiple_candidates (more than one) or no_candidate (none). Candidates are deduplicated in first-seen order across every Goal supplied; candidate_count is the full distinct total. address_shared records that the same host thread also names a different registered Agent — the condition the forward resolver answers conflict for — and scope is goals_supplied, so a lone candidate here is not a project-level uniqueness claim.
  • agent-directory attaches that object to each row as peer_route, and docs/reference/protocols/peer-agent-directory-and-observation-v0.md states the vocabulary.

Candidates are exactly the bindings the owner's own normalisation accepts. Measured consequence: a binding whose host_surface is absent is rejected by _normalized_host_surface inside _bindings_for_goal, so it is dropped there rather than counted here — the summary never advertises a route the forward resolver could not later match.

Explicitly not in this slice

  • It selects nothing and verifies nothing. When several candidates exist the answer is multiple_candidates, and a lone candidate sharing its address with another Agent carries address_shared: true, and the caller must still resolve an exact link before addressing the peer. No host call is made, so the unavailable and not_authorized outcomes Resolve existing peer Agent delivery routes before substituting host sub-agents #5039 lists belong to the host-verification slice, not here.
  • No second registry, scheduler or session store, and no session copying or rebinding across host/profile boundaries. thread_agent_binding.py stays the only reader of coordination.thread_agent_bindings for routing purposes; directory.py calls into it rather than parsing the payload itself.
  • The delivery / receiver-adoption / result-return identity split stays in Resolve existing peer Agent delivery routes before substituting host sub-agents #5039.

Acceptance

  • One binding reports single_candidate with that candidate; two distinct bindings report multiple_candidates and carry both; no binding reports no_candidate with an empty list.
  • A thread bound to two Agents gives each of them single_candidate and address_shared: true, agreeing with the forward resolver's conflict on the same data.
  • A candidate that fails the existing public-output boundary is withheld and declared in limitations, yet still counted in candidate_count; it is never truncated.
  • A binding republished across two Goals counts once, and five bindings yield three candidates with candidate_count: 5.
  • Another Agent's bindings never enter these candidates, and an unknown, empty, non-string or over-long agent_id reports no_candidate instead of matching loosely.
  • A malformed binding is excluded rather than counted.
  • The directory row carries peer_route while the packet's existing non-authorization guarantees (caller_membership, lease_state_not_projected) are unchanged, and all 30 pre-existing tests in the two owning files still pass unmodified.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions