Show aliases beneath empty command groups

This commit is contained in:
2026-10-20 00:26:07 +00:00
parent c231a41665
commit 744b8135f2
3 changed files with 32 additions and 2 deletions

View File

@@ -187,7 +187,7 @@ passed on Go 1.26.8.
## R-5: Include alias routes in help for groups without canonical children
Status: open. Priority: P2. Category: help discovery gap.
Status: complete. Priority: P2. Category: help discovery gap.
**Evidence:** [routeHelpAliases](../../help_view.go#L48) returns immediately when
the selected canonical node has no canonical children. Alias records are
@@ -206,6 +206,13 @@ commands and routing records; do not install fake children to fill the view.
its explicit alias routes and their resolved destinations, while groups with
no such routes retain their ordinary help behavior.
**Completed:** Group alias discovery no longer depends on canonical child
count. A new regression failed for canonical and shortcut help before the
correction, and now both help forms list the separate alias route. An unrelated
empty group's help remains unchanged and the complete route still executes.
No synthetic command children were introduced. The complete offline root suite
passed on Go 1.26.8.
## R-6: Keep the flag interpretation pass consistent with value consumption
Status: open. Priority: P2. Category: canonical/alias parsing divergence.

View File

@@ -64,7 +64,7 @@ func routeHelpAliases(snapshot *routingSnapshot, route routeResult) []commandmet
return append([]commandmeta.CommandAlias(nil), snapshot.commandAliases...)
}
node := traceCanonicalPath(snapshot.canonical, route.canonicalPath).exact
if node == nil || len(node.children) == 0 {
if node == nil {
return nil
}

View File

@@ -214,6 +214,29 @@ func TestDiscoveryHelpThroughExpandedGroupPath(t *testing.T) {
}
}
func TestEmptyCanonicalGroupHelpListsAliasRoutes(t *testing.T) {
app, err := New(Identity{Name: "Demo"}, Group("box", "Box"), Group("quiet", "Quiet"),
simpleRunnable("inspect"), Alias("box pick", "inspect"), Alias("pk", "box"))
if err != nil {
t.Fatal(err)
}
for _, args := range [][]string{{"box", "--help"}, {"help", "box"}, {"pk", "--help"}, {"help", "pk"}} {
var output bytes.Buffer
outcome := app.Execute(context.Background(), args, Streams{Out: &output, Err: io.Discard})
if outcome.Kind != OutcomeSuccess || !strings.Contains(output.String(), "pick → inspect") {
t.Errorf("Execute(%q) = %v, help %q", args, outcome.Kind, output.String())
}
}
var output bytes.Buffer
outcome := app.Execute(context.Background(), []string{"quiet", "--help"}, Streams{Out: &output, Err: io.Discard})
if outcome.Kind != OutcomeSuccess || strings.Contains(output.String(), "Command Aliases:") {
t.Fatalf("unaliased group help = %v, %q", outcome.Kind, output.String())
}
if outcome := app.Execute(context.Background(), []string{"box", "pick"}, Streams{Out: io.Discard, Err: io.Discard}); outcome.Kind != OutcomeSuccess {
t.Fatal("alias route became unavailable")
}
}
func routeTestApp(t *testing.T, extra ...Declaration) *App {
t.Helper()
collections := simpleRunnable("collections")