Commit f84c7c4
committed
feat: ultraworkers#148 + ultraworkers#128 closure — model provenance in claw status JSON/text
## Scope
Two deltas in one commit:
### ultraworkers#128 closure (docs)
Re-verified on main HEAD `4cb8fa0`: malformed `--model` strings already
rejected at parse time (`validate_model_syntax` in parse_args). All
historical repro cases now produce specific errors:
claw --model '' → error: model string cannot be empty
claw --model 'bad model' → error: invalid model syntax: 'bad model' contains spaces
claw --model 'sonet' → error: invalid model syntax: 'sonet'. Expected provider/model or known alias
claw --model '@invalid' → error: invalid model syntax: '@invalid'. Expected provider/model ...
claw --model 'totally-not-real-xyz' → error: invalid model syntax: ...
claw --model sonnet → ok, resolves to claude-sonnet-4-6
claw --model anthropic/claude-opus-4-6 → ok, passes through
Marked ultraworkers#128 CLOSED in ROADMAP with repro block. Residual provenance gap
split off as ultraworkers#148.
### ultraworkers#148 implementation
**Problem.** After ultraworkers#128 closure, `claw status --output-format json`
still surfaces only the resolved model string. No way for a claw to
distinguish whether `claude-sonnet-4-6` came from `--model sonnet`
(alias resolution) vs `--model claude-sonnet-4-6` (pass-through) vs
`ANTHROPIC_MODEL` env vs `.claw.json` config vs compiled-in default.
Debug forensics had to re-read argv instead of reading a structured
field. Clawhip orchestrators sending `--model` couldn't confirm the
flag was honored vs falling back to default.
**Fix.** Added two fields to status JSON envelope:
- `model_source`: "flag" | "env" | "config" | "default"
- `model_raw`: user's input before alias resolution (null on default)
Text mode appends a `Model source` line under `Model`, showing the
source and raw input (e.g. `Model source flag (raw: sonnet)`).
**Resolution order** (mirrors resolve_repl_model but with source
attribution):
1. If `--model` / `--model=` flag supplied → source: flag, raw: flag value
2. Else if ANTHROPIC_MODEL set → source: env, raw: env value
3. Else if `.claw.json` model key set → source: config, raw: config value
4. Else → source: default, raw: null
## Changes
### rust/crates/rusty-claude-cli/src/main.rs
- Added `ModelSource` enum (Flag/Env/Config/Default) with `as_str()`.
- Added `ModelProvenance` struct (resolved, raw, source) with
three constructors: `default_fallback()`, `from_flag(raw)`, and
`from_env_or_config_or_default(cli_model)`.
- Added `model_flag_raw: Option<String>` field to `CliAction::Status`.
- Parse loop captures raw input in `--model` and `--model=` arms.
- Extended `parse_single_word_command_alias` to thread
`model_flag_raw: Option<&str>` through.
- Extended `print_status_snapshot` signature to accept
`model_flag_raw: Option<&str>`. Resolves provenance at dispatch time
(flag provenance from arg; else probe env/config/default).
- Extended `status_json_value` signature with
`provenance: Option<&ModelProvenance>`. On Some, adds `model_source`
and `model_raw` fields; on None (legacy resume paths), omits them
for backward compat.
- Extended `format_status_report` signature with optional provenance.
On Some, renders `Model source` line after `Model`.
- Updated all existing callers (REPL /status, resume /status, tests)
to pass None (legacy paths don't carry flag provenance).
- Added 2 regression assertions in parse_args test covering both
`--model sonnet` and `--model=...` forms.
### ROADMAP.md
- Marked ultraworkers#128 CLOSED with re-verification block.
- Filed ultraworkers#148 documenting the provenance gap split, fix shape, and
acceptance criteria.
## Live verification
$ claw --model sonnet --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-sonnet-4-6", "model_source": "flag", "model_raw": "sonnet"}
$ claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-opus-4-6", "model_source": "default", "model_raw": null}
$ ANTHROPIC_MODEL=haiku claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-haiku-4-5-20251213", "model_source": "env", "model_raw": "haiku"}
$ echo '{"model":"claude-opus-4-7"}' > .claw.json && claw --output-format json status | jq '{model,model_source,model_raw}'
{"model": "claude-opus-4-7", "model_source": "config", "model_raw": "claude-opus-4-7"}
$ claw --model sonnet status
Status
Model claude-sonnet-4-6
Model source flag (raw: sonnet)
Permission mode danger-full-access
...
## Tests
- rusty-claude-cli bin: 177 tests pass (2 new assertions for ultraworkers#148)
- Full workspace green except pre-existing resume_latest flake (unrelated)
Closes ROADMAP ultraworkers#128, ultraworkers#148.1 parent 4cb8fa0 commit f84c7c4
2 files changed
Lines changed: 253 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4815 | 4815 | | |
4816 | 4816 | | |
4817 | 4817 | | |
4818 | | - | |
| 4818 | + | |
| 4819 | + | |
| 4820 | + | |
| 4821 | + | |
| 4822 | + | |
| 4823 | + | |
| 4824 | + | |
| 4825 | + | |
| 4826 | + | |
| 4827 | + | |
| 4828 | + | |
| 4829 | + | |
| 4830 | + | |
4819 | 4831 | | |
4820 | 4832 | | |
4821 | 4833 | | |
| |||
5665 | 5677 | | |
5666 | 5678 | | |
5667 | 5679 | | |
| 5680 | + | |
| 5681 | + | |
| 5682 | + | |
| 5683 | + | |
| 5684 | + | |
| 5685 | + | |
| 5686 | + | |
| 5687 | + | |
| 5688 | + | |
| 5689 | + | |
| 5690 | + | |
| 5691 | + | |
| 5692 | + | |
| 5693 | + | |
| 5694 | + | |
| 5695 | + | |
| 5696 | + | |
| 5697 | + | |
| 5698 | + | |
| 5699 | + | |
| 5700 | + | |
| 5701 | + | |
| 5702 | + | |
| 5703 | + | |
| 5704 | + | |
| 5705 | + | |
| 5706 | + | |
| 5707 | + | |
| 5708 | + | |
| 5709 | + | |
| 5710 | + | |
| 5711 | + | |
| 5712 | + | |
| 5713 | + | |
| 5714 | + | |
| 5715 | + | |
| 5716 | + | |
| 5717 | + | |
| 5718 | + | |
| 5719 | + | |
| 5720 | + | |
| 5721 | + | |
| 5722 | + | |
0 commit comments