Commit 933e4cc
fix(audit r9): /cd whitespace, allow-always placeholder safety, prompts rebuild, MCP hardening (#68)
TDD-driven fixes for the verified real bugs from audit round 9.
## Round A — verified real bugs (CRITICAL/HIGH)
### `/cd /tmp` no longer cds to home
`splitn(3, ' ')` produces empty middle elements for consecutive
spaces — `/cd /tmp` parsed as `["/cd", "", "/tmp"]`, and the
empty `parts[1]` collapsed to "cd to home". Now derives the
target by stripping the `/cd` prefix and trimming, so any
amount of whitespace between the command and the path resolves
correctly.
### Empty-input "allow always" no longer pins literal placeholder
PR #67 made `suggest_pattern("bash", "")` return the literal
`"<edit this pattern>"` placeholder so an accidental "(a) allow
always" wouldn't pin a catch-all `"* *"`. But the ask-dialog
fed that placeholder straight into `UserDecision::AllowAlways`,
storing the literal text as a real pattern in
`permission_allowlist`. The dialog now detects placeholders via
the new `is_placeholder_pattern` predicate and falls back to
`AllowOnce` with a dim "can't derive a useful pattern from empty
input; allowing once only" message.
### `/regen-prompts` rebuilds the agent
Regenerating overwrote the on-disk prompt content + reloaded
`context.prompts`, but the LIVE agent kept the old preamble in
memory. Users had to `/prompt <name>` to actually see the new
content. Now also re-binds `context.current_prompt` to the
freshly-loaded body for the currently-active name and rebuilds
the agent so the new system prompt takes effect immediately.
### `/toggle` added to `/help`
Slipped through PR #54 — the feature exists and is in the
README but the in-app `/help` text didn't list it.
### Anthropic `overloaded_error` classified as RateLimit
The error classifier matched "rate limit" / "too many requests"
/ "429" but not Anthropic's `overloaded_error` (structurally a
rate-limit signal). Falls through to `Other` and no retry
fires — user saw a one-shot failure on transient backend
pressure. Now any error string containing "overloaded" routes
to `ErrorKind::RateLimit` and triggers the exponential-backoff
retry.
## Round B — MCP hardening
### MCP server init timeout (10s)
`serve_client((), transport).await` had no upper bound. A
command-based MCP server that hung on `initialize` (waiting for
stdin / wedged binary) would pin dirge's startup indefinitely.
Now wrapped in `tokio::time::timeout(MCP_INIT_TIMEOUT)`; past
10s we abort, log the failure, and continue with the other
servers.
### Empty `EXA_API_KEY` skips Exa default registration
A user with `EXA_API_KEY=""` (explicit empty, e.g. from a
`.envrc` that intentionally clears it) used to register the
Exa server anyway, then every web-search call failed with 401
at first use. Now treats empty key the same as unset — Exa
default skipped, no broken server in the list.
### MCP tool `inputSchema` null fallback to `{}`
Servers that omit `inputSchema` had it serialized as JSON
`null`, which rig's tool registration treats as an invalid
parameters block. The tool became unusable. Now substitute an
empty object: the tool stays callable; the LLM sees "no params"
correctly.
## Tests
3 new tests, written failing first:
- `classify_anthropic_overloaded_error_as_retryable` (2 cases)
- `placeholder_pattern_is_detectable`
(`/cd` whitespace fix is observable in the existing /cd tests if
present; verified by manual trace through the parsing path.)
Total: 635 pass (was 633), 0 fail across all build profiles
(`--features plugin`, `--all-features`, `--no-default-features`).
## Deferred from this audit (bigger scope)
- **Compression prunes sibling branches** — needs a tree-walk
during `compress` to preserve sibling subtrees whose parents
get dropped. Real bug, real risk on branched sessions, but
the fix touches the compress data flow significantly.
- **Subagent permission/sandbox/hooks** — `task` tool's
`btw_query` is a bare LLM call with no permission/sandbox
inheritance and no plugin hook dispatch. By design today, but
the safety properties are not what users expect.
- **`convert_history` loses tool call structure** — pre-existing
finding; resuming a session shows the LLM text-only traces of
prior tool calls, losing the structured tool_use markers.
- **README "events are buffered" claim** — implementation streams
live; doc says buffered. Tokens are NOT re-emitted on retry
but the user already saw the partial.
- **Multi-plugin harness-block last-write-wins** — design
question (queue vs. first-wins) deferred for discussion.
- **`/clear` no confirmation** — design choice; standard for CLI.
- **`/quit` save-before-break** — needs verification; the outer
loop may already save on Interrupted.
## Test plan
- [x] `cargo test --features plugin` -> 635 pass, 0 fail.
- [x] `cargo build --all-features` -> compiles, no warnings.
- [x] `cargo build --no-default-features` -> compiles.
Co-authored-by: Yogthos <yogthos@gmail.com>1 parent 88d255d commit 933e4cc
6 files changed
Lines changed: 181 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
92 | 102 | | |
93 | 103 | | |
94 | 104 | | |
| |||
268 | 278 | | |
269 | 279 | | |
270 | 280 | | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
271 | 301 | | |
272 | 302 | | |
273 | 303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
205 | | - | |
206 | | - | |
207 | | - | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
208 | 229 | | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
218 | 230 | | |
219 | 231 | | |
220 | 232 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
14 | 22 | | |
15 | 23 | | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
16 | 38 | | |
17 | 39 | | |
18 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
47 | 55 | | |
48 | 56 | | |
49 | 57 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2340 | 2340 | | |
2341 | 2341 | | |
2342 | 2342 | | |
| 2343 | + | |
| 2344 | + | |
| 2345 | + | |
| 2346 | + | |
| 2347 | + | |
| 2348 | + | |
| 2349 | + | |
| 2350 | + | |
| 2351 | + | |
| 2352 | + | |
| 2353 | + | |
| 2354 | + | |
| 2355 | + | |
| 2356 | + | |
| 2357 | + | |
| 2358 | + | |
| 2359 | + | |
| 2360 | + | |
2343 | 2361 | | |
2344 | 2362 | | |
2345 | 2363 | | |
| |||
2913 | 2931 | | |
2914 | 2932 | | |
2915 | 2933 | | |
| 2934 | + | |
| 2935 | + | |
| 2936 | + | |
| 2937 | + | |
| 2938 | + | |
| 2939 | + | |
| 2940 | + | |
| 2941 | + | |
| 2942 | + | |
| 2943 | + | |
| 2944 | + | |
2916 | 2945 | | |
2917 | 2946 | | |
2918 | 2947 | | |
2919 | 2948 | | |
2920 | 2949 | | |
2921 | 2950 | | |
2922 | 2951 | | |
2923 | | - | |
| 2952 | + | |
2924 | 2953 | | |
2925 | 2954 | | |
2926 | 2955 | | |
| |||
3345 | 3374 | | |
3346 | 3375 | | |
3347 | 3376 | | |
| 3377 | + | |
| 3378 | + | |
| 3379 | + | |
| 3380 | + | |
| 3381 | + | |
| 3382 | + | |
| 3383 | + | |
| 3384 | + | |
| 3385 | + | |
| 3386 | + | |
| 3387 | + | |
| 3388 | + | |
| 3389 | + | |
| 3390 | + | |
| 3391 | + | |
| 3392 | + | |
| 3393 | + | |
| 3394 | + | |
| 3395 | + | |
| 3396 | + | |
| 3397 | + | |
3348 | 3398 | | |
3349 | 3399 | | |
3350 | 3400 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
807 | 807 | | |
808 | 808 | | |
809 | 809 | | |
810 | | - | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
811 | 844 | | |
812 | 845 | | |
813 | 846 | | |
| |||
986 | 1019 | | |
987 | 1020 | | |
988 | 1021 | | |
989 | | - | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
990 | 1029 | | |
991 | 1030 | | |
992 | 1031 | | |
| |||
1161 | 1200 | | |
1162 | 1201 | | |
1163 | 1202 | | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
1164 | 1207 | | |
1165 | 1208 | | |
1166 | 1209 | | |
| |||
0 commit comments