diff --git a/ROADMAP.md b/ROADMAP.md index 700b0855..3f1072a1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -6585,3 +6585,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed) 514. **`write_file` silently overwrites existing binary/non-UTF8 files while reporting them as creates because the previous-file read uses `fs::read_to_string(...).ok()` and drops decode/errors** — dogfooded 2026-05-20 from the `#clawcode-building-in-public` 22:30 UTC nudge on `/home/bellman/Workspace/claw-code-pr2967` with branch/origin `docs/roadmap-workdir-provenance@ea29fbe`. Code inspection: `runtime/src/file_ops.rs::write_file` enforces only the new content byte length, resolves the destination, then does `let original_file = fs::read_to_string(&absolute_path).ok();`. Any read error — file missing, permission denied, directory race, or existing binary/non-UTF8 content — is collapsed to `None`. The function then creates parents and `fs::write(&absolute_path, content)?`. Because `kind` is computed as `if original_file.is_some() { "update" } else { "create" }`, overwriting a binary file is reported as a create with no warning and no binary guard. `read_file` has binary detection; `write_file` does not apply it before clobbering existing files. **Required fix shape:** (a) distinguish `NotFound` from other read/decode errors instead of using `.ok()`; (b) if the destination exists and is not valid UTF-8 or appears binary, require an explicit `overwrite_binary:true` / `force:true` flag or return `kind:"binary_overwrite_refused"`; (c) report `existed:true` based on metadata, not successful UTF-8 decoding; (d) preserve structured diff only for text files; (e) add regressions for overwriting a binary file and a permission-denied/unreadable file proving the tool does not silently treat them as creates. **Why this matters:** write tools are allowed to create/update source artifacts, but silently clobbering a binary asset or unreadable file is data-lossy and misleading. The model/operator needs to know whether a file existed and whether a safe text diff is possible before replacement. Source: gaebal-gajae dogfood response to Clawhip message `1506786124176293919` on 2026-05-20. 515. **Config `model` values are parsed with `JsonValue::as_str` and otherwise silently ignored, so non-string model config falls back to defaults with no error or warning** — dogfooded 2026-05-20 from the `#clawcode-building-in-public` 23:00 UTC nudge on `/home/bellman/Workspace/claw-code-pr2967` with branch/origin `docs/roadmap-workdir-provenance@07a12d4`, following Jobdori's config-loader diagnostic sweep. Code inspection: after validation/merge, `ConfigLoader::load` builds `RuntimeFeatureConfig { model: parse_optional_model(&merged_value), ... }`. `parse_optional_model` is `root.as_object().and_then(|object| object.get("model")).and_then(JsonValue::as_str).map(ToOwned::to_owned)`. If a config file contains `{"model": 123}`, `{"model": null}`, `{"model": ["opus"]}`, or an object, `parse_optional_model` returns `None` exactly as if no model key existed. Other config fields use typed helpers that return `ConfigError::Parse` on wrong types (`permissions.defaultMode`, plugin fields, hooks, etc.), so `model` is an outlier. **Required fix shape:** (a) replace `parse_optional_model -> Option` with `Result, ConfigError>`; (b) when `model` exists but is not a non-empty string, emit `ConfigError::Parse` or a structured warning with path/key/type; (c) run the same model-syntax validator used for `--model` and env model values so config, env, and CLI flag agree; (d) expose `model_source`, `model_raw`, and validation diagnostics in status/config JSON; (e) add regressions for numeric/null/array/object model values proving they are not silently treated as missing. **Why this matters:** model selection is control-plane state. A typo like `model: ["opus"]` should not silently revert to the default model while status appears healthy; that creates prompt misdelivery and cost surprises that are hard to attribute back to config. Source: gaebal-gajae dogfood response to Clawhip message `1506793682257580144` on 2026-05-20. + +516. **`config env --output-format json` prints raw environment secret values from config files, including API-key-shaped entries, instead of redacting sensitive keys** — dogfooded 2026-05-20 from the `#clawcode-building-in-public` 23:30 UTC nudge on `/home/bellman/Workspace/claw-code-pr2967` with branch/origin `docs/roadmap-workdir-provenance@fbd2f01` and binary `./rust/target/debug/claw` built from source SHA `25d663d`. Reproduction in a clean temp workspace: create `.claw.json` with `{"env":{"ANTHROPIC_API_KEY":"sk-SECRET-should-not-print","SAFE":"ok"}}`, then run `claw config env --output-format json`. The command exits 0 and emits `section_value` containing the raw `ANTHROPIC_API_KEY` value unchanged while also showing benign keys like `SAFE`. This makes the config-inspection surface unsafe to paste into issue reports, Discord, CI logs, or support threads. **Required fix shape:** (a) redact values for sensitive key patterns (`*_API_KEY`, `*_TOKEN`, `*_SECRET`, `PASSWORD`, `AUTH`, etc.) in both JSON and text config inspection output; (b) preserve enough metadata for debugging (`redacted:true`, maybe `value_present:true`, source file, key name) without exposing bytes; (c) keep non-sensitive env values visible; (d) add a `--show-secrets`/`--unsafe-show-secrets` escape hatch only if explicitly confirmed and never enabled by default; (e) add regression coverage for `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, token/password variants, and safe keys. **Why this matters:** `config env` is exactly the surface operators use when debugging configuration. If it dumps credentials by default, the first support/debug paste can leak provider keys. Automation-friendly JSON must be safer than prose, not a secret exfiltration footgun. Source: gaebal-gajae dogfood response to Clawhip message `1506801223465046210` on 2026-05-20.