Skip to content

Root command namespace flexibility: nest, rename, selective exposure #47

@djdarcy

Description

@djdarcy

Summary

Let downstream aggregators control the root command namespace more aggressively than MetaCommandRegistry allows today. Three discrete mechanisms:

  1. Nest under a sub-command: wtf dz list (library commands tucked under "dz") instead of wtf list.
  2. Selective exposure: wtf list stays at root, but wtf tree / wtf setup go under wtf dz tree / wtf dz setup.
  3. Rename: wtf tools as an alias for list, sysdiag monitors for list, etc.

All three are aggregator-configurable; the library defaults to today's flat structure (no breaking change).

Problem

v0.7.23 shipped MetaCommandRegistry with register / override / unregister as the primary extension API. For an aggregator like wtf-windows this is sufficient — drop tree / setup, override list / info, add mode / new / add, done.

But for aggregators with richer domain vocabulary (sysdiagnose-public, future consumers), the top-level name space gets crowded or inappropriate:

  • Name collisions: a diagnostic aggregator with a tool legitimately named info (e.g., "info gathering") can't use that name today — it's reserved by the library's built-in info meta-command. Same for list, setup, etc.
  • Weak defaults for the domain: sysdiag list is grammatically weaker than sysdiag monitors. The library's generic verbs don't fit every domain.
  • Discoverability pollution: wtf --help shows 6+ library meta-commands intermixed with 2-10 domain tools. Ratio gets worse as tool counts grow.

Proposed API

# Full nesting
engine.meta_registry.nest_all_under("dz")
# Result: wtf dz list, wtf dz info, wtf dz kit, etc.
# Top-level only contains: tools + aggregator-registered meta-commands

# Partial nesting
engine.meta_registry.nest_under("dz", members=["kit", "tree", "setup"])
engine.meta_registry.keep_at_root(["list", "info"])
# Result: wtf list / wtf info at root, wtf dz kit / wtf dz tree / wtf dz setup nested

# Rename
engine.meta_registry.rename("info", "inspect")
engine.meta_registry.rename("list", "tools")
# Result: wtf inspect (from info), wtf tools (from list)

# Combined
engine.meta_registry.nest_all_under("manage")
engine.meta_registry.rename("setup", "bootstrap")
# Result: wtf manage list, wtf manage inspect, wtf manage bootstrap, etc.

Semantics

  • nest_under(group_name, members=[...]): moves specified commands to a sub-parser; top-level reserves only group_name.
  • nest_all_under(group_name): shorthand for "every command the library auto-registered."
  • keep_at_root(members): explicitly opts command(s) out of a group.
  • rename(old_name, new_name): changes the CLI surface name; _meta tag and handler identification still use old_name internally (so handlers stay stable).

Dispatch: args._meta routes by the ORIGINAL command identifier. A rename("info", "inspect") still dispatches _meta="info" to the info handler. Rename is CLI-surface only.

Reserved commands: dynamically derived. nest_all_under("dz") means only "dz" is reserved at root + aggregator's own tool / extra reservations.

Help text: nest_under automatically groups commands in argparse's subparser rendering. Aggregator can still provide epilog_builder for custom section layout.

Backward compat: none of the new methods must be called. Default behavior (flat top-level namespace) is unchanged from v0.7.23.

Acceptance criteria

  • MetaCommandRegistry.nest_under(group, members) implemented
  • MetaCommandRegistry.nest_all_under(group) convenience method
  • MetaCommandRegistry.keep_at_root(members) override
  • MetaCommandRegistry.rename(old, new) method
  • Dispatch routes by original _meta name even after rename
  • Reserved commands dynamically updated (nested command NOT reserved at root)
  • Engine's build_parsers builds nested sub-parsers per group
  • Help text reflects nesting (argparse native rendering; epilog_builder optional)
  • Mutation methods check the registry lock (same as existing register/override/unregister)
  • ~30 unit tests covering: nest CRUD, rename CRUD, combined nest+rename, reserved-name derivation, locked-registry errors, backward-compat (no-op when none called)
  • Human test checklist
  • Documentation: tests/README.md + docs/guides/manifests.md updates + library module docstring
  • Demonstrated in at least one real aggregator (likely sysdiagnose-public — see related issue)

Dependencies

  • Depends on: v0.7.23 MetaCommandRegistry — done
  • Likely drives: sysdiagnose-public adoption (will be the first demanding use case)
  • Not blocking: wtf-windows adoption (wtf doesn't need this feature; default flat namespace works)

Design tradeoffs considered

Pattern A (full nesting only): simpler surface but too rigid. Forces wtf dz <cmd> for everything or nothing. Most aggregators will want to keep list / info at root because those are universally useful top-level verbs.

Pattern B (selective + rename, this proposal): more surface area but matches real need. Costs ~120 LOC library + ~30 tests.

Pattern C (help-only grouping via epilog_builder): purely cosmetic, works today in v0.7.23, doesn't solve name-collision. Useful as a stopgap but not a substitute.

Status

Design-stage. Implementation deferred until there's a real aggregator asking for it (the sysdiagnose-public adoption issue will likely surface this need).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    NextTaskNext item to pick up -- becomes CurrentTask when work startsarchitectureStructural and design decisionsenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions