mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-25 13:44:06 +08:00
369 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d5373ac5d6 |
merge: fix/jobdori-161-worktree-git-sha — diagnostic-strictness family
Fix: resolve actual HEAD path in git worktrees for correct Git SHA in build metadata. In worktrees, .git is a pointer file not a directory, so cargo's rerun-if-changed=.git/HEAD never triggers. Per MERGE_CHECKLIST.md Cluster 2 (P1 Diagnostic-strictness, isolated): - 25 lines in build.rs only (no crate-level conflicts) - Verified: build → commit → rebuild → SHA updates correctly Diagnostic-strictness family member (joins #122/#122b). Applied: execution artifact runbook. Cycle #72 integration. |
||
|
|
a6f4e0d8d1 |
merge: feat/jobdori-130e-surface-help — help-parity cluster + #251 session-dispatch
Contains linear chain of 6 fixes: - #251: intercept session-management verbs at top-level parser (dc274a0) - #130b: enrich filesystem I/O errors with operation + path context (d49a75c) - #130c: accept --help / -h in claw diff arm (83f744a) - #130d: accept --help / -h in claw config arm, route to help topic (19638a0) - #130e-A: route help/submit/resume --help to help topics before credential check (0ca0344) - #130e-B: route plugins/prompt --help to dedicated help topics (9dd7e79) Per MERGE_CHECKLIST.md: - Cluster 1 (Typed-error): #251 (session-dispatch) - Cluster 3 (Help-parity): #130b, #130c, #130d, #130e-A/B All changes are in rust/crates/rusty-claude-cli/src/main.rs (dispatch/help routing). No test regressions expected (fixes add new guards, don't modify existing paths). Applied: execution artifact runbook. Cycle #72 integration. |
||
|
|
66765ea96d |
merge: docs/parity-update-2026-04-23 — refresh PARITY.md stats for 2026-04-23
Growth since 2026-04-03: - Rust LOC: 48,599 → 80,789 (+66%) - Test LOC: 2,568 → 4,533 (+76%) - Commits: 292 → 979 (+235%) Per MERGE_CHECKLIST.md Cluster 6 (P3 Doc-truthfulness, low-risk): - Diff: 4 lines in PARITY.md only - Zero code risk - Merge-ready Applied: execution artifact runbook. Cycle #72 integration. |
||
|
|
c5b6fa5be3 |
fix(#161): resolve actual HEAD path in git worktrees for correct Git SHA in build metadata
Problem: In git worktrees, .git is a pointer file (not a directory), so cargo's rerun-if-changed=.git/HEAD never triggers when commits are made. This causes claw version to report a stale SHA after new commits. Solution: Add resolve_git_head_path() helper that detects worktree mode: - If .git is a file: parse gitdir pointer, watch <gitdir>/HEAD - If .git is a directory: watch .git/HEAD (regular repo) This ensures build.rs invalidates on each commit, making version output truthful. Verification: Binary built in worktree now reports correct SHA after commits (before: stale, after: current HEAD). Relates to ROADMAP #161 (filed cycle #65, implemented cycle #69). Diagnostic-strictness family member. Diff: 21 lines added (resolve_git_head_path + conditional rerun-if-changed). |
||
|
|
553893410b |
fix(#160): reserved-semantic verbs with positional args now emit slash-command guidance
Verbs with CLI-reserved positional-arg meanings (resume, compact, memory, commit, pr, issue, bughunter) were falling through to Prompt dispatch when invoked with args, causing users to see 'missing_credentials' errors instead of guidance that the verb is a slash command. #160 investigation revealed the underlying design question: which verbs are 'promptable' (can start a prompt like 'explain this pattern') vs. 'reserved' (have specific CLI meaning like 'resume SESSION_ID')? This fix implements the reserved-verb classification: at parse time, intercept reserved verbs with trailing args and emit slash-command guidance before falling through to Prompt. Promptable verbs (explain, bughunter, clear) continue to route to Prompt as before. Helper: is_reserved_semantic_verb() lists the reserved set. All 181 tests pass (no regressions). |
||
|
|
0aa0d3f7cf |
fix(#122b): claw doctor warns when cwd is broad path (home/root)
## What Was Broken
`claw doctor` reported "Status: ok" when run from ~/ or /, but `claw
prompt` in the same directory would error out with:
error: claw is running from a very broad directory (/Users/yeongyu).
The agent can read and search everything under this path.
Diagnostic deception: doctor said green, prompt said red. User runs
doctor to check their setup, sees all green, runs prompt, gets blocked.
Trust in doctor erodes.
This is the exact pattern captured in the 'Diagnostic Commands Must Be
At Least As Strict As Runtime Commands' principle recorded in ROADMAP.md
at cycle #57.
## Root Cause
Two code paths perform the broad-cwd check:
- CliAction::Prompt handler → `enforce_broad_cwd_policy()` (errors out)
- CliAction::Repl handler → same function
But render_doctor_report() never called detect_broad_cwd(). The workspace
health check only looked at whether cwd was inside a git project, not
whether cwd was a dangerously broad path.
## What This Fix Does
Extend `check_workspace_health()` to also probe `detect_broad_cwd()`:
let broad_cwd = detect_broad_cwd();
let (level, summary) = match (in_repo, &broad_cwd) {
(_, Some(path)) => (
DiagnosticLevel::Warn,
format!(
"current directory is a broad path ({}); Prompt/REPL will \
refuse to run here without --allow-broad-cwd",
path.display()
),
),
(true, None) => (DiagnosticLevel::Ok, "project root detected"),
(false, None) => (DiagnosticLevel::Warn, "not inside a git project"),
};
The check now warns about BOTH failure modes with clear messaging about
what Prompt/REPL will do.
## Dogfood Verification
Before fix:
$ cd ~ && claw doctor
Workspace
Status warn
Summary current directory is not inside a git project
[all green otherwise]
$ echo | claw prompt "test"
error: claw is running from a very broad directory (/Users/yeongyu)...
After fix:
$ cd ~ && claw doctor
Workspace
Status warn
Summary current directory is a broad path (/Users/yeongyu);
Prompt/REPL will refuse to run here without
--allow-broad-cwd
$ cd / && claw doctor
Workspace
Status warn
Summary current directory is a broad path (/); ...
Non-regression:
$ cd /tmp/my-project && claw doctor
Workspace
Status warn
Summary current directory is not inside a git project
(unchanged)
$ cd /path/to/real/git/project && claw doctor
Workspace
Status ok
Summary project root detected on branch main
(unchanged)
## Regression Tests Added
- `workspace_check_in_project_dir_reports_ok` — non-broad + in-project = OK
- `workspace_check_outside_project_reports_warn` — non-broad + not-in-project = Warn with 'not inside git project' summary
- 181 binary tests pass (was 179, added 2)
## Related
- Principle: 'Diagnostic Commands Must Be At Least As Strict As Runtime
Commands' (ROADMAP.md cycle #57)
- Companion to #122 (stale-base preflight in doctor)
- Sibling: next step is probably a full runtime-vs-doctor audit for
other asymmetries (auth, sandbox, plugins, hooks)
|
||
|
|
9dd7e79eb2 |
fix(#130e-B): route plugins/prompt --help to dedicated help topics
## What Was Broken (ROADMAP #130e Category B)
Two remaining surface-level help outliers after #130e-A:
$ claw plugins --help
Unknown /plugins action '--help'. Use list, install, enable, disable, uninstall, or update.
$ claw prompt --help
claw v0.1.0 (top-level help — wrong help topic)
`plugins` treated `--help` as an invalid subaction name. `prompt`
was explicitly listed in the early `wants_help` interception with
commit/pr/issue, which routed to top-level help instead of
prompt-specific help.
## Root Cause (Traced)
1. **plugins**: `parse_local_help_action()` didn't have a "plugins"
arm, so `["plugins", "--help"]` returned None and continued into
the `"plugins"` parser arm (main.rs:1031), which treated `--help`
as the `action` argument. Runtime layer then rejected it as
"Unknown action".
2. **prompt**: At main.rs:~800, there was an early interception for
`--help` following certain subcommands (prompt, commit, pr, issue)
that forced `wants_help = true`, routing to generic top-level help
instead of letting parse_local_help_action produce a prompt-specific
topic.
## What This Fix Does
Same pattern as #130c/#130d/#130e-A:
1. **LocalHelpTopic enum extended** with Plugins, Prompt variants
2. **parse_local_help_action() extended** to map both new cases
3. **Help topic renderers added** with accurate usage info
4. **Early prompt-interception removed** — prompt now falls through to
parse_local_help_action like other subcommands. commit/pr/issue
(which aren't actual subcommands yet) remain in the early list.
## Dogfood Verification
Before fix:
$ claw plugins --help
Unknown /plugins action '--help'. Use list, install, enable, ...
$ claw prompt --help
claw v0.1.0
(top-level help, not prompt-specific)
After fix:
$ claw plugins --help
Plugins
Usage claw plugins [list|install|enable|disable|uninstall|update] [<target>]
Purpose manage bundled and user plugins from the CLI surface
...
$ claw prompt --help
Prompt
Usage claw prompt <prompt-text>
Purpose run a single-turn, non-interactive prompt and exit
Flags --model · --allowedTools · --output-format · --compact
...
## Non-Regression Verification
- `claw plugins` (no args) → still displays plugin inventory ✅
- `claw plugins list` → still works correctly ✅
- `claw prompt "text"` → still requires credentials, runs prompt ✅
- All 180 binary tests pass ✅
- All 466 library tests pass ✅
## Regression Tests Added (4+ assertions)
- `plugins --help` → HelpTopic(Plugins)
- `prompt --help` → HelpTopic(Prompt)
- Short forms `plugins -h` / `prompt -h` both work
- `prompt "hello world"` still routes to Prompt action with correct text
## HELP-PARITY SWEEP COMPLETE
All 22 top-level subcommands now emit proper help topics:
| Command | Status |
|---|---|
| help --help | ✅ #130e-A |
| version --help | ✅ pre-existing |
| status --help | ✅ pre-existing |
| sandbox --help | ✅ pre-existing |
| doctor --help | ✅ pre-existing |
| acp --help | ✅ pre-existing |
| init --help | ✅ pre-existing |
| state --help | ✅ pre-existing |
| export --help | ✅ pre-existing |
| diff --help | ✅ #130c |
| config --help | ✅ #130d |
| mcp --help | ✅ pre-existing |
| agents --help | ✅ pre-existing |
| plugins --help | ✅ #130e-B (this commit) |
| skills --help | ✅ pre-existing |
| submit --help | ✅ #130e-A |
| prompt --help | ✅ #130e-B (this commit) |
| resume --help | ✅ #130e-A |
| system-prompt --help | ✅ pre-existing |
| dump-manifests --help | ✅ pre-existing |
| bootstrap-plan --help | ✅ pre-existing |
Zero outliers. Contract universally enforced.
## Related
- Closes #130e Category B (plugins, prompt surface-parity)
- Completes entire help-parity sweep family (#130c, #130d, #130e)
- Stacks on #130e-A (dispatch-order fixes) on same worktree
|
||
|
|
0ca034472b |
fix(#130e-A): route help/submit/resume --help to help topics before credential check
## What Was Broken (ROADMAP #130e, filed cycle #53) Three subcommands leaked `missing_credentials` errors when called with `--help`: $ claw help --help [error-kind: missing_credentials] error: missing Anthropic credentials... $ claw submit --help [error-kind: missing_credentials] error: missing Anthropic credentials... $ claw resume --help [error-kind: missing_credentials] error: missing Anthropic credentials... This is the same dispatch-order bug class as #251 (session verbs). The parser fell through to the credential check before help-flag resolution ran. Critical discoverability gap: users couldn't learn what these commands do without valid credentials. ## Root Cause (Traced) `parse_local_help_action()` (main.rs:1260) is called early in `parse_args()` (main.rs:1002), BEFORE credential check. But the match statement inside only recognized: status, sandbox, doctor, acp, init, state, export, version, system-prompt, dump-manifests, bootstrap-plan, diff, config. `help`, `submit`, `resume` were NOT in the list, so the function returned `None`, and parsing continued to credential check which then failed. ## What This Fix Does Same pattern as #130c (diff) and #130d (config): 1. **LocalHelpTopic enum extended** with Meta, Submit, Resume variants 2. **parse_local_help_action() extended** to map the three new cases 3. **Help topic renderers added** with accurate usage info Three-line change to parse_local_help_action: "help" => LocalHelpTopic::Meta, "submit" => LocalHelpTopic::Submit, "resume" => LocalHelpTopic::Resume, Dispatch order (parse_args): 1. --resume parsing 2. parse_local_help_action() ← NOW catches help/submit/resume --help 3. parse_single_word_command_alias() 4. parse_subcommand() ← Credential check happens here ## Dogfood Verification Before fix (all three): $ claw help --help [error-kind: missing_credentials] error: missing Anthropic credentials... After fix: $ claw help --help Help Usage claw help [--output-format <format>] Purpose show the full CLI help text (all subcommands, flags, environment) ... $ claw submit --help Submit Usage claw submit [--session <id|latest>] <prompt-text> Purpose send a prompt to an existing managed session Requires valid Anthropic credentials (when actually submitting) ... $ claw resume --help Resume Usage claw resume [<session-id|latest>] Purpose restart an interactive REPL attached to a managed session ... ## Non-Regression Verification - `claw help` (no --help) → still shows full CLI help ✅ - `claw submit "text"` (with prompt) → still requires credentials ✅ - `claw resume` (bare) → still emits slash command guidance ✅ - All 180 binary tests pass ✅ - All 466 library tests pass ✅ ## Regression Tests Added (6 assertions) - `help --help` → routes to HelpTopic(Meta) - `submit --help` → routes to HelpTopic(Submit) - `resume --help` → routes to HelpTopic(Resume) - Short forms: `help -h`, `submit -h`, `resume -h` all work ## Pattern Note This is Category A of #130e (dispatch-order bugs). Same class as #251. Category B (surface-parity: plugins, prompt) will be handled in a follow-up commit/branch. ## Help-Parity Sweep Status After cycle #52 (#130c diff, #130d config), help sweep revealed: | Command | Before | After This Commit | |---|---|---| | help --help | missing_credentials | ✅ Meta help | | submit --help | missing_credentials | ✅ Submit help | | resume --help | missing_credentials | ✅ Resume help | | plugins --help | "Unknown action" | ⏳ #130e-B (next) | | prompt --help | wrong help | ⏳ #130e-B (next) | ## Related - Closes #130e Category A (dispatch-order help fixes) - Same bug class as #251 (session verbs) - Stacks on #130d (config help) on same worktree branch - #130e Category B (plugins, prompt) queued for follow-up |
||
|
|
19638a015e |
fix(#130d): accept --help / -h in claw config arm, route to help topic
## What Was Broken (ROADMAP #130d, filed cycle #52) `claw config --help` was silently ignored — the command executed and displayed the config dump instead of showing help: $ claw config --help Config Working directory /private/tmp/dogfood-probe-47 Loaded files 0 Merged keys 0 (displays full config, not help) Expected: help for the config command. Actual: silent acceptance of `--help`, runs config display anyway. This is the opposite outlier from #130c (which rejected help with an error). Together they form the help-parity anomaly: - #130c `diff --help` → error (rejects help) - #130d `config --help` → silent ignore (runs command, ignores help) - Others (status, mcp, export) → proper help - Expected behavior: all commands should show help on `--help` ## Root Cause (Traced) At main.rs:1050, the `"config"` parser arm parsed arguments positionally: "config" => { let tail = &rest[1..]; let section = tail.first().cloned(); // ... ignores unrecognized args like --help silently Ok(CliAction::Config { section, ... }) } Unlike the `diff` arm (#130c), `config` had no explicit check for extra args. It positionally parsed the first arg as an optional `section` and silently accepted/ignored any trailing arg, including `--help`. ## What This Fix Does Same pattern as #130c (help-surface parity): 1. **LocalHelpTopic enum extended** with new `Config` variant 2. **parse_local_help_action() extended** to map `"config"` → `LocalHelpTopic::Config` 3. **config arm guard added**: check for help flag before parsing section 4. **Help topic renderer added**: human-readable help text for config Fix locus at main.rs:1050: "config" => { // #130d: accept --help / -h and route to help topic if rest.len() >= 2 && is_help_flag(&rest[1]) { return Ok(CliAction::HelpTopic(LocalHelpTopic::Config)); } let tail = &rest[1..]; // ... existing parsing continues } ## Dogfood Verification Before fix: $ claw config --help Config Working directory ... Loaded files 0 (no help, runs config) After fix: $ claw config --help Config Usage claw config [--cwd <path>] [--output-format <format>] Purpose merge and display the resolved configuration Options --cwd overrides the workspace directory Output loaded files and merged key-value pairs Formats text (default), json Related claw status · claw doctor · claw init Short form `claw config -h` also works. ## Non-Regression Verification - `claw config` (no args) → still displays config dump ✅ - `claw config permissions` (section arg) → still works ✅ - All 180 binary tests pass ✅ - All 466 library tests pass ✅ ## Regression Tests Added (4 assertions) - `config --help` → routes to `HelpTopic(LocalHelpTopic::Config)` - `config -h` (short form) → routes to help topic - bare `config` (no args) → still routes to `Config` action - `config permissions` (with section) → still works correctly ## Pattern Note #130c and #130d form a pair: two outlier failure modes in help handling for local introspection commands: - #130c `diff` rejected help (loud error) → fixed with guard + routing - #130d `config` silently ignored help (silent accept) → fixed with same pattern Both are now consistent with the rest of the CLI (status, mcp, export, etc.). ## Related - Closes #130d (config help discoverability gap) - Completes help-parity family (#130c, #130d) - Stacks on #130c (diff help fix) on same worktree branch - Part of help-consistency thread (#141 audit) |
||
|
|
83f744adf0 |
fix(#130c): accept --help / -h in claw diff arm
## What Was Broken (ROADMAP #130c, filed cycle #50) `claw diff --help` was rejected with: [error-kind: unknown] error: unexpected extra arguments after `claw diff`: --help Other local introspection commands accept --help fine: - `claw status --help` → shows help ✅ - `claw mcp --help` → shows help ✅ - `claw export --help` → shows help ✅ - `claw diff --help` → error ❌ (outlier) This is a help-surface parity bug: `diff` is the only local command that rejects --help as "extra arguments" before the help detector gets a chance to run. ## Root Cause (Traced) At main.rs:1063, the `"diff"` parser arm rejected ALL extra args: "diff" => { if rest.len() > 1 { return Err(format!("unexpected extra arguments after `claw diff`: {}", ...)); } Ok(CliAction::Diff { output_format }) } When parsing `["diff", "--help"]`, `rest.len() > 1` was true (length is 2) and `--help` was rejected as extra argument. Other commands (status, sandbox, doctor, init, state, export, etc.) routed through `parse_local_help_action()` which detected `--help` / `-h` and routed to a LocalHelpTopic. The `diff` arm lacked this guard. ## What This Fix Does Three minimal changes: 1. **LocalHelpTopic enum extended** with new `Diff` variant 2. **parse_local_help_action() extended** to map `"diff"` → `LocalHelpTopic::Diff` 3. **diff arm guard added**: check for help flag before extra-args validation 4. **Help topic renderer added**: human-readable help text for diff command Fix locus at main.rs:1063: "diff" => { // #130c: accept --help / -h as first argument and route to help topic if rest.len() == 2 && is_help_flag(&rest[1]) { return Ok(CliAction::HelpTopic(LocalHelpTopic::Diff)); } if rest.len() > 1 { /* existing error */ } Ok(CliAction::Diff { output_format }) } ## Dogfood Verification Before fix: $ claw diff --help [error-kind: unknown] error: unexpected extra arguments after `claw diff`: --help After fix: $ claw diff --help Diff Usage claw diff [--output-format <format>] Purpose show local git staged + unstaged changes Requires workspace must be inside a git repository ... And `claw diff -h` (short form) also works. ## Non-Regression Verification - `claw diff` (no args) → still routes to Diff action correctly - `claw diff foo` (unknown arg) → still rejected as "unexpected extra arguments" - `claw diff --output-format json` (valid flag) → still works - All 180 binary tests pass - All 466 library tests pass ## Regression Tests Added (4 assertions) - `diff --help` → routes to HelpTopic(LocalHelpTopic::Diff) - `diff -h` (short form) → routes to HelpTopic(LocalHelpTopic::Diff) - bare `diff` → still routes to Diff action - `diff foo` (unknown arg) → still errors with "extra arguments" ## Pattern Follows #141 help-consistency work (extending LocalHelpTopic to cover more subcommands). Clean surface-parity fix: identify the outlier, add the missing guard. Low-risk, high-clarity. ## Related - Closes #130c (diff help discoverability gap) - Stacks on #130b (filesystem context) and #251 (session dispatch) - Part of help-consistency thread (#141 audit, #145 plugins wiring) |
||
|
|
d49a75cad5 |
fix(#130b): enrich filesystem I/O errors with operation + path context
## What Was Broken (ROADMAP #130b, filed cycle #47) In a fresh workspace, running: claw export latest --output /private/nonexistent/path/file.jsonl --output-format json produced: {"error":"No such file or directory (os error 2)","hint":null,"kind":"unknown","type":"error"} This violates the typed-error contract: - Error message is a raw errno string with zero context - Does not mention the operation that failed (export) - Does not mention the target path - Classifier defaults to "unknown" even though the code path knows this is a filesystem I/O error ## Root Cause (Traced) run_export() at main.rs:~6915 does: fs::write(path, &markdown)?; When this fails: 1. io::Error propagates via ? to main() 2. Converted to string via .to_string() in error handler 3. classify_error_kind() cannot match "os error" or "No such file" 4. Defaults to "kind": "unknown" The information is there at the source (operation name, target path, io::ErrorKind) but lost at the propagation boundary. ## What This Fix Does Three changes: 1. **New helper: contextualize_io_error()** (main.rs:~260) Wraps an io::Error with operation name + target path into a recognizable message format: "{operation} failed: {target} ({error})" 2. **Classifier branch added** (classify_error_kind at main.rs:~270) Recognizes the new format and classifies as "filesystem_io_error": else if message.contains("export failed:") || message.contains("diff failed:") || message.contains("config failed:") { "filesystem_io_error" } 3. **run_export() wired** (main.rs:~6915) fs::write() call now uses .map_err() to enrich io::Error: fs::write(path, &markdown).map_err(|e| -> Box<dyn std::error::Error> { contextualize_io_error("export", &path.display().to_string(), e).into() })?; ## Dogfood Verification Before fix: {"error":"No such file or directory (os error 2)","kind":"unknown","type":"error"} After fix: {"error":"export failed: /private/nonexistent/path/file.jsonl (No such file or directory (os error 2))","kind":"filesystem_io_error","type":"error"} The envelope now tells downstream claws: - WHAT operation failed (export) - WHERE it failed (the path) - WHAT KIND of failure (filesystem_io_error) - The original errno detail preserved for diagnosis ## Non-Regression Verification - Successful export still works (emits "kind": "export" envelope as before) - Session not found error still emits "session_not_found" (not filesystem) - missing_credentials still works correctly - cli_parse still works correctly - All 180 binary tests pass - All 466 library tests pass - All 95 compat-harness tests pass ## Regression Tests Added Inside the main CliAction test function: - "export failed:" pattern classifies as "filesystem_io_error" (not "unknown") - "diff failed:" pattern classifies as "filesystem_io_error" - "config failed:" pattern classifies as "filesystem_io_error" - contextualize_io_error() produces a message containing operation name - contextualize_io_error() produces a message containing target path - Messages produced by contextualize_io_error() are classifier-recognizable ## Scope This is the minimum viable fix: enrich export's fs::write with context. Future work (filed as part of #130b scope): apply same pattern to other filesystem operations (diff, plugins, config fs reads, session store writes, etc.). Each application is a copy-paste of the same helper pattern. ## Pattern Follows #145 (plugins parser interception), #248-249 (arm-level leak templates). Helper + classifier + call site wiring. Minimal diff, maximum observability gain. ## Related - Closes #130b (filesystem error context preservation) - Stacks on top of #251 (dispatch-order fix) — same worktree branch - Ground truth for future #130 broader sweep (other io::Error sites) |
||
|
|
dc274a0f96 |
fix(#251): intercept session-management verbs at top-level parser to bypass credential check
## What Was Broken (ROADMAP #251) Session-management verbs (list-sessions, load-session, delete-session, flush-transcript) were falling through to the parser's `_other => Prompt` catchall at main.rs:~1017. This construed them as `CliAction::Prompt { prompt: "list-sessions", ... }` which then required credentials via the Anthropic API path. The result: purely-local session operations emitted `missing_credentials` errors instead of session-layer envelopes. ## Acceptance Criterion The fix's essential requirement (stated by gaebal-gajae): **"These 4 verbs stop falling through to Prompt and emitting `missing_credentials`."** Not "all 4 are fully implemented to spec" — stubs are acceptable for delete-session and flush-transcript as long as they route LOCALLY. ## What This Fix Does Follows the exact pattern from #145 (plugins) and #146 (config/diff): 1. **CliAction enum** (main.rs:~700): Added 4 new variants. 2. **Parser** (main.rs:~945): Added 4 match arms before the `_other => Prompt` catchall. Each arm validates the verb's positional args (e.g., load-session requires a session-id) and rejects extra arguments. 3. **Dispatcher** (main.rs:~455): - list-sessions → dispatches to `runtime::session_control::list_managed_sessions_for()` - load-session → dispatches to `runtime::session_control::load_managed_session_for()` - delete-session → emits `not_yet_implemented` error (local, not auth) - flush-transcript → emits `not_yet_implemented` error (local, not auth) ## Dogfood Verification Run on clean environment (no credentials): ```bash $ env -i PATH=$PATH HOME=$HOME claw list-sessions --output-format json { "command": "list-sessions", "sessions": [ {"id": "session-1775777421902-1", ...}, ... ] } # ✓ Session-layer envelope, not auth error $ env -i PATH=$PATH HOME=$HOME claw load-session nonexistent --output-format json {"error":"session not found: nonexistent", "kind":"session_not_found", ...} # ✓ Local session_not_found error, not missing_credentials $ env -i PATH=$PATH HOME=$HOME claw delete-session test-id --output-format json {"command":"delete-session","error":"not_yet_implemented","kind":"not_yet_implemented","type":"error"} # ✓ Local not_yet_implemented, not auth error $ env -i PATH=$PATH HOME=$HOME claw flush-transcript test-id --output-format json {"command":"flush-transcript","error":"not_yet_implemented","kind":"not_yet_implemented","type":"error"} # ✓ Local not_yet_implemented, not auth error ``` Regression sanity: ```bash $ claw plugins --output-format json # #145 still works $ claw prompt "hello" --output-format json # still requires credentials correctly $ claw list-sessions extra arg --output-format json # rejects extra args with cli_parse ``` ## Regression Tests Added Inside `removed_login_and_logout_subcommands_error_helpfully` test function: - `list-sessions` → CliAction::ListSessions (both text and JSON output) - `load-session <id>` → CliAction::LoadSession with session_reference - `delete-session <id>` → CliAction::DeleteSession with session_id - `flush-transcript <id>` → CliAction::FlushTranscript with session_id - Missing required arg errors (load-session and delete-session without ID) - Extra args rejection (list-sessions with extra positional args) All 180 binary tests pass. 466 library tests pass. ## Fix Scope vs. Full Implementation This fix addresses #251 (dispatch-order bug) and #250's Option A (implement the surfaces). list-sessions and load-session are fully functional via existing runtime::session_control helpers. delete-session and flush-transcript are stubbed with local "not yet implemented" errors to satisfy #251's acceptance criterion without requiring additional session-store mutations that can ship independently in a follow-up. ## Template Exact same pattern as #145 (plugins) and #146 (config/diff): top-level verb interception → CliAction variant → dispatcher with local operation. ## Related Closes #251. Addresses #250 Option A for 4 verbs. Does not block #250 Option B (documentation scope guards) which remains valuable. |
||
|
|
84466bbb6c |
fix: #247 classify prompt-related parse errors + unify JSON hint plumbing
Cycle #34 dogfood follow-through on Jobdori cycle #33 pinpoint (#247 filed at fbcbe9d). Closes the two typed-error contract drifts surfaced in that pinpoint against the Rust `claw` binary. ## What was wrong 1. `classify_error_kind()` (main.rs:~251) used substring matching but did NOT match two common prompt-related parse errors: - "prompt subcommand requires a prompt string" - "empty prompt: provide a subcommand..." Both fell through to `"unknown"`. §4.44 typed-error contract specifies `parse | usage | unknown` as distinct classes, so claws dispatching on `error.kind == "cli_parse"` missed those paths entirely. 2. JSON mode dropped the `Run `claw --help` for usage.` hint. Text mode appends it at stderr-print time (main.rs:~234) AFTER split_error_hint() has already serialized the envelope, so JSON consumers never saw it. Text-mode humans got an actionable pointer; machine consumers did not. ## Fix Two small, targeted edits: 1. `classify_error_kind()`: add explicit branches for "prompt subcommand requires" and "empty prompt:" (the latter anchored with `starts_with` so it never hijacks unrelated error messages containing the word). Both route to `cli_parse`. 2. JSON error render path in `main()`: after calling split_error_hint(), if the message carried no embedded hint AND kind is `cli_parse` AND the short-reason does not already embed a `claw --help` pointer, synthesize the same `Run `claw --help` for usage.` trailer that text-mode stderr appends. The embedded-pointer check prevents duplication on the `empty prompt: ... (run `claw --help`)` message which already carries inline guidance. ## Verification Direct repro on the compiled binary: $ claw --output-format json prompt {"error":"prompt subcommand requires a prompt string", "hint":"Run `claw --help` for usage.", "kind":"cli_parse","type":"error"} $ claw --output-format json "" {"error":"empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string", "hint":null,"kind":"cli_parse","type":"error"} $ claw --output-format json doctor --foo # regression guard {"error":"unrecognized argument `--foo` for subcommand `doctor`", "hint":"Run `claw --help` for usage.", "kind":"cli_parse","type":"error"} Text mode unchanged in shape; `[error-kind: ...]` prefix now reads `cli_parse` for the two previously-misclassified paths. ## Regression coverage - Unit test `classify_error_kind_covers_prompt_parse_errors_247`: locks both patterns route to `cli_parse` AND that generic "prompt"-containing messages still fall through to `unknown`. - Integration tests in `tests/output_format_contract.rs`: * prompt_subcommand_without_arg_emits_cli_parse_envelope_with_hint_247 * empty_positional_arg_emits_cli_parse_envelope_247 * whitespace_only_positional_arg_emits_cli_parse_envelope_247 * unrecognized_argument_still_classifies_as_cli_parse_247_regression_guard - Full rusty-claude-cli test suite: 218 tests pass (180 bin unit + 15 output_format_contract + 12 resume_slash + 7 compact + 3 mock + 1 cli). ## Family / related Joins §4.44 typed-envelope contract gap family closure: #130, #179, #181, and now **#247**. All four quartet items now have real fixes landed on the canonical binary surface rather than only the Python harness. ROADMAP.md: #247 marked CLOSED with before/after evidence preserved. |
||
|
|
f1e4ad7574 |
feat: #156 — error classification for text-mode output (Phase 2 of #77)
## Problem #77 Phase 1 added machine-readable error `kind` discriminants to JSON error payloads. Text-mode (stderr) errors still emit prose-only output with no structured classification. Observability tools (log aggregators, CI error parsers) parsing stderr can't distinguish error classes without regex-scraping the prose. ## Fix Added `[error-kind: <class>]` prefix line to all text-mode error output. The prefix appears before the error prose, making it immediately parseable by line-based log tools without any substring matching. **Examples:** ## Impact - Stderr observers (log aggregators, CI systems) can now parse error class from the first line without regex or substring scraping - Same classifier function used for JSON (#77 P1) and text modes - Text-mode output remains human-readable (error prose unchanged) - Prefix format follows syslog/structured-logging conventions ## Tests All 179 rusty-claude-cli tests pass. Verified on 3 different error classes. Closes ROADMAP #156. |
||
|
|
9362900b1b |
feat: #77 Phase 1 — machine-readable error classification in JSON error payloads
## Problem
All JSON error payloads had the same three-field envelope:
```json
{"type": "error", "error": "<prose with hint baked in>"}
```
Five distinct error classes were indistinguishable at the schema level:
- missing_credentials (no API key)
- missing_worker_state (no state file)
- session_not_found / session_load_failed
- cli_parse (unrecognized args)
- invalid_model_syntax
Downstream claws had to regex-scrape the prose to route failures.
## Fix
1. **Added `classify_error_kind()`** — prefix/keyword classifier that returns a
snake_case discriminant token for 12 known error classes:
`missing_credentials`, `missing_manifests`, `missing_worker_state`,
`session_not_found`, `session_load_failed`, `no_managed_sessions`,
`cli_parse`, `invalid_model_syntax`, `unsupported_command`,
`unsupported_resumed_command`, `confirmation_required`, `api_http_error`,
plus `unknown` fallback.
2. **Added `split_error_hint()`** — splits multi-line error messages into
(short_reason, optional_hint) so the runbook prose stops being stuffed
into the `error` field.
3. **Extended JSON envelope** at 4 emit sites:
- Main error sink (line ~213)
- Session load failure in resume_session
- Stub command (unsupported_command)
- Unknown resumed command (unsupported_resumed_command)
## New JSON shape
```json
{
"type": "error",
"error": "short reason (first line)",
"kind": "missing_credentials",
"hint": "Hint: export ANTHROPIC_API_KEY..."
}
```
`kind` is always present. `hint` is null when no runbook follows.
`error` now carries only the short reason, not the full multi-line prose.
## Tests
Added 2 new regression tests:
- `classify_error_kind_returns_correct_discriminants` — all 9 known classes + fallback
- `split_error_hint_separates_reason_from_runbook` — with and without hints
All 179 rusty-claude-cli tests pass. Full workspace green.
Closes ROADMAP #77 Phase 1.
|
||
|
|
3cfe6e2b14 |
feat: #154 — hint provider prefix and env var when model name looks like different provider
## Problem When a user types `claw --model gpt-4` or `--model qwen-plus`, they get: ``` error: invalid model syntax: 'gpt-4'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias ``` USAGE.md documents that "The error message now includes a hint that names the detected env var" — but this hint does not actually exist. The user has to re-read USAGE.md or guess the correct prefix. ## Fix Enhance `validate_model_syntax` to detect when a model name looks like it belongs to a different provider: 1. **OpenAI models** (starts with `gpt-` or `gpt_`): ``` Did you mean `openai/gpt-4`? (Requires OPENAI_API_KEY env var) ``` 2. **Qwen/DashScope models** (starts with `qwen`): ``` Did you mean `qwen/qwen-plus`? (Requires DASHSCOPE_API_KEY env var) ``` 3. **Grok/xAI models** (starts with `grok`): ``` Did you mean `xai/grok-3`? (Requires XAI_API_KEY env var) ``` Unrelated invalid models (e.g., `asdfgh`) do not get a spurious hint. ## Verification - `claw --model gpt-4` → hints `openai/gpt-4` + `OPENAI_API_KEY` - `claw --model qwen-plus` → hints `qwen/qwen-plus` + `DASHSCOPE_API_KEY` - `claw --model grok-3` → hints `xai/grok-3` + `XAI_API_KEY` - `claw --model asdfgh` → generic error (no hint) ## Tests Added 3 new assertions in `parses_multiple_diagnostic_subcommands`: - GPT model error hints openai/ prefix and OPENAI_API_KEY - Qwen model error hints qwen/ prefix and DASHSCOPE_API_KEY - Unrelated models don't get a spurious hint All 177 rusty-claude-cli tests pass. Closes ROADMAP #154. |
||
|
|
79352a2d20 |
feat: #152 — hint --output-format json when user types --json on diagnostic verbs
## Problem Users commonly type `claw doctor --json`, `claw status --json`, or `claw system-prompt --json` expecting JSON output. These fail with `unrecognized argument \`--json\` for subcommand` with no hint that `--output-format json` is the correct flag. ## Discovery Filed as #152 during 21:17 dogfood nudge. The #127 worktree contained a more comprehensive patch but conflicted with #141 (unified --help). On re-investigation of main, Bugs 1 and 3 from #127 are already closed (positional arg rejection works, no double "error:" prefix). Only Bug 2 (the `--json` hint) remained. ## Fix Two call sites add the hint: 1. `parse_single_word_command_alias`'s diagnostic-verb suffix path: when rest[1] == "--json", append "Did you mean \`--output-format json\`?" 2. `parse_system_prompt_options` unknown-option path: same hint when the option is exactly `--json`. ## Verification Before: $ claw doctor --json error: unrecognized argument `--json` for subcommand `doctor` Run `claw --help` for usage. After: $ claw doctor --json error: unrecognized argument `--json` for subcommand `doctor` Did you mean `--output-format json`? Run `claw --help` for usage. Covers: `doctor --json`, `status --json`, `sandbox --json`, `system-prompt --json`, and any other diagnostic verb that routes through `parse_single_word_command_alias`. Other unrecognized args (`claw doctor garbage`) correctly don't trigger the hint. ## Tests - 2 new assertions in `parses_multiple_diagnostic_subcommands`: - `claw doctor --json` produces hint - `claw doctor garbage` does NOT produce hint - 177 rusty-claude-cli tests pass - Workspace tests green Closes ROADMAP #152. |
||
|
|
eaa077bf91 |
fix: #150 — eliminate symlink canonicalization flake in resume_latest test + file #246 (reminder outcome ambiguity)
## #150 Fix: resume_latest test flake **Problem:** `resume_latest_restores_the_most_recent_managed_session` intermittently fails when run in the workspace suite or multiple times in sequence, but passes in isolation. **Root cause:** `workspace_fingerprint(path)` hashes the path string without canonicalization. On macOS, `/tmp` is a symlink to `/private/tmp`. The test creates a temp dir via `std::env::temp_dir().join(...)` which returns `/var/folders/...` (non-canonical). When the subprocess spawns, `env::current_dir()` returns the canonical path `/private/var/folders/...`. The two fingerprints differ, so the subprocess looks in `.claw/sessions/<hash1>` while files are in `.claw/sessions/<hash2>`. Session discovery fails. **Fix:** Call `fs::canonicalize(&project_dir)` after creating the directory to ensure test and subprocess use identical path representations. **Verification:** 5 consecutive runs of the full test suite — all pass. Previously: 5/5 failed when run in sequence. ## #246 Filing: Reminder cron outcome ambiguity (control-loop blocker) The `clawcode-dogfood-cycle-reminder` cron times out repeatedly with no structured feedback on whether the nudge was delivered, skipped, or died in-flight. **Phase 1 outcome schema** — add explicit field to cron result: - `delivered` — nudge posted to Discord - `timed_out_before_send` — died before posting - `timed_out_after_send` — posted but cleanup timed out - `skipped_due_to_active_cycle` — previous cycle active - `aborted_gateway_draining` — daemon shutdown Assigned to gaebal-gajae (cron/orchestration domain). Unblocks trustworthy dogfood cycle observability. Closes ROADMAP #150. Filed ROADMAP #246. |
||
|
|
f84c7c4ed5 |
feat: #148 + #128 closure — model provenance in claw status JSON/text
## Scope Two deltas in one commit: ### #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 #128 CLOSED in ROADMAP with repro block. Residual provenance gap split off as #148. ### #148 implementation **Problem.** After #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 #128 CLOSED with re-verification block. - Filed #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 #148) - Full workspace green except pre-existing resume_latest flake (unrelated) Closes ROADMAP #128, #148. |
||
|
|
4cb8fa059a |
feat: #147 — reject empty / whitespace-only prompts at CLI fallthrough
## Problem
The `"prompt"` subcommand arm enforced `if prompt.trim().is_empty()`
and returned a specific error. The fallthrough `other` arm in the same
match block — which routes any unrecognized first positional arg to
`CliAction::Prompt` — had no such guard. Result:
$ claw ""
error: missing Anthropic credentials; export ANTHROPIC_AUTH_TOKEN ...
$ claw " "
error: missing Anthropic credentials; ...
$ claw "" ""
error: missing Anthropic credentials; ...
$ claw --output-format json ""
{"error":"missing Anthropic credentials; ...","type":"error"}
An empty prompt should never reach the credentials check. Worse: with
valid credentials, the literal empty string gets sent to Claude as a
user prompt, either burning tokens for nothing or triggering a model-
side refusal. Same prompt-misdelivery family as #145.
## Root cause
In `parse_subcommand()`, the final `other =>` arm in the top-level
match only guards against typos (#108 guard via `looks_like_subcommand_typo`)
and then unconditionally builds `CliAction::Prompt { prompt: rest.join(" ") }`.
An empty/whitespace-only join passes through.
## Changes
### rust/crates/rusty-claude-cli/src/main.rs
Added the same `if joined.trim().is_empty()` guard already used in the
`"prompt"` arm to the fallthrough path. Error message distinguishes it
from the `prompt` subcommand path:
empty prompt: provide a subcommand (run `claw --help`) or a
non-empty prompt string
Runs AFTER the typo guard (so `claw sttaus` still suggests `status`)
and BEFORE CliAction::Prompt construction (so no network call ever
happens for empty inputs).
### Regression tests
Added 4 assertions in the existing parse_args test:
- parse_args([""]) → Err("empty prompt: ...")
- parse_args([" "]) → Err("empty prompt: ...")
- parse_args(["", ""]) → Err("empty prompt: ...")
- parse_args(["sttaus"]) → Err("unknown subcommand: ...") [verifies #108 typo guard still takes precedence]
### ROADMAP.md
Added Pinpoint #147 documenting the gap, verification, root cause,
fix shape, and acceptance. Joins the prompt-misdelivery cluster
alongside #145.
## Live verification
$ claw ""
error: empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string
$ claw " "
error: empty prompt: provide a subcommand (run `claw --help`) or a non-empty prompt string
$ claw --output-format json ""
{"error":"empty prompt: provide a subcommand ...","type":"error"}
$ claw prompt "" # unchanged: subcommand-specific error preserved
error: prompt subcommand requires a prompt string
$ claw hello # unchanged: typo guard still fires
error: unknown subcommand: hello.
Did you mean help
$ claw "real prompt here" # unchanged: real prompts still reach API
error: api returned 401 Unauthorized (with dummy key, as expected)
All empty/whitespace-only paths exit 1. No network call. No misleading
credentials error.
## Tests
- rusty-claude-cli bin: 177 tests pass (4 new assertions)
- Full workspace green except pre-existing resume_latest flake (unrelated)
Closes ROADMAP #147.
|
||
|
|
f877acacbf |
feat: #146 — wire claw config and claw diff as standalone subcommands
## Problem `claw config` and `claw diff` are pure-local read-only introspection commands (config merges .claw.json + .claw/settings.json from disk; diff shells out to `git diff --cached` + `git diff`). Neither needs a session context, yet both rejected direct CLI invocation: $ claw config error: `claw config` is a slash command. Use `claw --resume SESSION.jsonl /config` ... $ claw diff error: `claw diff` is a slash command. ... This forced clawing operators to spin up a full session just to inspect static disk state, and broke natural pipelines like `claw config --output-format json | jq`. ## Root cause Sibling of #145: `SlashCommand::Config { section }` and `SlashCommand::Diff` had working renderers (`render_config_report`, `render_config_json`, `render_diff_report`, `render_diff_json_for`) exposed for resume sessions, but the top-level CLI parser in `parse_subcommand()` had no arms for them. Zero-arg `config`/`diff` hit `parse_single_word_command_alias`'s fallback to `bare_slash_command_guidance`, producing the misleading guidance. ## Changes ### rust/crates/rusty-claude-cli/src/main.rs - Added `CliAction::Config { section, output_format }` and `CliAction::Diff { output_format }` variants. - Added `"config"` / `"diff"` arms to the top-level parser in `parse_subcommand()`. `config` accepts an optional section name (env|hooks|model|plugins) matching SlashCommand::Config semantics. `diff` takes no positional args. Both reject extra trailing args with a clear error. - Added `"config" | "diff" => None` to `parse_single_word_command_alias` so bare invocations fall through to the new parser arms instead of the slash-guidance error. - Added dispatch in run() that calls existing renderers: text mode uses `render_config_report` / `render_diff_report`; JSON mode uses `render_config_json` / `render_diff_json_for` with `serde_json::to_string_pretty`. - Added 5 regression assertions in parse_args test covering: parse_args(["config"]), parse_args(["config", "env"]), parse_args(["config", "--output-format", "json"]), parse_args(["diff"]), parse_args(["diff", "--output-format", "json"]). ### ROADMAP.md Added Pinpoint #146 documenting the gap, verification, root cause, fix shape, and acceptance. Explicitly notes which other slash commands (`hooks`, `usage`, `context`, etc.) are NOT candidates because they are session-state-modifying. ## Live verification $ claw config # no config files Config Working directory /private/tmp/cd-146-verify Loaded files 0 Merged keys 0 Discovered files user missing ... project missing ... local missing ... Exit 0. $ claw config --output-format json { "cwd": "...", "files": [...], ... } $ claw diff # no git Diff Result no git repository Detail ... Exit 0. $ claw diff --output-format json # inside claw-code { "kind": "diff", "result": "changes", "staged": "", "unstaged": "diff --git ..." } Exit 0. ## Tests - rusty-claude-cli bin: 177 tests pass (5 new assertions in parse_args) - Full workspace green except pre-existing resume_latest flake (unrelated) ## Not changed `hooks`, `usage`, `context`, `tasks`, `theme`, `voice`, `rename`, `copy`, `color`, `effort`, `branch`, `rewind`, `ide`, `tag`, `output-style`, `add-dir` — all session-mutating or interactive-only; correctly remain slash-only. Closes ROADMAP #146. |
||
|
|
7d63699f9f |
feat: #145 — wire claw plugins subcommand to CLI parser (prompt misdelivery fix)
## Problem `claw plugins` (and `claw plugins list`, `claw plugins --help`, `claw plugins info <name>`, etc.) fell through the top-level subcommand match and got routed into the prompt-execution path. Result: a purely local introspection command triggered an Anthropic API call and surfaced `missing Anthropic credentials` to the user. With valid credentials, it would actually send the literal string "plugins" as a user prompt to Claude, burning tokens for a local query. $ claw plugins error: missing Anthropic credentials; export ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY before calling the Anthropic API $ ANTHROPIC_API_KEY=dummy claw plugins ⠋ 🦀 Thinking... ✘ ❌ Request failed error: api returned 401 Unauthorized Meanwhile siblings (`agents`, `mcp`, `skills`) all worked correctly: $ claw agents No agents found. $ claw mcp MCP Working directory ... Configured servers 0 ## Root cause `CliAction::Plugins` exists, has a working dispatcher (`LiveCli::print_plugins`), and is produced inside the REPL via `SlashCommand::Plugins`. But the top-level CLI parser in `parse_subcommand()` had arms for `agents`, `mcp`, `skills`, `status`, `doctor`, `init`, `export`, `prompt`, etc., and **no arm for `plugins`**. The dispatch never ran from the CLI entry point. ## Changes ### rust/crates/rusty-claude-cli/src/main.rs Added a `"plugins"` arm to the top-level match in `parse_subcommand()` that produces `CliAction::Plugins { action, target, output_format }`, following the same positional convention as `mcp` (`action` = first positional, `target` = second). Rejects >2 positional args with a clear error. Added four regression assertions in the existing `parse_args` test: - `plugins` alone → `CliAction::Plugins { action: None, target: None }` - `plugins list` → action: Some("list"), target: None - `plugins enable <name>` → action: Some("enable"), target: Some(...) - `plugins --output-format json` → action: None, output_format: Json ### ROADMAP.md Added Pinpoint #145 documenting the gap, verification, root cause, fix shape, and acceptance. ## Live verification $ claw plugins # no credentials set Plugins example-bundled v0.1.0 disabled sample-hooks v0.1.0 disabled $ claw plugins --output-format json # no credentials set { "action": "list", "kind": "plugin", "message": "Plugins\n example-bundled ...\n sample-hooks ...", "reload_runtime": false, "target": null } Exit 0 in all modes. No network call. No "missing credentials" error. ## Tests - rusty-claude-cli bin: 177 tests pass (new plugin assertions included) - Full workspace green except pre-existing resume_latest flake (unrelated) Closes ROADMAP #145. |
||
|
|
e2a43fcd49 |
feat: #143 phase 1 — claw status degrades gracefully on malformed config
Previously `claw status` hard-failed on any config parse error, emitting
a bare error string and exiting 1. This took down the entire health
surface for a single malformed MCP entry, even though workspace, git,
model, permission, and sandbox state could all be reported independently.
`claw doctor` already degraded gracefully on the exact same input.
This commit matches `claw status` to that contract.
Changes:
- Add `StatusContext::config_load_error: Option<String>` to capture parse
errors without aborting.
- Rewrite `status_context()` to match on `ConfigLoader::load()`: on Err,
fall back to default `SandboxConfig` for sandbox resolution and record
the parse error, then continue populating workspace/git/memory fields.
- JSON output gains top-level `status: "ok" | "degraded"` marker and a
`config_load_error` string (null on clean runs). All other existing
fields preserved for backward compat.
- Text output prepends a "Config load error" block with Details + Hint
when config failed to parse, then a "Status (degraded)" header on the
main block. Clean runs show the usual "Status" header.
- Doctor path updated to pass the config load error through StatusContext.
Regression test `status_degrades_gracefully_on_malformed_mcp_config_143`:
- Injects a .claw.json with one valid + one malformed mcpServers entry
- Asserts status_context() returns Ok (not Err)
- Asserts config_load_error names the malformed field path
- Asserts workspace/sandbox fields still populated in JSON
- Asserts top-level status is 'degraded'
- Asserts clean config path still returns status: 'ok'
Verified live on /Users/yeongyu/clawd (contains deliberately broken MCP entries):
$ claw status --output-format json
{ "status": "degraded",
"config_load_error": ".../mcpServers.missing-command: missing string field command",
"model": "claude-opus-4-6",
"workspace": {...},
"sandbox": {...},
... }
Phase 2 (typed error object joining #4.44 taxonomy) tracked separately.
Full workspace test green except pre-existing resume_latest flake (unrelated).
Closes ROADMAP #143 phase 1.
|
||
|
|
541c5bb95d |
feat: #139 actionable worker-state guidance in claw state error + help
Previously `claw state` errored with "no worker state file found ... — run a
worker first" but there is no `claw worker` subcommand, so claws had no
discoverable path from the error to a fix.
Changes:
- Rewrite the missing-state error to name the two concrete commands that
produce .claw/worker-state.json:
* `claw` (interactive REPL, writes state on first turn)
* `claw prompt <text>` (one non-interactive turn)
Also tell the user what to rerun: `claw state [--output-format json]`.
- Expand the State --help topic with "Produces state", "Observes state",
and "Exit codes" lines so the worker-state contract is discoverable
before the user hits the error.
- Add regression test state_error_surfaces_actionable_worker_commands_139
asserting the error contains `claw prompt`, REPL mention, and the
rerun path, plus that the help topic documents the producer contract.
Verified live:
$ claw state
error: no worker state file found at .claw/worker-state.json
Hint: worker state is written by the interactive REPL or a non-interactive prompt.
Run: claw # start the REPL (writes state on first turn)
Or: claw prompt <text> # run one non-interactive turn
Then rerun: claw state [--output-format json]
JSON mode preserves the full hint inside the error envelope so CI/claws
can match on `claw prompt` without losing the canonical prefix.
Full workspace test green except pre-existing resume_latest flake (unrelated).
Closes ROADMAP #139.
|
||
|
|
611eed1537 |
feat: #142 structured fields in claw init --output-format json
Previously `claw init --output-format json` emitted a valid JSON envelope but packed the entire human-formatted output into a single `message` string. Claw scripts had to substring-match human language to tell `created` from `skipped`. Changes: - Add InitStatus::json_tag() returning machine-stable "created"|"updated"|"skipped" (unlike label() which includes the human " (already exists)" suffix). - Add InitReport::NEXT_STEP constant so claws can read the next-step hint without grepping the message string. - Add InitReport::artifacts_with_status() to partition artifacts by state. - Add InitReport::artifact_json_entries() for the structured artifacts[] array. - Rewrite run_init + init_json_value to emit first-class fields alongside the legacy message string (kept for text consumers): project_path, created[], updated[], skipped[], artifacts[], next_step, message. - Update the slash-command Init dispatch to use the same structured JSON. - Add regression test artifacts_with_status_partitions_fresh_and_idempotent_runs asserting both fresh + idempotent runs produce the right partitioning and that the machine-stable tag is bare 'skipped' not label()'s phrasing. Verified output: - Fresh dir: created[] has 4 entries, skipped[] empty - Idempotent call: created[] empty, skipped[] has 4 entries - project_path, next_step as first-class keys - message preserved verbatim for backward compat Full workspace test green except pre-existing resume_latest flake (unrelated). Closes ROADMAP #142. |
||
|
|
7763ca3260 |
feat: #141 unify claw <subcommand> --help contract across all 14 subcommands
Previously, `claw <subcommand> --help` had 5 different behaviors: - 7 subcommands returned subcommand-specific help (correct) - init/export/state/version silently fell back to global `claw --help` - system-prompt/dump-manifests errored with `unknown <cmd> option: --help` - bootstrap-plan printed its phase list instead of help text Changes: - Extend LocalHelpTopic enum with Init, State, Export, Version, SystemPrompt, DumpManifests, BootstrapPlan variants. - Extend parse_local_help_action() to resolve those 7 subcommands to their local help topic instead of falling through to the main dispatch. - Remove init/state/export/version from the explicit wants_help=true matcher so they reach parse_local_help_action() before being routed to global help. - Add render_help_topic() entries for the 7 new topics with consistent Usage/Purpose/Output/Formats/Related structure. - Add regression test subcommand_help_flag_has_one_contract_across_all_subcommands_141 asserting every documented subcommand + both --help and -h variants resolve to a HelpTopic with non-empty text that contains a Usage line. Verification: - All 14 subcommands now return subcommand-specific help (live dogfood). - Full workspace test green except pre-existing resume_latest flake. Closes ROADMAP #141. |
||
|
|
27ffd75f03 |
fix: #140 isolate test cwd + env in punctuation_bearing_single_token test
Previously this test inherited the cargo test runner's CWD, which could contain a stale .claw/settings.json with "permissionMode": "acceptEdits" written by another test. The deprecated-field resolver then silently downgraded the default permission mode to WorkspaceWrite, breaking the test's assertion. Fix: wrap the assertion in with_current_dir() + env_lock() so the test runs in an isolated temp directory with no stale config. Full workspace test now passes except for pre-existing resume_latest flake (unrelated to #140, environment-dependent, tracked separately). Closes ROADMAP #140. |
||
|
|
f3f6643fb9 |
feat: #108 add did-you-mean guard for subcommand typos (prevents silent LLM dispatch)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
a8beca1463 |
fix: #136 support --output-format json with --compact flag
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
21adae9570 |
fix: #137 update test fixtures to use canonical 'opus' alias for main branch consistency
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> |
||
|
|
50e3fa3a83 |
docs: add --output-format to diagnostic verb help text
Updated LocalHelpTopic help strings to surface --output-format support: - Status, Sandbox, Doctor, Acp all now show [--output-format <format>] - Added 'Formats: text (default), json' line to each Diagnostic verbs support JSON output but help text didn't advertise it. Post-#127 fix: help text now matches actual CLI surface. Verified: cargo build passes, claw doctor --help shows output-format. Refs: #127 |
||
|
|
a3270db602 |
fix: #127 reject unrecognized suffix args for diagnostic verbs
Diagnostic verbs (help, version, status, sandbox, doctor, state) now reject unrecognized suffix arguments at parse time instead of silently falling through to Prompt dispatch. Fixes: claw doctor --json (and similar) no longer accepts --json silently and attempts to send it to the LLM as a prompt. Now properly emits: 'unrecognized argument `--json` for subcommand `doctor`' Joined parser-level trust gap quintet #108 + #117 + #119 + #122 + #127. Prevents token burn on rejected arguments. Verified: cargo build --workspace passes, claw doctor --json errors cleanly. Refs: #127, ROADMAP |
||
|
|
2678fa0af5 |
fix: #124 --model validation rejects malformed syntax at parse time
Adds validate_model_syntax() that rejects: - Empty strings - Strings with spaces (e.g., 'bad model') - Invalid provider/model format Accepts: - Known aliases (opus, sonnet, haiku) - Valid provider/model format (provider/model) Wired into parse_args for both --model <value> and --model=<value> forms. Errors exit with clear message before any API calls (no token burn). Verified: - 'claw --model "bad model" version' → error, exit 1 - 'claw --model "" version' → error, exit 1 - 'claw --model opus version' → works - 'claw --model anthropic/claude-opus-4-6 version' → works Refs: ROADMAP #124 (debbcbe cluster — parser-level trust gap family) |
||
|
|
b9990bb27c |
fix: #122 + #125 doctor consistency and git_state clarity
#122: doctor invocation now checks stale-base condition - Calls run_stale_base_preflight(None) in render_doctor_report() - Emits stale-base warnings to stderr when branch is behind main - Fixes inconsistency: doctor 'ok' vs prompt 'stale base' warning #125: git_state field reflects non-git directories - When !in_git_repo, git_state = 'not in git repo' instead of 'clean' - Fixes contradiction: in_git_repo: false but git_state: 'clean' - Applied in both doctor text output and status JSON Verified: cargo build --workspace passes. Refs: ROADMAP #122 (dd73962), #125 (debbcbe) |
||
|
|
f33c315c93 |
fix: #122 doctor invocation now checks stale-base condition
Adds run_stale_base_preflight(None) call to render_doctor_report() so that claw doctor emits stale-base warnings to stderr when the current branch is behind main. Previously doctor reported 'ok' even when branch was stale, creating inconsistency with prompt path warnings. Fixes silent-state inventory gap: doctor now consistent with prompt/repl stale-base checking. No behavior change for non-stale branches. Verified: cargo build --workspace passes, no test failures. Ref: ROADMAP #122 dogfood filing @ dd73962 |
||
|
|
c956f78e8a |
ROADMAP #4.44.5: Ship/provenance opacity — filed from dogfood
Added structured delivery-path contract to surface branch → merge → main-push provenance as first-class events. Filed from the 56-commit 2026-04-20 push that exposed the gap. Also fixes: ApiError test compilation — add suggested_action: None to 4 sites - Line ~8414: opaque_provider_wrapper_surfaces_failure_class_session_and_trace - Line ~8436: retry_exhaustion_uses_retry_failure_class_for_generic_provider_wrapper - Line ~8499: provider_context_window_errors_are_reframed_with_same_guidance - Line ~8533: retry_wrapped_context_window_errors_keep_recovery_guidance |
||
|
|
ac45bbec15 |
Make ACP/Zed status obvious before users go source-diving
ROADMAP #21, #22, and #23 were already closed on current main, so the next real repo-local backlog item was the ACP/Zed discoverability gap. This adds a local `claw acp` status surface plus aliases, updates help/docs, and separates the shipped discoverability fix from the still-open daemon/protocol follow-up so editor-first users get a crisp answer immediately. Constraint: No ACP/Zed daemon or protocol server exists in claw-code yet, so the new surface must be explicit status guidance rather than a fake implementation Rejected: Add a pretend `acp serve` daemon path | would imply supported protocol behavior that does not exist Rejected: Docs-only clarification | still leaves `claw --help` unable to answer the editor-launch question directly Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep ROADMAP discoverability fixes separate from future ACP daemon/protocol work so help text and backlog IDs stay unambiguous Tested: cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; cargo run -q -p rusty-claude-cli -- acp; cargo run -q -p rusty-claude-cli -- --output-format json acp; architect review APPROVED Not-tested: Real ACP/Zed daemon launch because no protocol-serving surface exists yet |
||
|
|
42bb6cdba6 |
Keep local clawhip artifacts from tripping routine repo work
Dogfooding kept reproducing OMX team merge conflicts on `.clawhip/state/prompt-submit.json`, so the init bootstrap now teaches repos to ignore `.clawhip/` alongside the existing local `.claw/` artifacts. This also updates the current repo ignore list so the fix helps immediately instead of only on future `claw init` runs. Constraint: Keep the fix narrow and centered on repo-local ignore hygiene Rejected: Broader team merge-hygiene changes | unnecessary for the proven local root cause Confidence: high Scope-risk: narrow Reversibility: clean Directive: If more runtime-local artifact directories appear, extend the shared init gitignore list instead of patching repos ad hoc Tested: cargo fmt --all --check Tested: cargo clippy --workspace --all-targets -- -D warnings Tested: cargo test --workspace Tested: Architect review (APPROVE) Not-tested: Existing clones with already-tracked `.clawhip` files still need manual cleanup Related: ROADMAP #75 |
||
|
|
f91d156f85 |
Keep poisoned test locks from cascading across unrelated regressions
The repo-local backlog was effectively exhausted, so this sweep promoted the newly observed test-lock poisoning pain point into ROADMAP #74 and fixed it in place. Test-only env/cwd lock acquisition now recovers poisoned mutexes in the remaining strict call sites, and each affected surface has a regression that proves a panic no longer permanently poisons later tests. Constraint: Keep the fix test-only and avoid widening runtime behavior changes Rejected: Refactor shared helper signatures across broader call paths | unnecessary churn beyond the remaining strict test sites Confidence: high Scope-risk: narrow Reversibility: clean Directive: These guards only recover the mutex; tests that mutate env or cwd still must restore process-global state explicitly Tested: cargo fmt --all --check Tested: cargo clippy --workspace --all-targets -- -D warnings Tested: cargo test --workspace Tested: Architect review (APPROVE) Not-tested: Additional fault-injection around partially restored env/cwd state after panic Related: ROADMAP #74 |
||
|
|
2e34949507 |
Keep latest-session timestamps increasing under tight loops
The next repo-local sweep target was ROADMAP #73: repeated backlog sweeps exposed that session writes could share the same wall-clock millisecond, which made semantic recency fragile and forced the resume-latest regression to sleep between saves. The fix makes session timestamps monotonic within the process and removes the timing hack from the test so latest-session selection stays stable under tight loops. Constraint: Preserve the existing session file format while changing only the timestamp source semantics Rejected: Keep the sleep-based test workaround | hides the real ordering hazard instead of fixing timestamp generation Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future session-recency logic must keep `current_time_millis`, ordering tests, and latest-session expectations aligned Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Cross-process monotonicity when multiple binaries write sessions concurrently |
||
|
|
8f53524bd3 |
Make backlog-scan lanes say what they actually selected
The next repo-local sweep target was ROADMAP #65: backlog-scanning lanes could stop with prose-only summaries naming roadmap items, but there was no machine-readable record of which items were chosen, which were skipped, or whether the lane intended to execute, review, or no-op. The fix teaches completed lane persistence to extract a structured selection outcome while preserving the existing quality- floor and review-verdict behavior for other lanes. Constraint: Keep selection-outcome extraction on the existing `lane.finished` metadata path instead of inventing a separate event stream Rejected: Add a dedicated selection event type first | unnecessary for this focused closeout because `lane.finished` already persists structured data downstream can read Confidence: high Scope-risk: narrow Reversibility: clean Directive: If backlog-scan summary conventions change later, update `extract_selection_outcome`, its regression test, and the ROADMAP closeout wording together Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE after roadmap closeout update Not-tested: Downstream consumers that may still ignore `lane.finished.data.selectionOutcome` |
||
|
|
dbc2824a3e |
Keep latest session selection tied to real session recency
The next repo-local sweep target was ROADMAP #72: the `latest` managed-session alias could depend on filesystem mtime before the session's own persisted recency markers, which made the selection path vulnerable to coarse or misleading file timestamps. The fix promotes `updated_at_ms` into the summary/order path, keeps CLI wrappers in sync, and locks the mtime-vs-session-recency case with regression coverage. Constraint: Preserve existing managed-session storage layout while changing only the ordering signal Rejected: Keep sorting by filesystem mtime and just sleep longer in tests | hides the semantic ordering bug instead of fixing it Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future managed-session ordering change must keep runtime and CLI summary structs aligned on the same recency fields Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Cross-filesystem behavior where persisted session JSON cannot be read and fallback ordering uses mtime only |
||
|
|
3b806702e7 |
Make the CLI point users at the real install source
The next repo-local backlog item was ROADMAP #70: users could mistake third-party pages or the deprecated `cargo install claw-code` path for the official install route. The CLI now surfaces the source of truth directly in `claw doctor` and `claw --help`, and the roadmap closeout records the change. Constraint: Keep the fix inside repo-local Rust CLI surfaces instead of relying on docs alone Rejected: Close #70 with README-only wording | the bug was user-facing CLI ambiguity, so the warning needed to appear in runtime help/doctor output Confidence: high Scope-risk: narrow Reversibility: clean Directive: If install guidance changes later, update both the doctor check payload and the help-text warning together Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Third-party websites outside this repo that may still present stale install instructions |
||
|
|
4f83a81cf6 |
Make dump-manifests recoverable outside the inferred build tree
The backlog sweep found that the user-cited #21-#23 items were already closed, and the next real pain point was `claw dump-manifests` failing without a direct way to point at the upstream manifest source. This adds an explicit `--manifests-dir` path, upgrades the failure messages to say whether the source root or required files are missing, and updates the ROADMAP closeout to reflect that #45 is now fixed. Constraint: Preserve existing dump-manifests behavior when no explicit override is supplied Rejected: Require CLAUDE_CODE_UPSTREAM for every invocation | breaks existing build-tree workflows and is unnecessarily rigid Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep manifest-source override guidance centralized so future error-path edits do not drift Tested: cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Manual invocation against every legacy env-based manifest lookup layout |
||
|
|
264fdc214e |
Retire the stale bare-skill dispatch backlog item
ROADMAP #36 remained open even though current main already dispatches bare skill names in the REPL through skill resolution instead of forwarding them to the model. This change adds a direct regression test for that behavior and marks the backlog item done with fresh verification evidence. Constraint: User required fresh cargo fmt, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace before closeout Rejected: Leave #36 open because the implementation already existed | keeps the immediate backlog inaccurate and invites duplicate work Confidence: high Scope-risk: narrow Reversibility: clean Directive: Reopen #36 only with a fresh repro showing a listed project skill still falls through to plain prompt handling on current main Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: No interactive manual REPL session beyond the new bare-skill unit coverage |
||
|
|
a7b1fef176 |
Keep the rebased workspace green after the backlog closeout
The ROADMAP #38 closeout was rebased onto a moving main branch. That pulled in new workspace files whose clippy/rustfmt fixes were required for the exact verification gate the user asked for. This follow-up records those remaining cleanups so the pushed branch matches the green tree that was actually tested. Constraint: The user-required full-workspace fmt/clippy/test sequence had to stay green after rebasing onto newer origin/main Rejected: Leave the rebase cleanup uncommitted locally | working tree would stay dirty and the pushed branch would not match the verified code Confidence: high Scope-risk: narrow Reversibility: clean Directive: When rebasing onto a moving main, commit any gate-fixing follow-up so pushed history matches the verified tree Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: No additional behavior beyond the already-green verification sweep |
||
|
|
16b9febdae |
feat: ultraclaw droid batch — ROADMAP #41 test isolation + #50 PowerShell permissions
Merged late-arriving droid output from 10 parallel ultraclaw sessions. ROADMAP #41 — Test isolation for plugin regression checks: - Add test_isolation.rs module with env_lock() for test environment isolation - Redirect HOME/XDG_CONFIG_HOME/XDG_DATA_HOME to unique temp dirs per test - Prevent host ~/.claude/plugins/ from bleeding into test runs - Auto-cleanup temp directories on drop via RAII pattern - Tests: 39 plugin tests passing ROADMAP #50 — PowerShell workspace-aware permissions: - Add is_safe_powershell_command() for command-level permission analysis - Add is_path_within_workspace() for workspace boundary validation - Classify read-only vs write-requiring bash commands (60+ commands) - Dynamic permission requirements based on command type and target path - Tests: permission enforcer and workspace boundary tests passing Additional improvements: - runtime/src/permission_enforcer.rs: Dynamic permission enforcement layer - check_with_required_mode() for dynamically-determined permissions - 60+ read-only command patterns (cat, find, grep, cargo, git, jq, yq, etc.) - Workspace-path detection for safe commands - compat-harness/src/lib.rs: Compat harness updates for permission testing - rusty-claude-cli/src/main.rs: CLI integration for permission modes - plugins/src/lib.rs: Updated imports for test isolation module Total: +410 lines across 5 files Workspace tests: 448+ passed Droid source: ultraclaw-04-test-isolation, ultraclaw-08-powershell-permissions Ultraclaw total: 4 ROADMAP items committed (38, 40, 41, 50) |
||
|
|
124e8661ed |
Remove the deprecated Claude subscription login path and restore a green Rust workspace
ROADMAP #37 was still open even though several earlier backlog items were already closed. This change removes the local login/logout surface, stops startup auth resolution from treating saved OAuth credentials as a supported path, and updates diagnostics/help to point users at ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN only. While proving the change with the user-requested workspace gates, clippy surfaced additional pre-existing warning failures across the Rust workspace. Those were cleaned up in-place so the required `cargo fmt`, `cargo clippy --workspace --all-targets -- -D warnings`, and `cargo test --workspace` sequence now passes end to end. Constraint: User explicitly required full-workspace fmt/clippy/test before commit/push Constraint: Existing dirty leader worktree had to be stashed before attempted OMX team worktree launch Rejected: Keep login/logout but hide them from help | left unsupported auth flow and saved OAuth fallback intact Rejected: Stop after ROADMAP #37 targeted tests | did not satisfy required full-workspace verification gate Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Do not reintroduce saved OAuth as a silent Anthropic startup fallback without an explicit supported auth policy Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: Remote push effects beyond origin/main update |
||
|
|
61c01ff7da |
Prevent cross-worktree session bleed during managed session resume/load
ROADMAP #41 was still leaving a phantom-completion class open: managed sessions could be resumed from the wrong workspace, and the CLI/runtime paths were split between partially isolated storage and older helper flows. This squashes the verified team work into one deliverable that routes managed session operations through the per-worktree SessionStore, rejects workspace mismatches explicitly, extends lane-event taxonomy for workspace mismatch reporting, and updates the affected CLI regression fixtures/docs so the new contract is enforced without losing same- workspace legacy coverage. Constraint: Keep same-workspace legacy flat sessions readable while blocking cross-worktree misuse Constraint: No new dependencies; stay within the ROADMAP #41 changed-file scope Rejected: Leave team auto-checkpoint history as final branch state | noisy/non-lore history for a single roadmap fix Confidence: high Scope-risk: moderate Reversibility: clean Directive: Preserve workspace_root validation on future resume/load helpers; do not reintroduce path-only fallback without equivalent mismatch checks Tested: cargo test -p runtime session_control -- --nocapture; cargo test -p rusty-claude-cli resume -- --nocapture; cargo test -p rusty-claude-cli --test cli_flags_and_config_defaults; cargo test -p rusty-claude-cli --test output_format_contract; cargo test -p rusty-claude-cli --test resume_slash_commands; cargo test --workspace --exclude compat-harness; cargo check --workspace --all-targets; git diff --check Not-tested: cargo clippy --workspace --all-targets -- -D warnings (pre-existing failures in unchanged rust/crates/rusty-claude-cli/build.rs) Related: ROADMAP #41 |
||
|
|
810036bf09 |
test(cli): add integration test for model persistence in resumed /status
New test: resumed_status_surfaces_persisted_model - Creates session with model='claude-sonnet-4-6' - Resumes with --output-format json /status - Asserts model round-trips through session metadata Resume integration tests: 11 → 12. |