Skip to content

docs(roadmap): add #468 — duplicate global flags silently last-win/accumulate with no provenance#3081

Open
Yeachan-Heo wants to merge 1 commit into
mainfrom
docs/roadmap-468-duplicate-global-flags-silent
Open

docs(roadmap): add #468 — duplicate global flags silently last-win/accumulate with no provenance#3081
Yeachan-Heo wants to merge 1 commit into
mainfrom
docs/roadmap-468-duplicate-global-flags-silent

Conversation

@Yeachan-Heo
Copy link
Copy Markdown
Contributor

ROADMAP pinpoint #468 — duplicate global flags silently last-win / accumulate with no provenance

Dogfooded for the 2026-05-24 18:30/19:00 Clawhip nudge window; finalized for message 1508182831573110904.

The pinpoint

Repeated global flags silently apply inconsistent merge semantics:

  • --model: last-write-wins
  • --permission-mode: last-write-wins
  • --output-format: last-write-wins, even if the first occurrence requested JSON
  • --allowedTools: accumulates/unions every occurrence

No duplicate_flags, no flag_occurrences, no overwritten-values provenance.

Repro matrix

# Last --model wins silently
claw --output-format json --model openai/gpt-4 --model opus status
# => model = claude-opus-4-6, model_raw = opus

claw --output-format json --model opus --model openai/gpt-4 status
# => model = openai/gpt-4

# Last --output-format wins silently, even overriding machine-readable intent
claw --output-format json --output-format text status
# => text prose on stdout, exit 0  ❌

claw --output-format text --output-format json status
# => JSON, exit 0

# Last permission mode wins silently
claw --output-format json --permission-mode read-only --permission-mode danger-full-access status
# => permission_mode = danger-full-access

claw --output-format json --permission-mode danger-full-access --permission-mode read-only status
# => permission_mode = read-only

# allowedTools accumulates instead of last-wins
claw --output-format json --allowedTools Bash --allowedTools Read status
# => entries ["bash", "read_file"]

claw --output-format json --allowedTools Read,Bash --allowedTools Edit status
# => entries ["bash", "edit_file", "read_file"]

Root cause traced

rust/crates/rusty-claude-cli/src/main.rs:617-694 uses mutable scalar parser state:

let mut model = DEFAULT_MODEL.to_string();
let mut model_flag_raw: Option<String> = None;
let mut output_format = CliOutputFormat::Text;
let mut permission_mode_override = None;

model = resolve_model_alias_with_config(value);
model_flag_raw = Some(value.clone());
output_format = CliOutputFormat::parse(value)?;
permission_mode_override = Some(parse_permission_mode_arg(value)?);

Each occurrence overwrites prior value.

But --allowedTools accumulates all occurrences into allowed_tool_values and normalizes later:

let allowed_tools = normalize_allowed_tools(&allowed_tool_values)?;

Same parser layer, two duplicate semantics, no provenance.

Why distinct

  • I was here #464 covers invalid --output-format values. This covers valid duplicate values, especially JSON intent overwritten by later text.
  • 前排留念 #117 covers -p greedily swallowing flags into prompt text. This covers normal global flags before subcommand.
  • 42 #122 covers --base-commit accepting arbitrary values / swallowing args. This covers duplicate flags that parse successfully.
  • 即将迎来一大波国产自研 CLI #97 covers --allowedTools ""; this entry notes allowedTools accumulation semantics versus scalar last-wins.
  • 真的假的kskbl #124 covers --model accepting garbage; this uses valid model values and focuses on duplicate/conflict invisibility.

Why it matters

  1. Automation wrappers commonly append flags. CLAW_ARGS="--output-format json" + wrapper-appended --output-format text silently breaks JSON consumers.
  2. Security posture drift: --permission-mode read-only --permission-mode danger-full-access silently escalates if unsafe flag appears later.
  3. Provider routing drift: --model openai/gpt-4 --model opus silently flips provider.
  4. Inconsistent semantics are not discoverable: why does duplicate --allowedTools union but duplicate --permission-mode overwrite?
  5. status cannot tell a claw whether final value came from one flag or conflicting duplicates.

Required fix shape

(a) Track occurrences for every global flag during parse with raw values and argv positions.

(b) For scalar flags, either reject duplicates with structured duplicate_flag parse error or allow last-wins but emit duplicate_flags / overwritten_flags provenance in status and doctor.

(c) For machine-output flags, prefer reject: --output-format json --output-format text should not silently break JSON consumers.

(d) For --allowedTools, document/expose accumulation semantics explicitly (source:"flag_accumulated", occurrences:2) or require comma-list single occurrence.

(e) Regression matrix covering examples above plus repeated --reasoning-effort / --base-commit.

Acceptance check

claw --output-format json --output-format text status

Should either fail with kind:"duplicate_flag" in a JSON error envelope or return JSON containing:

{"duplicate_flags":[{"flag":"--output-format","values":["json","text"],"effective":"text"}]}

Current behavior emits plain text and exit 0.

Method honesty

Initial repeated-flag probe was contaminated by zsh scalar splitting (whole arg string passed as one token). Evidence was re-run with eval and separate stdout/stderr capture before filing.


[repo owner's gaebal-gajae (clawdbot) 🦞]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants