diff options
| author | rottedfm <rottedfm@proton.me> | 2026-08-19 11:21:47 -0400 |
|---|---|---|
| committer | rottedfm <rottedfm@proton.me> | 2026-08-19 11:21:47 -0400 |
| commit | ea0bd36167b684c0accdb5ce2b2e21b8d84aeb25 (patch) | |
| tree | be1267972b5de2f1ae592577dfabce67f1fe6e87 /docs/requirements/derived.md | |
| parent | 8c0b4c53b130555f040884c1f52b90f16b23e241 (diff) | |
feat: implement Helix-style modal buffer under DO-178C DAL-C
The repository was an unmodified ratatui component template: no editor
code, JSON5 config, and placeholder widgets. This establishes the first
working baseline — `moji <file>` opens a file into a ropey rope and edits
it with Helix selection-first semantics.
Requirements, implementation and tests land together because they must:
the traceability check rejects requirements with no implementation and
tests naming requirements that do not exist, so neither half is a valid
commit on its own.
Package renamed to mojibake-editor (mojibake was taken on crates.io);
binary is moji, library target stays mojibake.
Class: New behaviour
Requirements: MJB-HLR-001..019, MJB-LLR-001..205
Derived: MJB-DR-001..007 (DR-001 resolved, six open for review)
Verified: cargo build; clippy --all-targets -D warnings clean;
cargo test 294 passing; ./scripts/check-trace.sh 98/98/98;
cargo package clean
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'docs/requirements/derived.md')
| -rw-r--r-- | docs/requirements/derived.md | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/docs/requirements/derived.md b/docs/requirements/derived.md new file mode 100644 index 0000000..8b31070 --- /dev/null +++ b/docs/requirements/derived.md @@ -0,0 +1,161 @@ +# mojibake — Derived Requirements + +Software Level: **DAL-C** (DO-178C) + +DO-178C §5.1.1.b: requirements arising from design decisions that are not +traceable to a higher-level requirement must be recorded and **flagged for +review** by the safety assessment process. Each entry below states what forced +it and what the reviewer must judge. + +--- + +## MJB-DR-001 — UTF-8 is strict; declared encodings are transcoded + +**Status:** **resolved** — no longer an open conflict +**Relates to:** the originating task statement, which listed a "non-UTF-8 +rejection path" as a robustness case. + +### How it stood + +Full Helix save fidelity transcodes rather than rejects, so the first +implementation decoded every input leniently and replaced bad bytes with U+FFFD. +That appeared to contradict the requested rejection path, and the contradiction +was recorded here for the safety assessment. The flagged risk was concrete: +substitution is invisible to the user, and saving a buffer that contains +substituted characters writes U+FFFD over their data. + +### How it was resolved + +The requester directed that the UTF-8 rule be made an **exception**. The two +behaviours are not in fact in conflict once split by whether the encoding is +*known*: + +| Input | Behaviour | Rationale | +|---|---|---| +| BOM declares a non-UTF-8 encoding | Transcode; invalid sequences become U+FFFD | The encoding is known, so the substitution is reproducible on write and Helix fidelity is preserved | +| UTF-8, declared or assumed | **Reject** with the failing byte offset | Nothing is known that would make a repair reproducible, so refusing is the only non-destructive answer | + +**Derived requirements.** MJB-LLR-113 governs the transcoding branch; +**MJB-LLR-118** governs the UTF-8 exception. MJB-LLR-115 continues to require +that a detected encoding and BOM round-trip through load and save. + +**Residual item for the reviewer.** A user who genuinely wants to inspect a +binary file now cannot open it at all. No override (`moji --binary`, or a +`:e!`-style force) is provided. This is a deliberate omission rather than an +oversight: an override would reintroduce the lossy-save hazard through a +different door, and should be added only with a corresponding requirement that +makes such a buffer read-only. + +--- + +## MJB-DR-002 — Byte offsets require explicit char-boundary defence + +**Status:** open — needs review +**Forced by:** the choice of ropey 2.0 with `metric_chars` disabled. + +Under char indexing, an index cannot fall inside a character. Under byte +indexing it can, and `Rope::insert`/`remove` panic when it does. This failure +mode does not exist in the Helix design being ported and so is not covered by +any HLR. + +**Derived requirement.** Every externally supplied byte offset shall be clamped +into range and moved to a char boundary at construction (MJB-LLR-011), and +change-set application shall validate operation boundaries and return an error +rather than allow a rope panic (MJB-LLR-044). + +**Reviewer must judge:** whether returning an error is the correct response, or +whether a violated boundary indicates a defect that should abort. Current choice +is to return an error, consistent with MJB-HLR-018. + +--- + +## MJB-DR-003 — Soft wrap is not implemented + +**Status:** open — needs review +**Forced by:** scope. + +Helix's viewport carries a `vertical_offset` to address rows within a +soft-wrapped line. With no soft wrap, one buffer line occupies exactly one +screen row, so `vertical_offset` is always zero and is omitted from +`ViewPosition` (MJB-LLR-090). + +**Derived requirement.** Lines wider than the viewport shall scroll horizontally +rather than wrap (MJB-LLR-099). + +**Reviewer must judge:** that horizontal scrolling is acceptable for the intended +use, and that reintroducing soft wrap later is understood to require reworking +the viewport anchor. + +--- + +## MJB-DR-004 — Keymap ownership is split between App and Buffer + +**Status:** open — needs review +**Forced by:** the template's event routing, which delivers every key both to the +application keymap and to every component. + +Leaving that routing intact would let a global binding such as `q` fire while the +user types in insert mode. Ownership is therefore split: `App` owns only the +`Global` mode and consumes matching keys; the buffer owns all other modes. + +**Derived requirement.** `App` shall not forward a key to components once the +`Global` keymap has matched it (MJB-LLR-203). + +**Reviewer must judge:** that `Global` bindings are intentionally unreachable +from every mode, and that placing `Ctrl-c` there is intended even though Helix +binds `Ctrl-c` to comment-toggle in normal mode. + +--- + +## MJB-DR-005 — Count is accumulated but consumed by few commands + +**Status:** open — needs review +**Forced by:** MJB-LLR-155 being cheap to implement but not required by any +command in the mandated binding set. + +No command in MJB-HLR-006 through MJB-HLR-013 requires a count. The count is +accumulated and passed to command execution, where motions honour it and other +commands ignore it. + +**Reviewer must judge:** whether silently ignoring a count on a command that does +not use it is acceptable, or whether it should be reported as an error. + +--- + +## MJB-DR-007 — UTF-16 must be encoded by hand + +**Status:** open — needs review +**Found during:** implementation, by a failing round-trip test. + +`encoding_rs::Encoding::encode` is deliberately asymmetric. It decodes UTF-16 +but refuses to encode to it, silently substituting UTF-8 and reporting the +substitution only through a return value that is easy to discard. Delegating +the save path to it wrote UTF-8 bytes beneath a UTF-16 byte order mark — a +file that no longer matched its own BOM. + +**Derived requirement.** UTF-16LE and UTF-16BE shall be encoded directly from +`str::encode_utf16`, with explicit little- and big-endian byte order, and shall +not be routed through `encoding_rs::Encoding::encode` (MJB-LLR-115). + +**Reviewer must judge:** whether the remaining encodings, which *are* delegated +to `encoding_rs`, share any comparable asymmetry. The known set is UTF-16LE and +UTF-16BE; single-byte and UTF-8 encodings round-trip correctly. Note that this +defect was invisible to inspection and was caught only by a round-trip test — +the same test shape should guard any encoding added later. + +--- + +## MJB-DR-006 — Pre-release dependency under DAL-C + +**Status:** accepted by the requester — recorded for review +**See:** [../reviews/library-selection.md](../reviews/library-selection.md) + +ropey 2.0.0-beta.1 is a pre-release, self-described as not battle-tested. It was +selected over the stable 1.6.1 deliberately, with the trade-off stated. + +**Derived requirement.** The dependency shall be pinned to an exact version and +reached only through `src/buffer/document.rs`, so that a replacement is confined +to one module. + +**Reviewer must judge:** whether a pre-release dependency is acceptable for the +intended deployment, and whether the confinement is in fact maintained. |
