Skip to content

Add per-version namespace override on ExtensionVersion in the extension registry #8135

Description

@JeffreyCA

Summary

Add a per-version namespace override on ExtensionVersion in the registry, used at install time. This enables an extension to evolve its top-level command namespace across versions (e.g. ai.agentai) without breaking older azd × older extension-version combinations.

Background

The registry today has namespace only at the extension level (pkg/extensions/registry.go, ExtensionMetadata.Namespace). When Manager.Install records an installed extension, it snapshots that extension-level value into the local user config. Because there is no per-version override, flipping the extension-level namespace in the official registry would simultaneously change the namespace reported for all historical versions of that extension, including old binaries that were built and tested against the previous namespace. That makes any namespace change a hard breaking change for existing installs.

This came up while working on #8109 (shared namespace prefixes). The azure.ai.agents extension wants to move from namespace ai.agent to ai so it can coexist with sibling extensions like azure.ai.finetune (ai.finetuning) and azure.ai.models (ai.models). The core support for sharing namespace prefixes landed in that PR, but we can't actually update the official registry to use the new namespace without a per-version override, because old azd × old version installs would break.

Scope

  • Add an optional namespace field on ExtensionVersion (registry.go) that overrides the extension-level namespace for installs of that specific version.
  • Update Manager.Install (and any related code paths that capture the namespace into the local installed record) to prefer the version-level value when present, falling back to the extension-level value.
  • Old azd versions silently ignore the new field (JSON unmarshal drops unknown fields), so the change is backward-compatible in the registry.
  • Add tests covering both code paths (override present / absent).
  • Brief mention in the extension-framework docs alongside requiredAzdVersion.

Out of scope: changing the registry contents itself; that happens after this ships in a release.

Backward-compatibility strategy (for the registry update that follows this PR)

Once this lands and ships in an azd release, the official registry can be updated like this for azure.ai.agents:

  • Keep extension-level namespace: "ai.agent" so old azd installing old versions still resolves to the namespace those old binaries were built for.
  • New versions of the extension that are built for namespace: "ai" include both:
    • versions[].namespace: "ai" — used by new azd.
    • versions[].requiredAzdVersion: ">= <release that contains this fix and #8109>" — prevents old azd from installing versions whose binaries no longer match the extension-level namespace.

Outcome matrix once the registry is updated:

azd version extension version installed resolved namespace OK?
old old extension-level (ai.agent)
old new filtered out by requiredAzdVersion
new old extension-level (ai.agent)
new new version-level override (ai)

Suggested implementation outline

  1. Add the field on ExtensionVersion with a clear doc comment explaining the override semantics and the recommended use of requiredAzdVersion to gate it.
  2. Introduce a small helper (e.g. func (ext *ExtensionMetadata) NamespaceForVersion(v *ExtensionVersion) string) so callers don't repeat the fallback logic.
  3. Update Manager.Install to use the helper when building the local Extension record. Audit other call sites that read ExtensionMetadata.Namespace and consider whether they should also use the helper (the install path is the only one that matters for binding, but consistency is worth a quick check).
  4. Tests: a registry fixture with a version-level override + a no-override version, asserting that the resolved namespace differs accordingly. Add a test that registers an installed-record with the resolved namespace and verifies the local record reflects it.
  5. Schema: update cli/azd/extensions/registry.schema.json to document the new optional field.
  6. Docs: add a short paragraph to cli/azd/docs/extensions/extension-framework.md in the namespace section explaining that a version can override the extension-level namespace, and the recommended pairing with requiredAzdVersion when shipping a breaking namespace change.

Related

azd x publish must avoid clobbering top-level namespace

The microsoft.azd.extensions publish flow currently unconditionally rewrites the registry entry's top-level namespace from the local extension.yaml:

cli/azd/extensions/microsoft.azd.extensions/internal/cmd/publish.go around the addOrUpdateExtension helper:

ext.Namespace = extensionMetadata.Namespace

That would silently undo the backcompat strategy: as soon as someone runs azd x publish on a new azure.ai.agents (whose extension.yaml declares namespace: ai), the registry's top-level namespace for that extension flips from ai.agent to ai, and the table at the top of this issue collapses for old azd × old binary combos.

The publish command should be updated so the top-level namespace is preserved when it already exists in the registry and the local extension.yaml declares a different namespace. Suggested behavior:

  1. No existing registry entry for this extension id → write top-level namespace from extension.yaml as today. New extension, nothing to preserve.
  2. Existing entry, local namespace matches existing top-level → no change to top-level; no version-level override needed.
  3. Existing entry, local namespace differs from existing top-level → do NOT modify top-level. Instead set versions[<this version>].namespace to the local value, and print a warning explaining that the registry's top-level namespace is preserved for backcompat and that this version will use the override.

Optional: add a --force-namespace (or --update-toplevel-namespace) flag for the rare case where the author explicitly wants to update the top-level value (e.g. when retiring all old versions).

This change is part of the same follow-up — without it, the rollout strategy in this issue can be accidentally broken by a routine azd x publish invocation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions