Design toolbelt/cmd — the shared cobra bootstrap surface #9
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ginjiruu/toolbelt#9
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Question
What does the absorbed
toolbelt/cmdsurface look like?Research ticket "Research: can the cobra bootstrap boilerplate be reduced without losing cobra API power?" established absorb is viable (~86% of
package cmdis absorbable; cobra is data-driven so a parent-command factory preserves the raw API). This ticket designs the concrete API.Recommended shape to react to (Option B from the research, branch
research/cobra-bootstrap,docs/research/cobra-bootstrap.md):NewRootCommand(RootOptions) *cobra.Command— persistent flags (--config/-c,-v/-vv/-vvvladder viaCountVarP,--log-level),cobra.OnInitializeinitConfig, optionalValidatecallback; returns raw*cobra.Command.AddConfigCommand(root, SaveOptions)— theconfig list/describe/setgroup driven by theFields()registry, with injectable KCL/YAML save strategy (gate KCL on.ksuffix, matchinggo-template/cmd/config.go:322).Pager() (io.Writer, func()).Serve(ServeOptions, run)scaffold — signal ctx, logger build/sync, close/run.DevSetup(prefix string, ...)inembeddedoidc(standardize onConfigureEnv).BuildLogger(level, format)replacing the five copies.Decisions this ticket resolves:
toolbelt/cmd?) and its option structs.-voption needed?config/kclin the toolbelt root (blank import seam vs callback).New*Appconstructor, startup Debug summary, redirect URI) as options/callbacks.Resolve with
/prototype(sketch the API to react to) +/grilling+/domain-modeling. Per the research facts: derive binary name fromcmd.CommandPath()/root.Use(not literals), readConfigEnvPrefix/AppNameat call time, keep the Debug summary per-app. On close, write the plan todocs/plans/and reshape theexample/cmd/surface onto it.Child of: "Toolbelt glue absorption — map: what the toolbelt absorbs from duplicated consumer glue" (#1).
Resolved via /prototype (throwaway branch
prototype/cmd-bootstrap) + /grilling. The absorbed surface is research #6's Option B — a parent-command factory returning the raw*cobra.Command— designed against the #3/#4 instance shape.The four decisions
config.New(...)/app.New(ctx, cfg, opts)):NewRootCommand(cfg *config.Config, RootOptions{Short, Long, PreInit}) *cobra.CommandAddConfigCommand(cfg *config.Config, root *cobra.Command)— full group, no optionsPager() (io.Writer, func())Serve(cfg *config.Config, ServeOptions{Options app.Options})— logger build/sync + startup summary absorbed intoapp.New(plan #4)embeddedoidc.DevSetup(prefix, WithRedirectURI/WithStartupDelay/WithDevLogger)Per-app
Execute()stays; factory owns flag storage; panics on nil cfg.-v/-vv/-vvvladder only, no transition bool. Modeled in KCL as theapp.Log.levelliteral-union enum (source of truth);-vresolves into it viacfg.VerboseToLevel. ttrpg's bool-vis not a compat constraint (stale fork, out of build).config/kcl; gate the.ksuffix by default (go-template pattern); no save callback. All apps define config in KCL (spec), end users configure via YAML/flags/env (runtime). Validation is structural — the generatedconfig.Appownscfg.Validate(), no app can skip it.RootOptions{Short, Long, PreInit}+ServeOptions{Options}+DevOptions — the only residue.Deliverables
prototype/cmd-bootstrap.docs/plans/cmd-bootstrap.md.check/rule-block → Go validation codegen (#23).Closed: toolbelt/cmd surface designed (Option B factory, KCL-aware by default, structural validation, Serve absorbs app). Plan docs/plans/cmd-bootstrap.md; prototype on branch prototype/cmd-bootstrap; lands with #3/#4.