Commit 79555dc
fix(audit r3): /retry handles system msgs, table breaks, apply_patch path-tool, unique save nonce, ragged tables (#57)
Round of follow-up fixes after auditing the recent PRs (granular
selection, markdown tables, CRLF apply_patch, atomic save, bold
streaming).
## CRITICAL fixes
### /retry now handles system messages
The original /retry used `undo_last` which only pops Assistant/User
pairs. If a system message (compress summary, error note) landed
between the user prompt and the retry trigger, it stayed in the
session and the agent saw it as context. Replaced with a tight
loop that pops messages until the last user message has been
removed too. Works regardless of how many system/assistant
messages were sandwiched between.
### Markdown table cells handle SoftBreak / HardBreak
`Event::SoftBreak` and `Event::HardBreak` pushed `\n` into `acc`
unconditionally. When inside a table cell, this smeared the cell
across multiple lines and broke alignment. Now substitutes a
single space for the break when `in_table`, so the cell's visible
content stays on one row.
### apply_patch / lsp now in `is_path_tool_name`
The tool-rule map registered `apply_patch` and `lsp`, but the
external-directory consultation check used a narrower whitelist
(`read | write | edit | list_dir`). When a config had
`external_directory: { "/tmp/**": "deny" }` and an agent ran
`apply_patch` against `/tmp/...`, the rule was silently ignored.
Both `is_path_tool_name` and the inner `is_path_tool` now use
the same canonical list (DRY: inner calls the public predicate).
## HIGH fix
### Atomic save tmp filename includes per-call nonce
Tmp file was `dir/.{id}.json.tmp` — deterministic. Two concurrent
saves of the same session id (rare, but possible across processes
or via plugin tree-ops) raced on the same tmp file. Now the tmp
name is `dir/.{id}.{pid}-{ns}.json.tmp` so each save writes its
own tmp. Rename collisions remain harmless (last writer wins on
the target; each tmp was complete before rename).
## LOW fix
### Ragged table rows get a minimum column width
When `ncols` was the max across header + rows but some body row
had fewer cells, the missing columns kept `widths[i] = 0`, leaving
the separator line wider than the cells. Each column now gets a
minimum width of 1 char so the right border aligns.
## Skipped (false positives from the audit)
- Bold "leak past colored span" — `ResetColor` does reset attributes
on the terminals we care about; `NormalIntensity` is belt-and-braces.
No actual leak in practice.
- Multi-byte UTF-8 grapheme cluster slicing — Rust's `chars()`
iterator guarantees char-boundary safety; the code is correct.
- `buffer_pos_at` clamp comment — accurate; just terminology nitpick.
- Auto-compact alert "may scroll out of view" — the framed alert
already stops the eye; further hardening would require a modal
overlay (bigger UX change).
## Test plan
- [x] `cargo test --features plugin` -> 612 pass, 0 fail.
- [x] Both build profiles -> 0 warnings.
Co-authored-by: Yogthos <yogthos@gmail.com>1 parent 2559208 commit 79555dc
6 files changed
Lines changed: 92 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
99 | 114 | | |
100 | 115 | | |
101 | 116 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
33 | 36 | | |
34 | 37 | | |
35 | 38 | | |
| |||
291 | 294 | | |
292 | 295 | | |
293 | 296 | | |
294 | | - | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
295 | 303 | | |
296 | 304 | | |
297 | 305 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
49 | 53 | | |
50 | 54 | | |
| 55 | + | |
| 56 | + | |
51 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
52 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
53 | 69 | | |
| 70 | + | |
54 | 71 | | |
55 | 72 | | |
56 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
66 | 80 | | |
67 | 81 | | |
68 | 82 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
97 | 104 | | |
98 | 105 | | |
99 | 106 | | |
| |||
404 | 411 | | |
405 | 412 | | |
406 | 413 | | |
407 | | - | |
408 | | - | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
409 | 421 | | |
410 | 422 | | |
411 | 423 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1050 | 1050 | | |
1051 | 1051 | | |
1052 | 1052 | | |
1053 | | - | |
1054 | | - | |
1055 | | - | |
1056 | | - | |
1057 | | - | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
1058 | 1068 | | |
1059 | 1069 | | |
1060 | 1070 | | |
| |||
0 commit comments