diff --git a/docs/roadmap/aliases.md b/docs/roadmap/aliases.md index b2804c2..d0a1fff 100644 --- a/docs/roadmap/aliases.md +++ b/docs/roadmap/aliases.md @@ -1,9 +1,8 @@ # Application-owned aliases -Status: command- and flag-alias directions accepted; implementation has not -started. Consumer registration conventions are selected; the installer declaration -type and remaining routing details are open. This roadmap records -the target and work needed before implementation. +Status: command- and flag-alias behavior and consumer API direction accepted; +implementation has not started. OD-1 through OD-6 are resolved. This roadmap +records the target and work needed before implementation. ## Purpose and ownership @@ -60,6 +59,47 @@ Commands and aliases use the same installation boundary through `New` and aliases as separate routing records internally. No separate alias-registration API is needed. +### Installation types and composition + +Introduce a common `Declaration` interface with an unexported marker method. +CommandKit supplies its implementations through public factories. `Command` +represents canonical commands; a separate opaque `RoutingRule` represents +aliases. Both satisfy `Declaration`: + +```text +Declaration +├── Command → canonical command tree +└── RoutingRule → alias routing layer +``` + +The selected installation signatures are: + +```go +func New(identity Identity, declarations ...Declaration) (*App, error) +func (a *App) Register(declarations ...Declaration) error +``` + +`NewCommand`, `Group`, and integration command factories return `Command`. +`Alias`, `FlagAlias`, and `FlagAliasFor` return `RoutingRule`. The common +installation boundary validates relationships across the layers atomically; +installation directs each declaration to its corresponding registry. Routing +rules reference canonical commands without becoming command nodes or owning +handlers. Preserve eager capture and retained construction errors for both +declaration kinds, without introducing deferred installation callbacks. + +`Group`, `RootGroup`, and command children continue to accept only `Command`. +Install routing rules alongside the completed canonical tree through `New` or +`Register`. Alias paths remain relative to the application root; moving a +command beneath a group does not silently rebase an alias. + +Use ordinary `[]Declaration` slices for reusable collections containing commands +and routing rules. Command-only helpers may return `Command` or `[]Command`. +Go does not allow expanding `[]Command` into `...Declaration`; callers assembling +installation collections should use `[]Declaration` or convert their elements. +No additional bundle abstraction is selected. + +### Consumer declarations + Use space-separated strings for command paths, interpreted as sequences of command names without shell quoting or expansion. Application-wide flag aliases use `FlagAlias`; command-scoped aliases use `FlagAliasFor` with an explicit @@ -75,14 +115,12 @@ app, err := commandkit.New( ) ``` -This illustrates planned APIs. The declaration type accepted by the installer -still needs design; aliases remain routing records rather than fabricated -canonical command nodes. Later registration uses the same model and validation. +These are planned APIs. Later registration uses the same model and validation. Accept identical duplicate alias mappings as harmless, including when reusable helpers contribute them. Identity includes the alias kind, source, target, and scope where applicable. Different-target command mappings for the same source -remain errors; overlap between flag scopes is addressed under OD-6. +remain errors; flag scope precedence is specified below. Alias support lives entirely in core and works with built-in commands, application commands, optional integrations, or any combination. Validation @@ -91,6 +129,35 @@ dependency is required for alias support. Core continues to supply help and version automatically, including for applications whose other commands are entirely application-defined. +### Shared path handling + +Centralize path parsing, component validation, comparison, and formatting in +shared internal helpers with a common structured path representation. Alias +validation, routing, help lookup, and invocation metadata use those primitives. +Keep the routing and canonical command models distinct while sharing path +operations. + +Declaration factories tokenize command-path strings on whitespace, accepting +leading, trailing, and repeated whitespace, then validate each component using +the canonical command-name rules. Reject empty paths and invalid components. +Normalize equivalent paths before duplicate detection. Quotes and backslashes +receive no shell interpretation; paths cannot contain flag assignments or +operand templates. Capture parsed paths once when constructing declarations. + +CLI arguments are already tokenized. Preserve those boundaries: an argument +containing `list collections` is one argument, not two command components. +Do not trim or split incoming operands or flag values. Invocation routing +identifies the command portion of the supplied tokens and carries structured +paths through alias resolution and canonical selection. Format paths for +presentation when needed, avoiding repeated string joining and parsing through +alias chains. + +Convert inputs at their appropriate boundaries and reuse the result where +practical. Parsing once per query is an implementation objective, not a permanent +allocation or parsing guarantee. Use an internal path package if sharing across +core and the backend adapter warrants it; otherwise private core helpers and a +path type suffice. No additional public path package is selected. + ## Selected command-alias behavior ### Paths and resolution @@ -222,8 +289,15 @@ the selected public metadata. Consumers can use the canonical path to identify the operation consistently and the entered path for logging and diagnostics. CommandKit dispatch and -bindings continue to use the canonical target. Exact accessor names and path -representation remain API design details under OD-1. +bindings continue to use the canonical target. Expose the paths through these +accessors, returning normalized, space-separated strings: + +```go +func (inv Invocation) EnteredPath() string +func (inv Invocation) CanonicalPath() string +``` + +Strings provide read-only values and match the declaration path representation. Support both help forms: @@ -415,7 +489,7 @@ canonical binding. ## Work needed from the baseline -1. Finalize the installer declaration type and open routing details with consumer examples +1. Prepare consumer examples for the selected declaration types and shared path handling, covering a command shortcut, a synonym inside an integration namespace, and a group shortcut, an application-wide help flag alias, and a command-scoped flag alias reached through a command shortcut. @@ -423,8 +497,9 @@ canonical binding. command and flag ownership in an ADR. Keep exact API, matching, and presentation details in their appropriate contracts. No relaxation of canonical namespace ownership is selected. -3. Extend installation to validate aliases and commands against the complete - candidate batch, preserving retained errors and atomicity. Keep aliases +3. Implement the selected declaration types and shared path primitives. Extend + installation to validate aliases and commands against the complete candidate + batch, preserving retained errors and atomicity. Keep aliases independent of optional-library dependencies and backend-specific exports. 4. Integrate command and flag alias resolution with canonical routing, help, and invocation identity. Use canonical flag metadata and the existing parser to @@ -448,17 +523,4 @@ Completion means consumers can declare alternate command paths and flag spellings with minimal wiring and invoke canonical behavior and help through them while canonical ownership and binding semantics stay intact. Installation rejects conflicts with canonical commands and applicable flags in either -registration order. Remaining public API details are listed below. - -## Open Decisions - -### OD-1: Installer declaration type and composition details - -The shared installation API, path strings, explicit flag scopes, identical -duplicate handling, and core-only availability are selected above. What public -declaration type lets `New` and `App.Register` accept commands and aliases -together? How do collections of reusable declarations and group composition -interact with application-root alias paths? What exact whitespace parsing and -path validation rules accompany the selected string representation? What -accessor names and representation expose the selected read-only entered and -canonical invocation paths? +registration order.