Skip to content

Value provenance: track resolution stage and expose explicitness to handlers #90

Closed

Description

@kjanat

Sub-issue of #88 — Part 1.

Problem

FLAG_RESOLUTION_ORDER = ['cli', 'env', 'config', 'prompt', 'default'] is an ordered, named chain, and the failure path already models sources via FlagDiagnosticSource. On the success path the winning stage is dropped: ResolveResult is {flags, args, deprecations} and ActionParams narrows to {args, flags, ctx, out, meta}. A handler can observe what a flag resolved to, never whether the user said it.

Why it matters

  1. Mutually exclusive flags — both-passed should be a hard error, not a silent precedence rule.
  2. Warning on ignored input — "you passed --x, it has no effect in this mode" is only honest if --x was actually typed.
  3. Explicit-wins merges over a project manifest — a defaulted value must not silently outrank the manifest.
  4. Derived or conditional defaults — today the only way to express "unset" is to omit .default().
  5. --explain-style diagnostics — wants the stage label, not just a boolean.

(1)–(4) need only a boolean; (5) needs the stage.

Every current workaround is lossy: dropping .default() removes defaultValue from the definition document, so --help --json and completions lose it; comparing against the default sentinel fails in exactly the case the check exists for, a user explicitly passing a value equal to the default; and .derive() runs after resolution, so it cannot tell explicit from default either.

Scope

Two layers, shipped together so the plumbing is reachable.

1. Resolver — carry the winning stage onto the success path

interface ResolveResult {
  readonly flags: Readonly<Record<string, unknown>>;
  readonly args: Readonly<Record<string, unknown>>;
  readonly deprecations: readonly DeprecationWarning[];
  readonly flagSources: Readonly<Record<string, FlagResolutionStage>>;
  readonly argSources: Readonly<Record<string, ArgResolutionStage>>;
}

2. Handler surface — expose it on ActionParams

Shape A, full provenance:

.action(({ flags, sources }) => {
  if (sources.flags.output === 'cli' && flags['keep-name']) { /* … */ }
});

Shape B, narrower predicate — covers (1)–(4) without committing the stage vocabulary to the public API, loses (5):

.action(({ flags, wasExplicit }) => {
  if (wasExplicit('output') && flags['keep-name']) { /* … */ }
});

Open question: ship A, B, or both.

Acceptance criteria

  • Winning stage recorded for every resolved flag and arg
  • FlagResolutionStage / ArgResolutionStage exported from the @kjanat/dreamcli root — today they stay in core/resolve/contracts
  • A handler can distinguish explicit from defaulted without dropping .default()
  • Declared defaults keep their defaultValue in the definition document
  • Docs: revise the Limitations page, where the "Exactly One Of" .derive() workaround silently depends on this gap not being hit

Blocks the cross-flag constraint work — Part 2 needs Part 1 underneath.

Activity

  1. added
    kind: featureNew capability or product enhancement
    area: cliCLI dispatch, planning, root UX, or plugins
    area: resolveArgv, env, config, prompt, and default resolution
    on Aug 1, 2026
  2. coderabbitai commented on Aug 1, 2026

    @coderabbitai
    ⚠️ Possible Duplicate Issue(s)
    🔗 Related PRs

    #1 - feat: shell completions, config discovery, custom flags, deprecation, and nested subcommand trees (v0.5–v0.7) [closed]
    #12 - add: prompt-flag compatibility validation [closed]
    #57 - feat: themed help output + negatable/duplicates/case-parity flags (rc.8) [merged]
    #60 - feat(schema): Standard Schema interop and DSL removal [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. self-assigned this
    on Aug 3, 2026
  4. added this to the v4 milestone on Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: cliCLI dispatch, planning, root UX, or pluginsarea: resolveArgv, env, config, prompt, and default resolutionarea: schemaSchema builders, schema DSL, and JSON Schema exportkind: featureNew capability or product enhancement

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions