# Shared debug binding and reporting-mode access Status: agreed scope and public behavior; not implemented. OD-1 and OD-2 are resolved; no significant open decisions remain. 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 OD-1 is resolved: an application declares a normal `BoolFlag` with `FlagMeta.Name` set to `debug`. CommandKit attaches that binding to the existing core flag. The application uses the same `Flag[I]`, `Supplied[bool]`, command binding list, and typed setter model as for other boolean flags. No dedicated `DebugFlag` constructor is planned. Application metadata follows the accepted OD-2 decision below. Commands without such a binding retain the standard core flag and reporting behavior. This choice keeps the application API familiar and avoids another public constructor. Core handles shared registration internally; an arbitrary string-valued or custom-parser flag named `debug` is not equivalent to the selected boolean binding merely because its name matches. The shared binding receives `Supplied[bool]`: omission produces the application's declared default with `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. ### Honor application binding settings, help, and aliases OD-2 is resolved: honor ordinary application binding settings while keeping core reporting selection independent. Application defaults and requiredness affect the receiving binding; they do not select a reporting mode. Core continues to resolve reporting from explicit CLI occurrences under the existing rules. For example, a non-required binding with a true default receives `Value=true, Set=false` when debug is omitted, while reporting remains ordinary in the absence of mode flags. An explicit `--debug=false` overrides that default. With `--debug --quiet`, the binding receives true while reporting is quiet. Requiredness follows ordinary presence semantics: if required, omission is a usage failure, while an explicit false satisfies the presence requirement. An application default does not count as explicit input. Honor the application's short spelling through existing alias and collision rules. Both that spelling and other registered aliases feed the same canonical debug input and reporting occurrence sequence. Show one debug option in command help, combining the core reporting explanation with any application-specific usage text. Clearly label an application default that differs from core behavior, so a true application default does not imply that debug reporting is enabled by default. Show application requiredness using the existing help conventions. Keep ordinary boolean binding completion behavior and metadata handling; this feature adds no special rejection of completion settings. Completion suggestions do not change the shared boolean grammar. This choice keeps ordinary `BoolFlag` settings useful without adding restrictions solely for shared registration. The distinction between application input and reporting selection must be clear in help and consumer guidance. Implement this small combination directly; a general metadata-merging framework is unnecessary. ### 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. **Implement ordinary binding attachment and metadata handling.** Use the selected `BoolFlag` form and the accepted application metadata rules above. Preserve application defaults, presence, requiredness, and short spellings; combine help without changing core reporting selection. 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 accepted metadata rules. 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. - [ ] Cover a true application default without debug reporting, explicit false overriding that default, and ordinary requiredness/presence behavior. Help combines core and application explanations and distinguishes the application default; local short spellings use existing collision and alias rules. - [ ] Replace obsolete blanket rejection assertions for the allowed binding form. Preserve ordinary metadata handling without new debug-specific restrictions beyond the selected boolean binding contract. - [ ] 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.