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.agent → ai) 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
- Add the field on
ExtensionVersion with a clear doc comment explaining the override semantics and the recommended use of requiredAzdVersion to gate it.
- Introduce a small helper (e.g.
func (ext *ExtensionMetadata) NamespaceForVersion(v *ExtensionVersion) string) so callers don't repeat the fallback logic.
- 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).
- 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.
- Schema: update
cli/azd/extensions/registry.schema.json to document the new optional field.
- 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:
- No existing registry entry for this extension id → write top-level
namespace from extension.yaml as today. New extension, nothing to preserve.
- Existing entry, local namespace matches existing top-level → no change to top-level; no version-level override needed.
- 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.
Summary
Add a per-version
namespaceoverride onExtensionVersionin the registry, used at install time. This enables an extension to evolve its top-level command namespace across versions (e.g.ai.agent→ai) without breaking older azd × older extension-version combinations.Background
The registry today has
namespaceonly at the extension level (pkg/extensions/registry.go,ExtensionMetadata.Namespace). WhenManager.Installrecords an installed extension, it snapshots that extension-level value into the local user config. Because there is no per-version override, flipping the extension-levelnamespacein 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.agentsextension wants to move from namespaceai.agenttoaiso it can coexist with sibling extensions likeazure.ai.finetune(ai.finetuning) andazure.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
namespacefield onExtensionVersion(registry.go) that overrides the extension-level namespace for installs of that specific version.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.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:namespace: "ai.agent"so old azd installing old versions still resolves to the namespace those old binaries were 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:
ai.agent)requiredAzdVersionai.agent)ai)Suggested implementation outline
ExtensionVersionwith a clear doc comment explaining the override semantics and the recommended use ofrequiredAzdVersionto gate it.func (ext *ExtensionMetadata) NamespaceForVersion(v *ExtensionVersion) string) so callers don't repeat the fallback logic.Manager.Installto use the helper when building the localExtensionrecord. Audit other call sites that readExtensionMetadata.Namespaceand 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).cli/azd/extensions/registry.schema.jsonto document the new optional field.cli/azd/docs/extensions/extension-framework.mdin the namespace section explaining that a version can override the extension-level namespace, and the recommended pairing withrequiredAzdVersionwhen shipping a breaking namespace change.Related
azd x publishmust avoid clobbering top-levelnamespaceThe
microsoft.azd.extensionspublish flow currently unconditionally rewrites the registry entry's top-level namespace from the localextension.yaml:That would silently undo the backcompat strategy: as soon as someone runs
azd x publishon a newazure.ai.agents(whoseextension.yamldeclaresnamespace: ai), the registry's top-levelnamespacefor that extension flips fromai.agenttoai, 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.yamldeclares a different namespace. Suggested behavior:namespacefromextension.yamlas today. New extension, nothing to preserve.versions[<this version>].namespaceto 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 publishinvocation.