# mojibake — Requirements and Commit Process Software Level: **DAL-C** (DO-178C) Project status: **early development** — see the README for what does not work yet. This document explains the system: what the requirement artifacts are, how they relate to each other and to the code, and what a commit must satisfy to be accepted. It is the entry point — read this before `docs/requirements/`. --- ## 1. Why the artifacts exist DO-178C does not ask "is the code good?" It asks a narrower, checkable question: **can you show that every behaviour the software has was asked for, and that everything asked for was built and verified?** Answering it needs four things, and each one is a directory here: | Artifact | Answers | |---|---| | `docs/requirements/hlr.md` | What must the software do? | | `docs/requirements/llr.md` | How, specifically, in terms a test can check? | | `docs/requirements/derived.md` | What did the implementation need that nobody asked for? | | `docs/traceability/trace.md` | Where is each of those in the code, and which test proves it? | The chain runs **HLR → LLR → source → test**, and it must hold in *both* directions. Forward: every requirement is implemented and verified. Backward: every piece of behaviour traces to a requirement. Backward traceability is the one people skip, and it is the one that catches unrequested behaviour — code that does something nobody asked for is either a missing requirement or a defect, and you cannot tell which until you look. --- ## 2. The three requirement kinds ### High-level requirements — `MJB-HLR-nnn` What the software does, in terms a user would recognise. One behaviour per requirement, phrased with "shall", and stated so that it is *observable* — if you cannot describe how to check it, it is not a requirement yet. > **MJB-HLR-007** — The editor shall provide next-word-start, > previous-word-start and next-word-end motions bound by default to `w`, `b` > and `e`. Consistent with Helix and unlike Vim, each shall leave a selection > spanning the traversed text rather than a collapsed cursor […] HLRs must not specify implementation. Two here do — MJB-HLR-004 names a rope and MJB-HLR-014 names TOML — because those were themselves requested. That exception is recorded in the requirements review rather than hidden. ### Low-level requirements — `MJB-LLR-nnn` The same behaviour decomposed until each statement maps to one identifiable thing in the code and one test. An LLR names types, functions and exact values; an HLR does not. > **MJB-LLR-065** — `move_next_word_start` shall return a range whose anchor is > the pre-motion cursor position and whose head is the start of the following > word, so the result is a selection spanning the traversed text. Every LLR **must** cite a parent HLR. An LLR with no parent is either a derived requirement in disguise or scope creep. ### Derived requirements — `MJB-DR-nnn` Requirements the implementation turned out to need that no HLR anticipated. DO-178C §5.1.1.b requires these to be recorded **and fed back to the safety assessment**, because by definition nobody evaluated them when the HLRs were written. Every entry states what forced it and carries an explicit *"Reviewer must judge"* clause. A derived requirement is not a note-to-self; it is an open question addressed to someone else. Two of the seven here could not have been anticipated, and both are instructive: - **MJB-DR-002** — choosing byte indices over char indices admits a failure mode char indexing cannot express: an offset landing inside a character, which makes ropey panic. - **MJB-DR-007** — `encoding_rs` decodes UTF-16 but silently refuses to *encode* it, substituting UTF-8. Found by a round-trip test, not by reading the code. --- ## 3. The ID system ``` MJB-HLR-nnn high-level requirement MJB-LLR-nnn low-level requirement MJB-DR-nnn derived requirement ``` IDs are **permanent and never reused**. Deleting a requirement means marking it withdrawn, not freeing its number — a trace matrix that silently renumbers is worse than no matrix, because reviews and commits reference IDs by number. LLR numbers are allocated in blocks by subsystem so related requirements read together: | Block | Subsystem | |---|---| | 001–019 | selection | | 020–039 | graphemes | | 040–059 | transactions, history | | 060–089 | movement | | 090–109 | viewport | | 110–129 | document, encoding, line endings | | 130–149 | save | | 150–179 | keymap, commands | | 180–199 | configuration | | 200–219 | presentation | Blocks are a convenience, not a rule. Leave gaps. --- ## 4. How code and tests bind to requirements Two conventions, both chosen so a machine can check them. **Source carries a tag** immediately above the item implementing it: ```rust /// MJB-LLR-005: the byte offset the block cursor is drawn at. pub fn cursor(&self, text: RopeSlice) -> usize { ``` **Tests are named for what they verify**: ```rust #[test] fn mjb_llr_065_w_leaves_a_selection() { ``` A test name is a claim. `mjb_llr_065_w_leaves_a_selection` asserts that MJB-LLR-065 holds; if the test does not actually establish that, the name is a lie and the matrix is worthless. Prefer a name that states the expected behaviour over one that describes the mechanics. More than one test may carry the same ID. Robustness cases usually do. --- ## 5. The matrix is verified mechanically Inspection does not scale and does not survive refactoring. Three `grep`s answer the whole question: ```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 # requirement with no implementation comm -23 defined tested # requirement with no test comm -13 defined tested # test naming a requirement that does not exist ``` **All three sets must be empty.** The third catches typos and stale references after a requirement is renumbered or withdrawn — a check that is easy to omit and that fails silently when omitted. Run it before every commit. It is not optional, and it takes under a second. --- ## 6. Commit requirements DO-178C Table A-8 (configuration management) wants changes to be controlled, traceable, and reviewable. In practice, for this repository, that reduces to five rules. ### Rule 1 — A commit is a complete unit A commit must leave the tree in a state where all of the following pass: ```bash cargo build cargo clippy --all-targets -- -D warnings cargo test ``` plus the traceability check from §5. Requirements, implementation, and tests land **together**. Do not commit an LLR whose test arrives in the next commit: between the two, the matrix is broken and the repository cannot be reviewed. ### Rule 2 — The message states which requirements are affected ``` Requirements: MJB-LLR-065, MJB-LLR-066 Derived: MJB-DR-007 (omit if none) Verified: cargo test (294 passing), clippy clean, trace 98/98/98 ``` The body explains *why*. The diff already shows what changed; it cannot show what problem you were solving, and that is the part a reviewer cannot reconstruct. ### Rule 3 — Classify the change Every commit is exactly one of these, and the class determines what else must be in it: | Class | Also required in the same commit | |---|---| | **Implementation only** — behaviour already specified | Test naming the existing LLR | | **New behaviour** | New LLR, its parent HLR, source tag, test | | **Requirement change** | Updated LLR/HLR, updated tests, updated trace matrix | | **Derived requirement discovered** | Entry in `derived.md` with a "Reviewer must judge" clause | | **Defect fix** | Regression test named for the LLR that was violated | | **Process/docs only** | Nothing further; state that no source changed | If a change does not fit a class, that is the signal: either it is unrequested behaviour needing a requirement, or it is two commits. ### Rule 4 — A defect fix explains how it was found This is the rule most worth keeping. Recording the *detection method* tells the next person which verification activities are actually working: > **MJB-DR-007** — `encoding_rs::encode` silently substitutes UTF-8 for UTF-16. > Invisible to inspection; found only by a byte-comparing round-trip test. Any > encoding added later should be guarded by the same test shape. Four defects in the initial buffer work were found by four different means — a failing integration test, a clippy lint, a round-trip test, and a coverage report showing 0% on a function nobody called. None were found by re-reading the code. That is worth knowing. ### Rule 5 — Reviews are recorded, not implied DAL-C permits **review without independence**: the author may review their own work. It does not permit skipping the review. Completion is recorded in `docs/reviews/`: - `requirements-checklist.md` — against DO-178C Tables A-3 and A-4 - `code-checklist.md` — against Table A-5 - `library-selection.md` — rationale for non-obvious dependencies A checklist that says only "Pass" everywhere has not been used. The value is in the entries that say something: a conflict found, a requirement reworded, a dependency accepted with a stated risk. --- ## 7. Definition of done ``` [ ] Requirements written or updated before the code [ ] Every new LLR cites a parent HLR [ ] Source tagged // MJB-LLR-nnn [ ] Test named mjb_llr_nnn_* for every affected LLR [ ] Robustness cases covered: empty, boundary, malformed, overflow [ ] Trace matrix updated [ ] Three-way grep check: all sets empty [ ] cargo build [ ] cargo clippy --all-targets -- -D warnings [ ] cargo test [ ] Statement coverage reported for changed modules [ ] Derived requirements recorded and flagged [ ] Review checklist updated [ ] Commit message names affected requirements and classifies the change ``` --- ## 8. What is deliberately *not* claimed Overstating compliance is worse than not claiming it, because it removes the reader's ability to judge. This project claims: - **Statement coverage only.** MC/DC and decision coverage are DAL-A and DAL-B objectives. They are not measured and are not claimed. - **Review without independence.** The author reviewed their own work, which is permitted at DAL-C and stated plainly in each checklist. - **No qualified tools.** `cargo-llvm-cov` and `clippy` are not tool-qualified per DO-330. Their output is evidence, not proof. - **Six open derived requirements.** They need a safety-assessment judgement that has not happened. They are listed as open, not quietly resolved. --- ## 9. Worked example Adding `t` (find-till-char) would go: 1. **HLR** — does an existing one cover it? MJB-HLR-006 covers character and line motion but not character search. So a new HLR is needed. 2. **LLR** — decompose: the pending-argument state, the forward search, the stop-one-short semantics, the not-found case, the count interaction. Roughly five LLRs in the 060–089 block. 3. **Derived?** — the keymap has no way to capture "the next keystroke as a literal character". That is a real gap MJB-HLR-015 did not anticipate: record it in `derived.md` and flag it. 4. **Implement**, tagging each item. 5. **Test** each LLR, including: not found, at end of buffer, on a multi-byte character, with a count exceeding the number of matches. 6. **Update** the trace matrix; run the three-way grep. 7. **Commit** as class *New behaviour*, naming every new ID. The step people skip is 3. It is the one DO-178C exists to catch.