Plan shared debug binding and reporting mode access
This commit is contained in:
233
docs/roadmap/shared-debug-binding.md
Normal file
233
docs/roadmap/shared-debug-binding.md
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
# Shared debug binding and reporting-mode access
|
||||||
|
|
||||||
|
Status: agreed capability direction; not implemented. Public binding details
|
||||||
|
remain open below. Baseline: `69257066e21c12ea3492237ebee072598adea60c`.
|
||||||
|
|
||||||
|
## Purpose and authority
|
||||||
|
|
||||||
|
Applications should be able to receive the boolean value of CommandKit's
|
||||||
|
`--debug` flag through normal typed input binding. Applications that need the
|
||||||
|
resolved reporting mode should also have a read-only invocation API.
|
||||||
|
|
||||||
|
The [Audita request](audita-feature-request.md) motivated this work. Subsequent
|
||||||
|
discussion selected a broader distinction: application debug behavior and CLI
|
||||||
|
reporting visibility are separate choices. Audita's original requirement that
|
||||||
|
capture follow reporting mode is an application policy, not a CommandKit
|
||||||
|
requirement. This roadmap records the agreed direction from that discussion;
|
||||||
|
it does not treat every proposal in the original request as accepted.
|
||||||
|
|
||||||
|
The [architecture policy](../policy/architecture.md#ownership-boundaries) and
|
||||||
|
[ADR 0006](../adr/0006-invocation-reporting.md) already assign CLI mechanics to
|
||||||
|
core and domain behavior to applications. This work fits those boundaries and
|
||||||
|
does not require another ADR. Implemented behavior remains documented in
|
||||||
|
[Go declarations](../../command.go) and the [CLI reference](../cli.md#reporting)
|
||||||
|
until this work lands.
|
||||||
|
|
||||||
|
## Current baseline
|
||||||
|
|
||||||
|
| Area | Current implementation | Work needed |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Typed binding | `BoolFlag` in [binding.go](../../binding.go) converts supplied occurrences and applies the last boolean with `Supplied[bool]` presence. | Let a binding receive the existing core debug flag's input. |
|
||||||
|
| Registration | `FlagMeta` and `validateBindings` in [command.go](../../command.go) reserve every reporting name, including `debug`. | Recognize the selected shared-binding form without treating it as an independent competing flag. |
|
||||||
|
| Parser metadata | [commandmeta](../../internal/commandmeta/metadata.go) separates `CoreFlags` from consumer-indexed `Flags`; [parser.go](../../parser.go) supplies core flags to each command. | Preserve consumer binding identity and indexes while sharing one parser declaration. |
|
||||||
|
| Backend | [internal/cobra/parse.go](../../internal/cobra/parse.go) registers both lists, records occurrences, and extracts indexed values only for consumer flags. | Supply shared debug values and spellings to the participating binding without duplicate backend registration. |
|
||||||
|
| Reporting | `reduceReportingOccurrences` in [parser.go](../../parser.go) resolves the mutually exclusive mode from ordered occurrences. | Preserve that reduction independently of the bound debug boolean. |
|
||||||
|
| Invocation | [execute.go](../../execute.go) applies reporting selection before input preparation and execution. [reporter.go](../../reporter.go) stores the mode privately; [Invocation](../../identity.go) exposes no mode query. | Expose the already resolved selection through a CommandKit-owned public type. |
|
||||||
|
|
||||||
|
Simply permitting the name in registration is insufficient: the backend would
|
||||||
|
otherwise receive two declarations for `debug`. Existing occurrence capture is
|
||||||
|
the starting point; applications should not scan or reparse arguments.
|
||||||
|
|
||||||
|
## Targeted end state
|
||||||
|
|
||||||
|
### Bind the debug boolean independently
|
||||||
|
|
||||||
|
An application opts into receiving `--debug` in its command input through a
|
||||||
|
`Flag[I]` and the existing typed setter model. Exact construction and metadata
|
||||||
|
rules are subject to OD-1 and OD-2. Commands without such a binding retain the
|
||||||
|
standard core flag and reporting behavior.
|
||||||
|
|
||||||
|
The ordinary shared binding receives `Supplied[bool]`: omission produces
|
||||||
|
`Value=false, Set=false`; an explicitly supplied false produces
|
||||||
|
`Value=false, Set=true`. Repeated debug occurrences follow ordinary boolean
|
||||||
|
binding semantics: every supplied value must be valid, and the last value wins.
|
||||||
|
Other reporting flags do not rewrite the bound debug value.
|
||||||
|
|
||||||
|
The following examples appear after a complete command path. They show the
|
||||||
|
ordinary false-default binding and the existing mode-selection rules together:
|
||||||
|
|
||||||
|
| Arguments | Debug value | Supplied | Resolved reporting mode |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| None | false | false | Ordinary |
|
||||||
|
| `--debug` | true | true | Debug |
|
||||||
|
| `--debug --quiet` | true | true | Quiet |
|
||||||
|
| `--quiet --debug` | true | true | Debug |
|
||||||
|
| `--debug --verbose` | true | true | Verbose |
|
||||||
|
| `--debug --debug=false` | false | true | Ordinary |
|
||||||
|
| `--verbose --debug --debug=false` | false | true | Ordinary |
|
||||||
|
| `--debug --quiet=false` | true | true | Debug |
|
||||||
|
| `--debug --progress=false` | true | true | Debug |
|
||||||
|
|
||||||
|
Aliases use the same canonical debug input. Tokens consumed as other flag
|
||||||
|
values or appearing after `--` keep their existing meaning. Invalid reporting
|
||||||
|
booleans remain usage failures without handler execution. Existing placement,
|
||||||
|
help, and input-preparation timing continue to apply.
|
||||||
|
|
||||||
|
Application code can use the bound value for diagnostic artifacts even when
|
||||||
|
quiet mode suppresses ordinary CLI output. Core continues to own reporting
|
||||||
|
visibility; applications own capture, redaction, file lifecycle, and any other
|
||||||
|
domain effects. No capture policy is imposed on Audita or other applications.
|
||||||
|
|
||||||
|
### Query the resolved reporting mode
|
||||||
|
|
||||||
|
Add a CommandKit-owned `ReportingMode` type with ordinary, quiet, verbose, and
|
||||||
|
debug values, and one canonical accessor:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (inv Invocation) ReportingMode() ReportingMode
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `ReportingModeOrdinary`, `ReportingModeQuiet`, `ReportingModeVerbose`, and
|
||||||
|
`ReportingModeDebug` as the planned constant names, with ordinary as zero.
|
||||||
|
The accessor reads the existing selection without output or mutation. The mode
|
||||||
|
is final when the handler receives the invocation and remains its selection
|
||||||
|
after execution finishes. A manually constructed zero-value invocation returns
|
||||||
|
ordinary. Reads are compatible with concurrent reporting on that invocation;
|
||||||
|
this does not add concurrent dispatch support or extend writer lifetimes.
|
||||||
|
|
||||||
|
An application deliberately following reporting visibility can use:
|
||||||
|
|
||||||
|
```go
|
||||||
|
captureArtifacts := inv.ReportingMode() == commandkit.ReportingModeDebug
|
||||||
|
```
|
||||||
|
|
||||||
|
This reports the selected mode, not whether output was delivered successfully.
|
||||||
|
Progress and report format remain separate selections. Keep the public type
|
||||||
|
independent of internal/backend types and avoid a second mutable copy of the
|
||||||
|
resolved mode. A second accessor on `Reporter` is unnecessary for this scope.
|
||||||
|
|
||||||
|
## Implementation work
|
||||||
|
|
||||||
|
1. **Settle binding construction and metadata.** Resolve OD-1 and OD-2 before
|
||||||
|
making the registration exception. Document which declaration attaches to
|
||||||
|
the core flag and how its application metadata is handled. Preserve ordinary
|
||||||
|
validation for unrelated names and bindings; do not broadly disable reserved
|
||||||
|
name or collision checks.
|
||||||
|
2. **Connect shared parsing to typed input.** Update declaration validation,
|
||||||
|
metadata bridging, and backend value extraction together. Register one
|
||||||
|
effective debug flag, retain canonical occurrence order and supplied
|
||||||
|
spellings, and deliver its values through normal input preparation. Keep
|
||||||
|
reporting reduction based on the same parsed occurrences, including for
|
||||||
|
commands that have no application debug binding. Reuse current mechanisms;
|
||||||
|
a general observer registry, alternate parser, or subscription lifecycle is
|
||||||
|
not needed for this feature.
|
||||||
|
3. **Preserve binding composition.** Review `CaptureFlags`, `FlagValues.ApplyTo`,
|
||||||
|
and `ApplySuppliedTo` in [flag_values.go](../../flag_values.go). A shared
|
||||||
|
binding should participate through the existing identity and presence
|
||||||
|
machinery. Review `RenameFlag` if a special binding marker is introduced:
|
||||||
|
renaming to an ordinary application name must not accidentally keep reading
|
||||||
|
core debug input. Avoid adding separate storage or application callbacks just
|
||||||
|
to carry this boolean.
|
||||||
|
4. **Expose the mode query.** Reuse reporter state and its synchronization as
|
||||||
|
appropriate; document the zero-value, invocation lifetime, and concurrent-read
|
||||||
|
behavior. Do not expose backend objects or add public mutation APIs.
|
||||||
|
5. **Update canonical documentation when implemented.** Update binding and
|
||||||
|
invocation GoDoc, the reserved-name wording in `FlagMeta`, and the
|
||||||
|
[CLI reference](../cli.md#placement-and-aliases). Add a short example to the
|
||||||
|
[consumer guide](../consumers/guide.md#invocation-reporting) contrasting a
|
||||||
|
bound debug boolean with an explicit mode query. Explain application effects
|
||||||
|
in application help using the metadata choice from OD-2. Record the core API
|
||||||
|
addition in the applicable release note. Retire this roadmap through the
|
||||||
|
normal documentation lifecycle when complete.
|
||||||
|
|
||||||
|
This is core work. It does not require ConfigKit or PromptKit API additions or
|
||||||
|
adoption of Audita's artifact implementation. Binding other core flags, adding
|
||||||
|
mode setters, or creating a general flag-sharing framework is not part of this
|
||||||
|
sprint; that scope limit is not a permanent prohibition on later capabilities.
|
||||||
|
|
||||||
|
## Validation and completion checklist
|
||||||
|
|
||||||
|
Follow the [testing policy](../policy/testing.md): test the public behavior and
|
||||||
|
reuse existing parser and reporting coverage rather than duplicate it wholesale.
|
||||||
|
The table above supplies representative boundary expectations, not a request
|
||||||
|
for separate copies of the same cases at every layer.
|
||||||
|
|
||||||
|
- [ ] A registered application command receives the shared debug boolean and
|
||||||
|
presence through normal typed input preparation, including when quiet or
|
||||||
|
verbose becomes the resolved reporting mode.
|
||||||
|
- [ ] The public query returns the selected mode without changing visibility;
|
||||||
|
cover zero value, retained invocation, and fresh selection on subsequent runs.
|
||||||
|
- [ ] Focused coverage establishes safe mode reads during existing concurrent
|
||||||
|
reporter use, without asserting concurrent dispatch support.
|
||||||
|
- [ ] Exercise aliases, explicit false, repeated values, invalid booleans, and
|
||||||
|
option-looking operands/values where they establish the new binding boundary.
|
||||||
|
Preserve existing failure precedence and no-handler-on-parse-failure behavior.
|
||||||
|
- [ ] A command with both shared debug and ordinary flags receives the correct
|
||||||
|
values; help and completion describe one effective debug option. Exercise
|
||||||
|
representative capture/application composition and any affected rename path.
|
||||||
|
- [ ] Test only the metadata/collision decisions actually selected below;
|
||||||
|
replace obsolete blanket rejection assertions for the allowed binding form.
|
||||||
|
- [ ] Run focused binding/parser/reporter tests, then the core suite and aligned
|
||||||
|
integration suites using the [developer guide](../development.md). The shared
|
||||||
|
parser change justifies checking that standard integration commands still
|
||||||
|
register and execute. No live providers or Audita processing tests are needed.
|
||||||
|
- [ ] GoDoc and consumer guidance distinguish boolean input from reporting
|
||||||
|
mode, and canonical docs describe the implemented registration exception.
|
||||||
|
|
||||||
|
For this documentation pass, review baseline accuracy, scope, links, and
|
||||||
|
whitespace only. Implementation and the unchecked items above remain future work.
|
||||||
|
|
||||||
|
## Open Decisions
|
||||||
|
|
||||||
|
### OD-1: How does an application declare the shared binding?
|
||||||
|
|
||||||
|
**Recommendation:** recognize a normal `BoolFlag` with `FlagMeta.Name` set to
|
||||||
|
`debug` as attachment to the existing core flag. It returns the same `Flag[I]`,
|
||||||
|
uses `Supplied[bool]`, and occupies the normal command binding list. This most
|
||||||
|
directly meets the request that using debug should feel like using other flags
|
||||||
|
and avoids another public constructor. The cost is special handling of a
|
||||||
|
core-owned name, particularly the metadata rules in OD-2. Do not interpret an
|
||||||
|
arbitrary string-valued or custom-parser flag named `debug` as equivalent merely
|
||||||
|
because its name matches.
|
||||||
|
|
||||||
|
**Viable alternative:** add a dedicated constructor such as
|
||||||
|
`DebugFlag[I](set func(*I, Supplied[bool])) Flag[I]`. This still uses the ordinary
|
||||||
|
typed binding and composition path, but makes attachment explicit and avoids
|
||||||
|
exposing conflicting declaration options. It adds public API vocabulary and
|
||||||
|
makes debug construction different from other booleans. Prefer this if ordinary
|
||||||
|
`BoolFlag` reuse needs substantial metadata reconciliation or confusing exceptions.
|
||||||
|
|
||||||
|
The example constructor name is illustrative; choose one binding form rather
|
||||||
|
than adding both entry points without a demonstrated need.
|
||||||
|
|
||||||
|
### OD-2: Which application metadata can accompany a shared flag?
|
||||||
|
|
||||||
|
**Recommendation:** retain the core boolean grammar and false default. Allow
|
||||||
|
command-local usage text to explain additional application effects alongside
|
||||||
|
core reporting help, and allow a local short spelling through existing alias
|
||||||
|
and collision rules. Keep one displayed option. Continue using core boolean
|
||||||
|
completion. Under the ordinary `BoolFlag` approach, report conflicting default,
|
||||||
|
requiredness, or completion overrides as declaration errors rather than silently
|
||||||
|
ignoring them. A dedicated constructor could instead omit those options. These
|
||||||
|
are proposed rules for the shared attachment only, not accepted new restrictions
|
||||||
|
on ordinary application flags.
|
||||||
|
|
||||||
|
This preserves consistent debug input across applications and avoids help that
|
||||||
|
claims a default or requirement different from the shared flag. Its tradeoff is
|
||||||
|
that a normal-looking declaration would accept fewer metadata choices.
|
||||||
|
|
||||||
|
**Viable alternative:** let application defaults and requiredness affect only
|
||||||
|
the receiving binding, using ordinary binding semantics, while core reporting
|
||||||
|
continues to follow explicit occurrences. That gives applications more familiar
|
||||||
|
flexibility: for example, an application default of true could enable its own
|
||||||
|
diagnostics without selecting debug reporting. However, omission would no longer
|
||||||
|
necessarily mean a false bound value, and help would need to distinguish an
|
||||||
|
application default from the core reporting default. If selected, revise the
|
||||||
|
omission contract above and explain this distinction explicitly; do not silently
|
||||||
|
allow local metadata to change core reporting selection.
|
||||||
|
|
||||||
|
**Simpler help alternative:** retain core flag help unchanged and put application
|
||||||
|
effects in the command description, with short spellings supplied through the
|
||||||
|
existing `FlagAliasFor` mechanism. This avoids help merging, at the cost of less
|
||||||
|
convenient flag-local documentation. It combines naturally with the dedicated
|
||||||
|
constructor in OD-1. There is no need for a general metadata-merging framework.
|
||||||
Reference in New Issue
Block a user