Skip to content

internal: Fix descriptor-driven CLI kinds - #671

Open
timflannagan wants to merge 4 commits into
agentregistry-dev:mainfrom
timflannagan:fix/cli-kind-followups
Open

internal: Fix descriptor-driven CLI kinds#671
timflannagan wants to merge 4 commits into
agentregistry-dev:mainfrom
timflannagan:fix/cli-kind-followups

Conversation

@timflannagan

Copy link
Copy Markdown
Collaborator

Description

Follow up on #666 by completing descriptor-driven CLI kind dispatch. Extension
kinds now use the same descriptor-backed callbacks as built-in kinds, so plural
routing and tagged operations follow their registered storage behavior.

Untagged explicit deletes now use the name-only DELETE route directly instead
of issuing a preliminary GET. This removes a redundant request and allows a
caller with delete permission, but not read permission, to delete the resource.
Command tests now receive their clients through dependencies instead of shared
package state.

Change Type

/kind fix

Changelog

CLI extension kinds now honor their registered storage behavior, and deleting untagged resources no longer requires read permission.

Additional Notes

Tagged deletes still resolve the latest concrete tag when --tag is omitted.
Broader storage and CLI terminology renames remain isolated in follow-up
branches.

Align label errors and all-tags test naming with descriptor-driven kind
registration. The removed typed helper is no longer part of the CLI model.

Signed-off-by: timflannagan <timflannagan@gmail.com>
Make extension CLI commands use the same descriptor-driven callbacks as
built-in kinds.

Previously, extension registration discarded get tags and never exposed tag
listing or deletion, so tagged extension kinds behaved like untagged kinds.

Now extension plural routing and tagged capabilities come from the registered
API descriptor while aliases and table presentation remain CLI-owned.

Signed-off-by: timflannagan <timflannagan@gmail.com>
Pass server-specific runtime dependencies directly into each command test
instead of routing them through package-global client state. Tests can now run
independently and later opt into parallel execution.

Signed-off-by: timflannagan <timflannagan@gmail.com>
Allow explicit CLI deletes for untagged kinds to use their name-only DELETE
route directly.

Previously, generic deletion resolved a missing tag through GET for every
kind. Untagged resources do not have tags, so this added a request and could
reject callers that had delete permission but not read permission.

Now kind storage controls tag resolution. Tagged kinds still resolve the
latest concrete tag, while untagged kinds delete directly by namespace and
name.

Signed-off-by: timflannagan <timflannagan@gmail.com>
Copilot AI lite review requested due to automatic review settings September 1, 2026 16:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR completes descriptor-driven kind dispatch in the CLI so extension kinds use the same descriptor-backed routing/storage behavior as built-in kinds, and it refactors command tests to inject clients via dependencies instead of shared package state.

Changes:

  • Route extension-kind CRUD (pluralization, tagging capabilities, and endpoints) through v1alpha1 kind descriptors for consistent behavior with built-in kinds.
  • Optimize explicit deletes of untagged resources to call the name-only DELETE route directly (no preliminary GET), enabling delete-without-read-permission.
  • Refactor CLI command tests to pass cliruntime.Deps returned from server setup helpers rather than relying on shared global test client state.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/cli/commands/runtime_get_test.go Updates get-command tests to use dependency-injected clients.
internal/cli/commands/resources.go Updates generic delete helper to distinguish tagged vs untagged delete flows.
internal/cli/commands/get.go Aligns tagged-kind capability checks and error messaging with descriptor-driven behavior.
internal/cli/commands/get_test.go Refactors get-command tests to use injected deps and removes shared client state usage.
internal/cli/commands/extension.go Switches extension kinds to descriptor-driven kind construction and routing.
internal/cli/commands/extension_test.go Adds coverage ensuring extension kinds honor descriptor routes and storage/tagging capabilities.
internal/cli/commands/deployment_wait_test.go Updates wait-command tests to use dependency-injected clients.
internal/cli/commands/deployment_get_test.go Updates deployment get tests to use dependency-injected clients.
internal/cli/commands/deployment_delete_test.go Adjusts deployment delete test server to reflect name-only deletes and namespace matching.
internal/cli/commands/delete_test.go Refactors delete tests to injected deps and adds coverage for skipping GET on untagged deletes.
internal/cli/commands/declarative.go Introduces newKindFromDescriptor and uses it to build/register built-in kinds consistently.
internal/cli/commands/all_tags_test.go Removes shared global test client and updates tag-related tests to use injected deps.
Suppressed comments (1)

internal/cli/commands/resources.go:112

  • When deleting tagged resources with an omitted --tag, the latest tag is resolved via GET and then used for the DELETE. If the resolved metadata.tag is unexpectedly empty, this code will fall back to the name-only DELETE route (because Client.Delete treats empty tag as mutable-object delete), which is the wrong endpoint for tagged artifacts. Add a guard to error out when the resolved tag is empty.
	targetTag := tag
	if targetTag == "" {
		obj, err := client.GetTyped(ctx, c, kind, ref.Namespace, ref.Name, "", newObj)
		if err != nil {
			return err
		}
		targetTag = obj.GetMetadata().Tag
	}
	return c.Delete(ctx, kind, ref.Namespace, ref.Name, targetTag)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +147 to 153
newObj := func() v1alpha1.Object {
obj, ok := descriptor.NewObject().(T)
if !ok {
panic(fmt.Sprintf("commands.registerKind: constructor for %s returned %T", canonicalKind, obj))
}
return obj
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants