aboutsummaryrefslogtreecommitdiff
path: root/docs/requirements/derived.md
diff options
context:
space:
mode:
authorrottedfm <rottedfm@proton.me>2026-08-19 11:21:47 -0400
committerrottedfm <rottedfm@proton.me>2026-08-19 11:21:47 -0400
commitea0bd36167b684c0accdb5ce2b2e21b8d84aeb25 (patch)
treebe1267972b5de2f1ae592577dfabce67f1fe6e87 /docs/requirements/derived.md
parent8c0b4c53b130555f040884c1f52b90f16b23e241 (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.md161
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.