Skip to content

Derive meaningful names for external sub-agents instead of using 'root'#2132

Open
dgageot wants to merge 1 commit intodocker:mainfrom
dgageot:board/sub-agents-can-be-oci-reference-but-they-134a0dbe
Open

Derive meaningful names for external sub-agents instead of using 'root'#2132
dgageot wants to merge 1 commit intodocker:mainfrom
dgageot:board/sub-agents-can-be-oci-reference-but-they-134a0dbe

Conversation

@dgageot
Copy link
Member

@dgageot dgageot commented Mar 16, 2026

External sub-agents referenced via OCI images or URLs kept their internal
"root" name, colliding with the parent config's root agent.

Changes

  • Name derivation: External agents are automatically named after their last
    path segment (e.g. agentcatalog/review-prreview-pr), with tags and
    digests stripped.
  • Explicit naming: Support name:reference syntax for custom names
    (e.g. reviewer:agentcatalog/review-pr).
  • Collision detection: Reject configs where an external agent's resolved name
    conflicts with a locally-defined agent, both at config validation time and at
    agent loading time.
  • Updated schema descriptions, example YAML, and tests.

External sub-agents referenced via OCI images or URLs now get a proper
name derived from their reference (last path segment, without tag) instead
of keeping their internal 'root' name which collides with the parent config.

Support explicit naming with 'name:reference' syntax (e.g.
'reviewer:agentcatalog/review-pr') and automatic base name extraction
(e.g. 'agentcatalog/review-pr' becomes 'review-pr').

Detect and reject name collisions at both config validation time and
agent loading time to prevent external agents from silently shadowing
locally-defined agents.

- Add ParseExternalAgentRef and externalRefBaseName in pkg/config/resolve.go
- Rename loaded external agents in pkg/teamloader/teamloader.go
- Validate name collisions in pkg/config/config.go
- Update schema, example, and tests

Assisted-By: docker-agent
@dgageot dgageot requested a review from a team as a code owner March 16, 2026 18:19
Copy link

@docker-agent docker-agent bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Assessment: 🟡 NEEDS ATTENTION

This PR introduces automatic name derivation for external sub-agents, which is a valuable feature. However, there are several issues with the collision detection and caching logic that could cause problems in production:

Key Issues Found

  1. Collision detection incomplete (pkg/config/config.go:139): The validation only checks for collisions between external agents and local agents, but doesn't detect when multiple external references derive the same name (e.g., org1/helper and org2/helper both resolving to helper).

  2. External agent caching broken (pkg/teamloader/teamloader.go:534, 560): The cache uses the original reference string as the key instead of the normalized external reference. This means reviewer:agentcatalog/review-pr and agentcatalog/review-pr are treated as different cache entries, causing the same external agent to be loaded multiple times.

Recommendation

These issues should be addressed before merging:

  • Extend validateConfig to track and detect collisions between external agent names
  • Update the caching logic to use the normalized externalRef as the cache key instead of the original ref string

The test coverage is good, but consider adding tests for:

  • Multiple external references deriving the same name
  • The same external reference accessed with different syntax forms

}
if IsExternalReference(subAgentRef) {
name, _ := ParseExternalAgentRef(subAgentRef)
if allNames[name] {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM: Collision detection incomplete

The validation checks for collisions between external agents and locally-defined agents, but doesn't detect when multiple external references derive the same name.

For example, if a config has both org1/helper and org2/helper as sub-agents, both would derive the name helper, but this collision wouldn't be caught during validation. The collision would only be detected at runtime in resolveAgentRefs.

Recommendation: Track external agent names in validateConfig to detect these collisions early:

externalNames := make(map[string]string) // name -> first ref that claimed it
for _, subAgentRef := range agent.SubAgents {
    if IsExternalReference(subAgentRef) {
        name, _ := ParseExternalAgentRef(subAgentRef)
        if allNames[name] {
            return fmt.Errorf("...")
        }
        if firstRef, exists := externalNames[name]; exists {
            return fmt.Errorf("external agents '%s' and '%s' both resolve to name '%s'", firstRef, subAgentRef, name)
        }
        externalNames[name] = subAgentRef
    }
}

}

a, err := loadExternalAgent(ctx, ref, runConfig, loadOpts)
agentName, externalRef := config.ParseExternalAgentRef(ref)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM: External agent caching uses wrong key

The cache check at line 534 uses the original reference string (ref) instead of the normalized external reference (externalRef returned here). This means different syntax forms of the same reference won't hit the cache.

For example:

  • First reference: reviewer:agentcatalog/review-pr → loads and caches under key reviewer:agentcatalog/review-pr
  • Second reference: agentcatalog/review-pr → cache miss, loads the same agent again

Both references normalize to externalRef = "agentcatalog/review-pr", but the cache at line 534 checks externalAgents[ref] using the original string, and line 560 stores using the original string.

Recommendation: Use externalRef as the cache key in both the check (line 534) and the store (line 560):

// At line 534:
if a, ok := externalAgents[externalRef]; ok {
    resolved = append(resolved, a)
    continue
}

// At line 560:
externalAgents[externalRef] = a

agent.WithName(agentName)(a)

*agents = append(*agents, a)
externalAgents[ref] = a
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM: External agent caching stores with wrong key

The cache stores the loaded agent using the original reference string (ref) instead of the normalized external reference (externalRef). This is the storage side of the caching issue.

This means reviewer:agentcatalog/review-pr and agentcatalog/review-pr will be stored as separate cache entries, even though they refer to the same external agent.

Recommendation: Store using externalRef as the cache key:

externalAgents[externalRef] = a

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