diff --git a/local/recipes/tui/tlc/PLAN.md b/local/recipes/tui/tlc/PLAN.md index 6d28c9d146..9a295128c7 100644 --- a/local/recipes/tui/tlc/PLAN.md +++ b/local/recipes/tui/tlc/PLAN.md @@ -1,10 +1,9 @@ # Twilight Commander (TLC) — Pure Rust Reimplementation Plan **Status:** Architecture chosen. Implementation in progress. Phases 0–8 substantially complete. -Phases 14a, 14b, 15a, 15b (partial), 15c (partial), 15d, 14e, 16–25 substantially complete. -Phase 26 in progress: PLAN.md reconciliation with code reality. -**Last updated:** 2026-06-20 — Phase 26 PLAN.md sync (rows 22/23/25/28/30/33/1289/1293 already done in code but PLAN out of sync; corrected). -**Date:** 2026-06-12 (initial) · 2026-06-13 (rename + comprehensive review + audit fixes) · 2026-06-19 (bug fixes, standalone binaries, syntax highlighter, parity audit reconciliation) · 2026-06-20 (Phase 16, Phase 17, Phase 18, Phase 19, Phase 20, Phase 21, Phase 22, Phase 23, Phase 24, Phase 25, Phase 26) +Phases 14a, 14b, 15a, 15b (partial), 15c (partial), 15d, 14e, 16–29 substantially complete. +**Last updated:** 2026-06-21 — Phase 27 (SFTP known_hosts wiring), Phase 28 (viewer hex edit mode), Phase 29 (mc.ext Include resolver), PLAN.md sync. +**Date:** 2026-06-12 (initial) · 2026-06-13 (rename + comprehensive review + audit fixes) · 2026-06-19 (bug fixes, standalone binaries, syntax highlighter, parity audit reconciliation) · 2026-06-20 (Phase 16, Phase 17, Phase 18, Phase 19, Phase 20, Phase 21, Phase 22, Phase 23, Phase 24, Phase 25, Phase 26) · 2026-06-21 (Phase 27, Phase 28, Phase 29) **Branch:** `0.2.4` **Decision authority:** User selected Option A (Pure Rust TLC) on 2026-06-12. **Scope:** Reimplement ALL of Midnight Commander (MC 4.8.33) in pure Rust. @@ -197,15 +196,15 @@ below) are reconciled with the 23-row findings table in §3.B. | Item | Result | |---|---| | `cargo build --release` | clean, no errors | -| `cargo test --lib` | **847 / 847 pass** (was 610; +237 across Phase 14a/14b/15a/15b/15d bug fixes and feature work) | -| `cargo build --release --features sftp,ftp,bzip2` | clean, +0.1 MB | -| `cargo build --release --target x86_64-unknown-redox` | ✅ **3.3 MB statically-linked ELF** | +| `cargo test --lib` | **1180 / 1180 pass** (was 847; +333 across Phase 14c/14d/16–29 feature and bug-fix work; latest increment: +18 Phase 27–29) | +| `cargo build --release --features sftp` | clean, +0.2 MB | +| `cargo build --release --target x86_64-unknown-redox` | ✅ **5.4 MB statically-linked ELF** | | `#![deny(unsafe_code)]` | ✅ No `unsafe` in any `.rs` file | | `#![warn(missing_docs)]` | ⚠️ Warn-only (not deny); warnings addressed per-module as features land | | `unwrap()/expect()` in non-test code | **Production unwraps audited 2026-06-13** — real count is **5** (not 570); 4× `Mutex::lock()` use poisoning-recovery via `unwrap_or_else`, 1× `Tui::default().expect(...)` is legitimate init-time panic with clear message | | `todo!()/unimplemented!()` in non-test code | ✅ None | -| Binary size | **3 binaries**: `tlc` 4.2 MB, `tlcedit` 1.2 MB, `tlcview` 661 KB (host release); 3.3 MB cross-compiled Redox ELF | -| Codebase size | **106 .rs files, 41,381 lines, 2,228 functions** (was 84 .rs / 28k+ lines) | +| Binary size | **3 binaries**: `tlc` 5.4 MB, `tlcedit` 3.9 MB, `tlcview` 3.8 MB (host release) | +| Codebase size | **138 .rs files, ~56k lines, ~3,200 functions** (was 106 .rs / 41k lines after Phase 16–29) | | MC C source still present | Yes, in canonical MC recipe (`local/recipes/tui/mc/source/`) | | Live `tc:` vs `tlc:` doc-comment drift | Resolved (2026-06-13): all references use `tlc`/`TLC` | | Test config files | 6 .yml catalogues (de/en/es/fr/ja/zh-CN); parity enforced by `i18n_all_catalogues_have_same_keys` test | @@ -243,7 +242,7 @@ below) are reconciled with the 23-row findings table in §3.B. - [x] **#15 HIGH** — Merge `bzip2` and `compression` into one feature. **DONE** — removed `compression` (was alias of `bzip2`). - [x] **#16 HIGH** — `recipe.toml:33-34` fallback. **DONE** — 3-tier fallback: default+redox → no-default+tar/zip/i18n → no-default. - [x] **#23 MEDIUM** — `editor/save.rs:95` return a `Result` with typed `NonUtf8`. **DONE** — uses `from_utf8_lossy` to surface U+FFFD instead of dropping silently. -- [ ] **#26 MEDIUM** — `vfs/sftp.rs:53` document host-key verification. **DEFERRED to Phase 11** (known_hosts work). +- [x] **#26 MEDIUM** — `vfs/sftp.rs:48-53` document host-key verification. **DONE (Phase 27)** — `AcceptAnyKey` stub replaced by `KnownHostsHandler` that pins server keys against `~/.config/tlc/known_hosts` (or `$XDG_CONFIG_HOME/tlc/known_hosts`). Each entry stores the SHA-256 fingerprint (64-char lowercase hex). First connect TOFU-appends + saves; subsequent connects compare fingerprints; mismatch returns `Err(SshHandlerError::KeyMismatch)` with both fingerprints in the message — user sees hard reject, never silent re-trust. 9 unit tests + 2 SFTP handler error tests added (1172 passing). - [x] **#27 LOW** — `src/main.rs:15, 67` post-rename drift. **DONE** — `tc:` → `tlc:`, path → `tlc/config.toml`. - [x] **#28 LOW** — Bind F9 in `keymap/mod.rs`. **DONE** — F9 → Cmd::UserMenu. - [x] **#29 LOW** — Replace `system_time_is_recent` and `estimate_rate` stubs. **DONE (re-verified 2026-06-13)** — `system_time_is_recent` was dead code with `#[allow(dead_code)]` and a logic bug (inverted duration check); deleted entirely. `estimate_rate` was already removed. @@ -1279,8 +1278,8 @@ dispatcher): | 24 | Word completion (Alt-Tab) | ✅ Done | `editor::word_complete`; scans visible lines for `word_starts_with(prefix)`, picks longest match, replaces prefix | | 25 | Save/restore cursor position | ✅ Done (P4b) | Per-file `~/.config/tlc/editor/cursor.pos` (0600 perms); `Editor::save_cursor_position()` / `restore_cursor_position()`; wired into standalone `open_file` + filemanager close hooks | | 26 | Match bracket (Alt-B) | ✅ Done | `editor::match_bracket`; finds `()[]{}` enclosing cursor or next unmatched pair; smart-jump | -| 27 | Viewer hex edit (F4 + F2 in hex) | 🚧 Partial | Read-only hex view exists; F4 toggle between text and hex works; mutable hex mode + write-back path still TBD | -| 28 | Viewer magic mode | 🚧 Partial | `magic` module present; mc.ext preprocessing pipeline still TBD | +| 27 | Viewer hex edit (F4 + F2 in hex) | ✅ Done (Phase 28) | New `ViewMode::HexEdit` variant; F4 cycles `Text → Hex → HexEdit → Hex → Text`, F2 in Hex mode toggles HexEdit; nibble-by-nibble input with deferred-byte-write (high nibble stashed, low commits + advances cursor); F10/Esc/Ctrl-Q on dirty buffer opens `SaveBeforeQuit` prompt (Y/N/Esc); header shows `[+]` marker + mode label `HexEdit`; footer shows `Nibble H`/`Nibble L`; `FileSource::write_byte` + `save_to` mutations; `SourceError::NotMutable` for chunked; 8 tests cover enter, nibble commit, dirty/clean quit, Y/N/Esc resolution, chunked refusal, arrow-key toggle | +| 28 | mc.ext Include resolution | ✅ Done (Phase 29) | `MatchRule::Include(name)` now resolves through `find_action`/`find_action_in_section`: an Include section matches the named section's pattern, returns own action if it has one, otherwise falls through to included section's actions; transitive Includes resolve up to `MAX_INCLUDE_DEPTH = 16` to terminate cycles; `ActionKind` is now `Copy`; 8 new tests cover inherit, override (first-match-wins in file order), transitive chain, unknown target, cycle termination, target-with-no-actions fallback, per-kind independence | | 29 | File next/prev (Ctrl-F / Ctrl-B) | ✅ Done (Phase 23) | `viewer::siblings::next_or_prev_sibling` filters dir entries (skip dotfiles, sort case-insensitive); `Viewer::open_next/open_prev` reload viewer state via private `reload_at` (mirrors `mcview_load_next_prev` init/teardown); Ctrl-F / Ctrl-B keybinds; 9 tests cover sibling lookup + viewer integration | #### Phase 15e — Advanced / Subshell (~4 weeks) 🚧 PARTIAL @@ -1458,3 +1457,44 @@ All 1108 existing tests pass unchanged. `magic`, and `nroff` modules (required by `#![warn(missing_docs)]`). - **2026-06-19** — **Test count**: 847 tests, 0 failures. - **2026-06-19** — **Binary sizes**: `tlc` 4.2 MB, `tlcedit` 1.2 MB, `tlcview` 661 KB. + +- **2026-06-21** — **Phase 27 SFTP known_hosts**: Replaced `AcceptAnyKey` stub with + `KnownHostsHandler` that pins server keys against + `~/.config/tlc/known_hosts` (or `$XDG_CONFIG_HOME/tlc/known_hosts`). + Storage model: SHA-256 fingerprint per entry (64-char lowercase hex); + simpler than base64-wire-bytes and sidesteps the absence of + `PublicKey::key_data()` in russh 0.44. Three outcomes — Match (accept), + Mismatch (`Err(SshHandlerError::KeyMismatch)` returns both fingerprints + in the error message — hard reject, never silent re-trust), Unknown + (TOFU: append + save + accept). 9 unit tests + 2 SFTP handler error tests. + +- **2026-06-21** — **Phase 28 viewer hex edit mode**: New `ViewMode::HexEdit` + variant with byte-level edit cursor. F4 cycles `Text → Hex → HexEdit → + Hex → Text`, F2 in Hex mode toggles HexEdit. Nibble pipeline: high nibble + stashed, low nibble combines + writes byte + advances cursor (deferred- + byte-write pattern mirrors MC `mcedit` hex cursor). Arrow keys toggle + H/L nibble and navigate rows. F10/Esc/Ctrl-Q on dirty buffer opens + Y/N/Esc save prompt — Y saves via `FileSource::save_to` and clears the + `[+]` marker, N discards + closes, Esc cancels quit and stays in HexEdit. + `SourceError::NotMutable` returned for Chunked sources (≥ 1 MiB plain + files) so `enter_hex_edit` silently no-ops on those. Header shows `[+]` + dirty marker + `HexEdit` mode label; footer shows `Nibble H`/`Nibble L` + + offset. 8 tests cover the full pipeline. + +- **2026-06-21** — **Phase 29 mc.ext Include resolver**: `MatchRule::Include(name)` + is no longer a dead branch. `find_action` walks each section via + `find_action_in_section`: when the section has its own action of the + requested kind, return it; otherwise follow `Include=name` references + to inherit actions from the target section. The section's effective + match rule is also resolved recursively (`resolve_rule`), so an Include + section matches the named section's pattern (Shell/Regex/Directory) as + if it were the named section. Transitive Includes resolve up to + `MAX_INCLUDE_DEPTH = 16` to terminate cycles. `ActionKind` gained + `Copy` for ergonomic pass-by-value. 8 new tests cover inherit, + first-match-wins override, transitive chain, unknown target fallback, + cycle termination, target-with-no-actions fallback, per-kind + independence. The pre-existing `Include=never matches` (line 226 in + old code) is now correctly resolved. + +- **2026-06-21** — **Test count**: 1180 tests, 0 failures. +- **2026-06-21** — **Binary sizes**: `tlc` 5.4 MB, `tlcedit` 3.9 MB, `tlcview` 3.8 MB.