Skip to content

bug(init/google-adk): infer App root_agent instead of selecting the first literal sub-agent #324

Closed

Description

@pengfei-threemoonslab

Summary

init --write can select the first literal Google ADK sub-agent name as agent.name even when the module explicitly constructs an App(root_agent=...) with a different coordinator.

This creates a schema-valid but semantically misleading manifest.

Reproduction

Observed against google/adk-samples#1745 with Agents Shipgate 0.16.0b7, contract 19.

The source has two literal sub-agents:

salesforce_agent = LlmAgent(name="SalesforceAgent", ...)
sap_agent = LlmAgent(name="SapAgent", ...)

The coordinator is separately bound as the application root:

root_agent = LlmAgent(
    name=AGENT_NAME,
    sub_agents=[salesforce_agent, sap_agent],
    ...
)
app = App(name="smart_closer_app", root_agent=root_agent)

AGENT_NAME defaults to SmartCloserAgent in the adjacent config module.

Observed init result:

{
  "agent_name": "SalesforceAgent",
  "agent_name_candidates": [
    {"value":"SalesforceAgent","source":"Agent_name_literal"},
    {"value":"SapAgent","source":"Agent_name_literal"}
  ]
}

The generated manifest therefore declared the Salesforce worker as the reviewed agent identity.

Expected behavior

Prefer structural root evidence over source-order literal candidates:

  1. Resolve App(root_agent=<symbol>).
  2. Follow the assignment for that symbol to the corresponding Agent/LlmAgent construction.
  3. Resolve simple local constants/defaults when safe and static.
  4. If the root name remains dynamic or ambiguous, emit CHANGE_ME plus a diagnostic instead of asserting a sub-agent identity.

This is related to candidate-quality issue #320, but it is a distinct hierarchy problem: both literal names are plausible agent names; neither is the application root.

Acceptance criteria

  • An ADK App(root_agent=root_agent) fixture selects the root coordinator, not the first sub-agent encountered.
  • Root assignment and sub-agent relationships are represented in detection evidence.
  • Simple imported/default configuration constants can contribute evidence without importing user code.
  • Ambiguous dynamic names fail closed to a placeholder and explicit review action.
  • agent_name_candidates include role/provenance metadata sufficient to explain the ranking.

Activity

  1. added this to the v0.17.0 milestone on Aug 7, 2026
  2. added
    bugSomething isn't working
    P1Picked up inside the current milestone window; keep about 15 open
    area:initinit / detect / manifest generation
    area:adapterFramework input adapters and tool catalog construction
    on Aug 7, 2026
  3. pengfei-threemoonslab commented on Aug 7, 2026

    @pengfei-threemoonslab
    ContributorAuthor

    Confirmed at 7e0bfdf5. As traced in #320, _agent_name_candidates (cli/discovery/signals.py:567) appends literals in file-then-AST order with no ranking, and cli/_register_init.py:363-364 takes the first with an accepted source — so SalesforceAgent won by position alone. Nothing in candidate generation consults App(root_agent=...) at all; the structural root evidence is not merely out-ranked, it is never collected.

    Worth noting the detection layer already knows the relevant class surface — signals.py:101 has GOOGLE_ADK_AGENT_CLASSES = {"Agent", "LlmAgent"} and :315 resolves literal constructions — so this is an extraction gap in one function, not a missing understanding of ADK.

    This is the more severe half of the #320 pair and should lead. #320 produces an obviously wrong name (t) that a human notices immediately. This produces a plausible wrong name: SalesforceAgent is a real agent in the repository, so a reviewer skimming the manifest has no signal that the declared identity is a worker rather than the coordinator. A wrong-but-plausible identity is the one that survives review.

    The proposed resolution order is right, and step 3 ("resolve simple local constants/defaults when safe and static") is the one to bound carefully: AGENT_NAME living in an adjacent config module means the safe version is a single-hop, same-package, literal-assignment lookup. Anything more — conditionals, f-strings, imports from outside the package — should fail closed to the placeholder rather than growing into partial evaluation of user code. The static-only boundary is easy to erode one convenience at a time here.

    The last acceptance criterion — agent_name_candidates carrying role and provenance metadata — is the durable one. Without it, whichever ranking lands is untestable from the output and the next regression looks identical to correct behavior.

    Labeled P1 / v0.17.0.

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

    P1Picked up inside the current milestone window; keep about 15 openarea:adapterFramework input adapters and tool catalog constructionarea:initinit / detect / manifest generationbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions