1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
# mojibake — Traceability Matrix
Software Level: **DAL-C** (DO-178C)
Bidirectional trace: **HLR → LLR → source → test**. Source items carry a
`// MJB-LLR-nnn` comment; tests are named `mjb_llr_nnn_<description>` so this
matrix can be checked mechanically:
```bash
# every LLR tagged in source
grep -rho 'MJB-LLR-[0-9]\+' src/ | sort -u
# every LLR exercised by a test
grep -rho 'mjb_llr_[0-9]\+' src/ tests/ | sort -u
```
Paths are relative to the repository root.
---
## HLR → LLR
| HLR | Subject | LLRs |
|---|---|---|
| MJB-HLR-001 | File load from command line | 111, 112 |
| MJB-HLR-002 | Encoding and BOM | 110, 111, 113, 115, 118 |
| MJB-HLR-003 | Line ending preservation | 114, 115 |
| MJB-HLR-004 | Rope text storage | 090–092, 117 |
| MJB-HLR-005 | Selection model | 001–011, 020–025 |
| MJB-HLR-006 | Character and line motion | 062, 063, 064 |
| MJB-HLR-007 | Word motions select | 060, 061, 065–069 |
| MJB-HLR-008 | Goto commands | 070–073, 150–154 |
| MJB-HLR-009 | Insert-mode entry | 049, 117 |
| MJB-HLR-010 | Text modification | 040–050, 117 |
| MJB-HLR-011 | Undo and redo | 045, 046, 051–053 |
| MJB-HLR-012 | Viewport pagination | 090–092, 200 |
| MJB-HLR-013 | Scrolling and paging | 093–102 |
| MJB-HLR-014 | TOML configuration | 180, 181 |
| MJB-HLR-015 | Config-driven modal keymap | 150–157, 182, 184 |
| MJB-HLR-016 | Command mode | 158, 159, 160 |
| MJB-HLR-017 | File write | 130–137 |
| MJB-HLR-018 | Error handling | 042, 044, 118, 183 |
| MJB-HLR-019 | Single-widget presentation | 200–205 |
---
## LLR → source → test
### Selection (`src/buffer/selection.rs`)
| LLR | Source item | Test |
|---|---|---|
| 001 | `Range` | `mjb_llr_001_offsets_are_byte_indices` |
| 002 | `Range::from`, `Range::to` | `mjb_llr_002_from_and_to_ignore_direction` |
| 003 | `Range::is_empty` | `mjb_llr_003_is_empty` |
| 004 | `Range::direction` | `mjb_llr_004_direction` |
| 005 | `Range::cursor` | `mjb_llr_005_cursor_steps_back_on_forward_range`, `mjb_llr_005_cursor_respects_grapheme_clusters` |
| 006 | `Range::put_cursor` | `mjb_llr_006_put_cursor_without_extend_collapses` |
| 007 | `Range::put_cursor` | `mjb_llr_007_put_cursor_with_extend_keeps_anchor`, `mjb_llr_007_put_cursor_extend_flips_direction`, `mjb_llr_007_select_mode_motions_extend` |
| 008 | `Range::line_range` | `mjb_llr_008_line_range` |
| 009 | `Selection` | `mjb_llr_009_selection_invariant` |
| 010 | `Selection::primary` | `mjb_llr_010_primary_round_trips` |
| 011 | `Range::clamped` | `mjb_llr_011_clamped_snaps_into_bounds_and_onto_char_boundary`, `mjb_llr_011_motions_at_boundaries_never_leave_the_buffer` |
### Graphemes (`src/buffer/grapheme.rs`)
| LLR | Source item | Test |
|---|---|---|
| 020 | `prev_grapheme_boundary` | `mjb_llr_020_prev_boundary_saturates_at_zero`, `mjb_llr_020_combining_mark_is_one_cluster` |
| 021 | `next_grapheme_boundary` | `mjb_llr_021_next_boundary_saturates_at_end`, `mjb_llr_021_multibyte_advances_whole_char` |
| 022 | chunk-walking loops in both | `mjb_llr_022_boundaries_resolve_across_chunk_edges` |
| 023 | `is_grapheme_boundary` | `mjb_llr_023_boundary_detection` |
| 024 | `grapheme_width` | `mjb_llr_024_widths` |
| 025 | `display_column` | `mjb_llr_025_display_column_counts_width_not_bytes`, `mjb_llr_025_display_column_tab_expands` |
### Transactions and history (`src/buffer/transaction.rs`, `history.rs`)
| LLR | Source item | Test |
|---|---|---|
| 040 | `Operation` | `mjb_llr_040_operation_counts_are_byte_lengths` |
| 041 | `ChangeSet` | `mjb_llr_041_changeset_records_both_lengths`, `mjb_llr_041_empty_changeset_reports_equal_lengths` |
| 042 | `ChangeSet::apply` length check | `mjb_llr_042_length_mismatch_is_rejected`, `mjb_llr_042_rejected_change_leaves_rope_untouched` |
| 043 | `ChangeSet::apply` | `mjb_llr_043_apply_insert_and_delete` |
| 044 | `ChangeSet::apply` boundary check | `mjb_llr_044_non_char_boundary_errors_rather_than_panics` |
| 045 | `ChangeSet::invert` | `mjb_llr_045_invert_maps_each_operation` |
| 046 | `apply` + `invert` | `mjb_llr_046_apply_then_invert_round_trips` |
| 047 | `Transaction` | `mjb_llr_047_transaction_carries_a_selection` |
| 048 | `Transaction::change` | `mjb_llr_048_multiple_ordered_changes` |
| 049 | `Transaction::insert` | `mjb_llr_049_insert_at_cursor` |
| 050 | `Transaction::delete` | `mjb_llr_050_delete_selection_span`, `mjb_llr_050_d_with_an_empty_selection_deletes_one_grapheme`, `mjb_llr_050_d_on_an_empty_buffer_is_a_noop` |
| 051 | `History::commit` | `mjb_llr_051_commit_then_undo_then_redo`, `mjb_llr_051_commit_after_undo_discards_the_redo_branch`, `mjb_llr_051_undo_restores_typed_text` |
| 052 | `History::undo`, `Document::undo` | `mjb_llr_052_undo_past_start_is_a_noop`, `mjb_llr_052_undo_past_history_start_is_a_noop`, `mjb_llr_052_undo_past_history_start_is_safe`, `mjb_llr_052_u_undoes_a_deletion` |
| 053 | `History::redo`, `Document::redo` | `mjb_llr_053_redo_past_end_is_a_noop`, `mjb_llr_053_redo_past_end_is_safe`, `mjb_llr_053_capital_u_redoes` |
### Movement (`src/buffer/movement.rs`)
| LLR | Source item | Test |
|---|---|---|
| 060 | `CharCategory`, `categorize_char` | `mjb_llr_060_categories` |
| 061 | `is_word_boundary` | `mjb_llr_061_word_boundary_is_category_change` |
| 062 | `move_char_left` | `mjb_llr_062_move_char_left_stops_at_zero`, `mjb_llr_062_h_and_l_move_by_one_grapheme`, `mjb_llr_062_h_at_start_of_buffer_is_a_noop` |
| 063 | `move_char_right` | `mjb_llr_063_move_char_right_stops_at_end`, `mjb_llr_063_move_char_right_skips_whole_multibyte_char`, `mjb_llr_063_l_at_end_of_buffer_is_a_noop` |
| 064 | `move_vertically` | `mjb_llr_064_vertical_motion_preserves_column`, `mjb_llr_064_vertical_motion_clamps_to_short_line`, `mjb_llr_064_vertical_motion_is_noop_at_edges`, `mjb_llr_064_j_and_k_move_between_lines` |
| 065 | `next_word_start` | `mjb_llr_065_next_word_start_produces_a_selection`, `mjb_llr_065_w_leaves_a_selection`, `mjb_llr_065_w_then_d_deletes_the_word`, `mjb_llr_065_counted_next_word_start_advances_once_per_count` |
| 066 | `prev_word_start` | `mjb_llr_066_prev_word_start_spans_backward`, `mjb_llr_066_b_selects_backward`, `mjb_llr_066_counted_prev_word_start_advances_once_per_count` |
| 067 | `next_word_end` | `mjb_llr_067_next_word_end_spans_the_word`, `mjb_llr_067_e_selects_to_the_word_end`, `mjb_llr_067_counted_next_word_end_advances_once_per_count` |
| 068 | `is_separator` and the skip loops in the three word functions | `mjb_llr_068_word_motion_stops_at_punctuation`, `mjb_llr_068_long_word_motion_absorbs_punctuation`, `mjb_llr_068_word_motion_crosses_line_endings`, `mjb_llr_068_capital_w_treats_punctuation_as_word_characters` |
| 069 | `word_move` boundary guards | `mjb_llr_069_word_motion_is_noop_at_boundaries`, `mjb_llr_069_w_at_end_of_buffer_is_a_noop`, `mjb_llr_069_counted_motion_saturates_at_the_buffer_end` |
| 070 | `goto_file_start` | `mjb_llr_070_goto_file_start`, `mjb_llr_070_gg_goes_to_file_start` |
| 071 | `goto_last_line` | `mjb_llr_071_goto_last_line`, `mjb_llr_071_goto_last_line_without_trailing_newline`, `mjb_llr_071_ge_goes_to_the_last_line` |
| 072 | `goto_line_start` | `mjb_llr_072_goto_line_start`, `mjb_llr_072_gh_goes_to_the_line_start`, `mjb_llr_072_gs_goes_to_first_non_whitespace` |
| 073 | `goto_line_end`, `line_end_byte` | `mjb_llr_073_goto_line_end_excludes_terminator`, `mjb_llr_073_goto_line_end_handles_crlf`, `mjb_llr_073_gl_goes_to_the_line_end` |
### Viewport (`src/buffer/view.rs`)
| LLR | Source item | Test |
|---|---|---|
| 090 | `ViewPosition` | `mjb_llr_090_anchor_is_a_byte_offset_at_a_line_start` |
| 091 | `View::top_line` | `mjb_llr_091_top_line_from_anchor` |
| 092 | `View::visible_lines`, `visible_line_range` | `mjb_llr_092_visible_lines_are_bounded_by_height`, `mjb_llr_092_visible_lines_clamp_near_end_of_buffer`, `mjb_llr_092_empty_buffer_renders_safely`, `mjb_llr_092_visible_line_count_is_independent_of_file_size`, `mjb_llr_092_scrolling_a_large_file_stays_responsive` |
| 093 | scroll-off clamping | `mjb_llr_093_scrolloff_larger_than_viewport_is_clamped` |
| 094 | scroll-up branch | `mjb_llr_094_scrolls_up_to_honour_top_margin`, `mjb_llr_094_scroll_near_start_saturates_at_zero` |
| 095 | scroll-down branch | `mjb_llr_095_scrolls_down_to_honour_bottom_margin` |
| 096 | no-scroll branch | `mjb_llr_096_no_scroll_when_cursor_is_comfortable` |
| 097 | `View::set_top_line` | `mjb_llr_097_top_line_clamps_into_buffer` |
| 098 | zero-height guard | `mjb_llr_098_zero_height_viewport_is_a_noop`, `mjb_llr_098_zero_height_viewport_does_not_panic` |
| 099 | `ensure_horizontal_in_view` | `mjb_llr_099_horizontal_scroll_follows_cursor` |
| 100 | `View::page` (half) | `mjb_llr_100_half_page_moves_cursor_and_view`, `mjb_llr_100_ctrl_d_pages_half_a_screen_down`, `mjb_llr_100_ctrl_u_pages_back_up` |
| 101 | `View::page` (full) | `mjb_llr_101_full_page_moves_by_height`, `mjb_llr_101_ctrl_f_pages_a_full_screen_down` |
| 102 | paging saturation | `mjb_llr_102_paging_saturates_at_both_ends` |
### Document, encoding, line endings
| LLR | Source item | Test |
|---|---|---|
| 110 | `encoding::detect_bom` | `mjb_llr_110_detects_each_bom` |
| 111 | `Document::open`, `Buffer::new` | `mjb_llr_111_loads_contents`, `mjb_llr_111_plain_utf8_round_trips`, `mjb_llr_111_no_path_yields_a_scratch_buffer` |
| 112 | `Document::open` not-found arm | `mjb_llr_112_missing_file_yields_empty_buffer_that_remembers_the_path`, `mjb_llr_112_empty_file_is_editable`, `mjb_llr_112_file_without_trailing_newline_round_trips`, `mjb_llr_112_writing_a_new_file_creates_it`, `mjb_llr_112_missing_file_opens_as_an_empty_buffer` |
| 113 | `encoding::decode` non-UTF-8 branch | `mjb_llr_113_declared_utf16_is_transcoded_not_rejected`, `mjb_llr_113_empty_input_decodes_to_empty`, `mjb_llr_113_declared_utf16_file_opens`, `mjb_llr_113_declared_utf16_file_opens_and_edits` |
| 118 | `DecodeError`, `encoding::decode` UTF-8 branch, `Document::open` | `mjb_llr_118_invalid_utf8_is_rejected`, `mjb_llr_118_rejection_names_the_offset`, `mjb_llr_118_truncated_multibyte_char_is_rejected`, `mjb_llr_118_declared_utf8_is_strict_too`, `mjb_llr_118_valid_multibyte_utf8_is_accepted`, `mjb_llr_118_invalid_utf8_file_is_refused`, `mjb_llr_118_binary_file_is_refused_not_opened` |
| 114 | `LineEnding::detect` | `mjb_llr_114_detects_lf`, `_detects_crlf`, `_detects_lone_cr`, `_falls_back_to_platform_default`, `_first_terminator_decides`, `_crlf_is_detected_and_normalized_for_storage` |
| 115 | `encoding::encode`, `Document::encode` | `mjb_llr_115_bom_round_trips`, `mjb_llr_115_utf16le_round_trips`, `mjb_llr_115_utf16be_round_trips`, `mjb_llr_115_utf16_handles_non_ascii_and_surrogates`, `mjb_llr_115_apply_restores_original_ending`, `mjb_llr_115_crlf_round_trips_through_save` |
| 116 | `Document::is_modified`, `History::revision` | `mjb_llr_116_modified_flag_lifecycle`, `mjb_llr_116_revision_is_not_stack_depth`, `mjb_llr_116_revision_is_zero_when_pristine_and_returns_on_undo`, `mjb_llr_116_undo_back_to_saved_state_is_clean`, `mjb_llr_116_divergent_edit_at_the_same_depth_stays_modified` |
| 117 | `Document::apply` | `mjb_llr_117_apply_updates_text_and_records_history` |
### Save path (`src/buffer/save.rs`)
| LLR | Source item | Test |
|---|---|---|
| 130 | `resolve_write_path` | `mjb_llr_130_write_follows_symlink_without_replacing_it`, `mjb_llr_130_relative_symlink_resolves_against_its_own_directory`, `mjb_llr_130_write_through_a_symlink_end_to_end` |
| 131 | `readonly` | `mjb_llr_131_readonly_target_is_refused`, `mjb_llr_131_missing_file_is_not_readonly`, `mjb_llr_131_readonly_file_reports_and_preserves_contents` |
| 132 | missing-parent branch | `mjb_llr_132_missing_parent_is_refused_without_force`, `mjb_llr_132_force_creates_the_parent` |
| 133 | `must_copy` | `mjb_llr_133_hardlink_is_detected_and_preserved`, `mjb_llr_133_plain_file_does_not_need_copy_mode` |
| 134 | `backup_path`, backup creation | `mjb_llr_134_backup_is_created_beside_the_target`, `mjb_llr_134_backup_path_handles_a_bare_file_name` |
| 135 | restore-on-failure | `mjb_llr_135_failed_write_restores_the_original` |
| 136 | `copy_permissions`, backup removal | `mjb_llr_136_no_backup_file_is_left_behind` |
| 137 | `Document::save` | `mjb_llr_116_modified_flag_lifecycle` |
### Keymap and commands (`src/buffer/keymap.rs`, `mod.rs`)
| LLR | Source item | Test |
|---|---|---|
| 150 | `Keymap::new` prefix set | `mjb_llr_150_proper_prefixes_are_precomputed`, `mjb_llr_150_prefix_set_is_empty_for_a_mode_without_sequences` |
| 151 | `Keymap::resolve` match arm | `mjb_llr_151_single_key_binding_matches_immediately`, `mjb_llr_151_two_key_sequence_resolves`, `mjb_llr_151_sequences_sharing_a_prefix_stay_distinct` |
| 152 | `Keymap::resolve` prefix arm | `mjb_llr_152_prefix_key_waits_for_more` |
| 153 | `Keymap::resolve` cancel arm | `mjb_llr_153_unknown_continuation_cancels`, `mjb_llr_153_unbound_key_cancels_immediately`, `mjb_llr_153_unknown_g_sequence_does_not_corrupt_state` |
| 154 | absence of any timer | `mjb_llr_154_pending_is_not_discarded_by_time`, `mjb_llr_154_gg_resolves_regardless_of_intervening_time` |
| 155 | count accumulation | `mjb_llr_155_count_accumulates_and_is_delivered`, `_count_is_consumed_once`, `_leading_zero_is_not_a_count`, `_zero_extends_an_existing_count`, `_digits_are_not_counts_in_insert_mode`, `_count_repeats_a_motion` |
| 156 | `self_insert_char` | `mjb_llr_156_self_insert_accepts_plain_printables`, `_rejects_control_and_alt`, `_rejects_non_char_and_sequences`, `_i_then_typing_inserts_text`, `_command_keys_type_literally_in_insert_mode`, `_unbound_control_key_is_discarded_in_insert_mode` |
| 157 | `Command` enum | `mjb_llr_157_deserializes_from_the_variant_name`, `mjb_llr_157_unknown_command_name_is_an_error_not_a_panic`, `mjb_llr_157_display_round_trips_through_deserialization` |
| 158 | `Buffer::handle_command_mode_key` | `mjb_llr_158_escape_cancels_the_command_line`, `_command_line_accepts_backspace`, `_backspacing_an_empty_command_line_leaves_command_mode` |
| 159 | `Buffer::run_command_line` | `mjb_llr_159_write_saves_to_disk`, `_quit_returns_the_quit_outcome`, `_wq_writes_then_quits`, `_x_is_an_alias_for_wq`, `_unknown_command_is_reported_not_fatal`, `_force_write_creates_missing_directories`, `_empty_command_line_does_nothing` |
| 160 | unsaved-changes guard | `mjb_llr_160_quit_with_unsaved_changes_is_refused`, `mjb_llr_160_force_quit_discards_changes` |
### Configuration (`src/config.rs`)
| LLR | Source item | Test |
|---|---|---|
| 180 | `CONFIG`, `Config::new` | `mjb_llr_180_builtin_defaults_parse` |
| 181 | single TOML source | `mjb_llr_181_only_config_toml_is_read` + `cargo tree` check (see below) |
| 182 | default-merge loop | `mjb_llr_182_user_bindings_merge_per_binding` |
| 183 | `KeyBindings::deserialize`, `parse_color` | `mjb_llr_183_invalid_keybinding_is_recoverable`, `mjb_llr_183_malformed_colours_never_panic`, `mjb_llr_183_colour_cube_bounds`, `mjb_llr_183_grayscale_ramp_bounds`, `mjb_llr_183_bright_colour_is_base_plus_eight` |
| 184 | `Mode` | `mjb_llr_184_modes_deserialize_lowercase`, `mjb_llr_184_v_toggles_select_mode` |
| 185 | `EditorConfig` | `mjb_llr_185_editor_defaults`, `mjb_llr_185_final_newline_only_added_when_missing`, `mjb_llr_185_final_newline_added_on_encode_when_requested`, `mjb_llr_185_empty_document_encodes_empty` |
### Presentation (`src/components/buffer.rs`, `src/app.rs`)
Rendering is tested against a real cell grid via ratatui's `TestBackend`
(`tests/rendering.rs`), not asserted by inspection.
| LLR | Source item | Test |
|---|---|---|
| 200 | `BufferComponent::draw` render loop | `mjb_llr_200_renders_the_file_contents`, `mjb_llr_200_renders_only_the_visible_window`, `mjb_llr_200_line_numbers_are_shown_in_the_gutter`, `mjb_llr_200_empty_file_renders_without_panicking`, `mjb_llr_200_wide_characters_render` |
| 201 | cursor/selection styling in `draw` | `mjb_llr_201_cursor_is_styled_distinctly` |
| 202 | `status_line`, `message_line` | `mjb_llr_202_status_line_shows_mode_and_path`, `mjb_llr_202_status_line_marks_an_unmodified_file`, `mjb_llr_202_scratch_buffer_is_labelled` |
| 203 | `App::handle_global_key`, `Keymap::lookup_single` | `mjb_llr_203_lookup_single_does_not_disturb_pending`, `mjb_llr_203_globally_bound_key_is_not_typed_as_text` |
| 204 | `App::new` component vec | `mjb_llr_204_no_fps_counter_or_hello_world_is_rendered` |
| 205 | absence of `last_tick_key_events` | `mjb_llr_205_pending_chord_survives_redraws` |
`mjb_llr_200_renders_only_the_visible_window` is the direct check on
MJB-HLR-012: a 1000-line file in an 8-row terminal must not put line 500 on
screen.
---
## Completeness check
The matrix above is verified mechanically, not by inspection:
```bash
grep -oE 'MJB-LLR-[0-9]+' docs/requirements/llr.md | sort -u > defined
grep -rhoE 'MJB-LLR-[0-9]+' src/ | sort -u > tagged
grep -rhoE 'mjb_llr_[0-9]+' src/ tests/ | sed 's/mjb_llr_/MJB-LLR-/' | sort -u > tested
comm -23 defined tagged # LLRs with no source tag
comm -23 defined tested # LLRs with no test
comm -13 defined tested # tests naming an LLR that does not exist
```
Result at time of writing — all three sets empty:
```
defined: 98 tagged: 98 tested: 98
```
Every low-level requirement is tagged in source and exercised by at least one
test named for it. Test totals: **282 passing** (179 unit, 89 editing
integration, 14 rendering integration).
---
## Coverage
Statement (line) coverage of the buffer core, the DAL-C structural criterion:
```
src/buffer/** statement coverage: 2665/2770 = 96.21%
```
Measured with `cargo llvm-cov --summary-only test`. Per-module figures:
| Module | Lines | Covered |
|---|---|---|
| `buffer/view.rs` | 233 | 99.57% |
| `buffer/transaction.rs` | 265 | 98.87% |
| `buffer/encoding.rs` | 140 | 98.57% |
| `buffer/movement.rs` | 346 | 97.98% |
| `buffer/selection.rs` | 182 | 97.80% |
| `buffer/keymap.rs` | 223 | 97.76% |
| `buffer/history.rs` | 90 | 96.67% |
| `buffer/command.rs` | 28 | 96.43% |
| `buffer/document.rs` | 268 | 94.40% |
| `buffer/mod.rs` | 408 | 93.87% |
| `buffer/save.rs` | 218 | 93.12% |
| `buffer/line_ending.rs` | 87 | 89.66% |
| `buffer/grapheme.rs` | 195 | 89.23% |
**MC/DC and decision coverage are not DAL-C objectives and are not claimed.**
Only statement coverage is reported.
### Uncovered code, and why
- `buffer/grapheme.rs` (89.23%) — the lowest figure. The uncovered lines are
defensive `Err(_) => return` arms for `GraphemeIncomplete` variants that
cannot arise from a cursor constructed over the whole slice. They exist so a
malformed state degrades instead of panicking (MJB-HLR-018), and are
unreachable by construction, so no test can drive them.
- `buffer/save.rs` (93.12%) — the restore-on-failure path is reachable only
when the process cannot write to a directory it owns. Running the suite as
root defeats the permission bits, so
`mjb_llr_135_failed_write_restores_the_original` skips its assertion in that
case rather than reporting a false pass. The path is covered when the suite
runs as an ordinary user.
- `buffer/line_ending.rs` (89.66%) — `LineEnding::platform_default` has one
arm per platform; only the host's arm executes.
- Remaining gaps are `Display`/`From` glue generated by `thiserror` on error
variants that no test provokes.
### Verification not expressible as a unit test
`cargo tree` confirms MJB-LLR-181 and MJB-HLR-014 — that no parser for a
non-TOML configuration format remains in the dependency graph:
```bash
cargo tree | grep -iE "json|yaml|ini\b" # returns nothing
```
Confirmed: `config v0.15.25` resolves with only `pathdiff`, `serde_core`,
`toml`, and `winnow`.
|