<feed xmlns='http://www.w3.org/2005/Atom'>
<title>mojibake/docs/traceability, branch main</title>
<subtitle>text editor built to DO-178C-style rigor</subtitle>
<link rel='alternate' type='text/html' href='https://git.mojibake.wiki/mojibake/'/>
<entry>
<title>Normalise every byte offset at entry, and close MJB-DR-002</title>
<updated>2026-08-20T05:16:54+00:00</updated>
<author>
<name>rottedfm</name>
<email>rottedfm@proton.me</email>
</author>
<published>2026-08-20T05:16:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.mojibake.wiki/mojibake/commit/?id=07cef79834d91ac8db11857b0517697b0e994662'/>
<id>07cef79834d91ac8db11857b0517697b0e994662</id>
<content type='text'>
MJB-DR-002 records the failure mode byte indexing admits and char
indexing cannot express: an offset landing inside a character, which
makes ropey panic. It named two guards and asked the reviewer one
question — error or abort?

Answers the question with a rule. If saturating the input still yields a
sensible answer, saturate; if saturating would silently alter the user's
text, return an error. Aborting is rejected under MJB-HLR-018, and
because a bad offset can come from configuration, not only from internal
code. That splits the subsystem into total functions (offsets, line
indices, ranges — normalised at entry) and validating ones (change sets).

Checking the entry against the code found the defence incomplete beyond
the panic already fixed in the previous commit. `Range::cursor` returned
a raw head for a backward or empty range; `Range::line_range` clamped one
bound but not the other; `grapheme_str`, `line_end_byte`, `char_at` and
`char_before` validated nothing. All feed rope calls that panic — and not
only `insert`/`remove`: `slice`, `char_indices_at`, `byte_to_line_idx`
and `line` panic just as readily, which is what the earlier audit missed.

Two requirement problems surfaced with them. MJB-LLR-011 claimed
constructing a `Range` clamps its offsets to the nearest char boundary;
neither half was true, since `clamped` is opt-in and snaps downward. It
is restated, and the invariant it was reaching for is written down where
it actually holds — MJB-LLR-012 for what the document stores, MJB-LLR-013
for every public entry point. Backward traceability found this: the code
was right and the requirement was wrong.

`tests/boundaries.rs` drives every public entry point from outside the
crate with offsets that are out of bounds, mid-character or both, over
text including a four-byte scalar and a ZWJ cluster — of which the suite
previously contained none. It was checked against the defects it exists
to catch: reverting any one of the three guards makes it fail.

MJB-DR-002 moves to resolved, with a residual: the invariant is enforced
by convention plus that test, not structurally by a type that cannot hold
an invalid offset. Whether that is sufficient assurance at DAL-C is the
reviewer's judgement, and it is recorded as such.

Class:        Requirement change
Requirements: MJB-LLR-011 (restated), MJB-LLR-012, MJB-LLR-013 (new)
Derived:      MJB-DR-002 (resolved; five of seven now open)
Verified:     cargo build; clippy --all-targets -D warnings clean;
              cargo test 302 passing; ./scripts/check-trace.sh 101/101/101;
              cargo llvm-cov 96.50% of src/buffer/**

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
MJB-DR-002 records the failure mode byte indexing admits and char
indexing cannot express: an offset landing inside a character, which
makes ropey panic. It named two guards and asked the reviewer one
question — error or abort?

Answers the question with a rule. If saturating the input still yields a
sensible answer, saturate; if saturating would silently alter the user's
text, return an error. Aborting is rejected under MJB-HLR-018, and
because a bad offset can come from configuration, not only from internal
code. That splits the subsystem into total functions (offsets, line
indices, ranges — normalised at entry) and validating ones (change sets).

Checking the entry against the code found the defence incomplete beyond
the panic already fixed in the previous commit. `Range::cursor` returned
a raw head for a backward or empty range; `Range::line_range` clamped one
bound but not the other; `grapheme_str`, `line_end_byte`, `char_at` and
`char_before` validated nothing. All feed rope calls that panic — and not
only `insert`/`remove`: `slice`, `char_indices_at`, `byte_to_line_idx`
and `line` panic just as readily, which is what the earlier audit missed.

Two requirement problems surfaced with them. MJB-LLR-011 claimed
constructing a `Range` clamps its offsets to the nearest char boundary;
neither half was true, since `clamped` is opt-in and snaps downward. It
is restated, and the invariant it was reaching for is written down where
it actually holds — MJB-LLR-012 for what the document stores, MJB-LLR-013
for every public entry point. Backward traceability found this: the code
was right and the requirement was wrong.

`tests/boundaries.rs` drives every public entry point from outside the
crate with offsets that are out of bounds, mid-character or both, over
text including a four-byte scalar and a ZWJ cluster — of which the suite
previously contained none. It was checked against the defects it exists
to catch: reverting any one of the three guards makes it fail.

MJB-DR-002 moves to resolved, with a residual: the invariant is enforced
by convention plus that test, not structurally by a type that cannot hold
an invalid offset. Whether that is sufficient assurance at DAL-C is the
reviewer's judgement, and it is recorded as such.

Class:        Requirement change
Requirements: MJB-LLR-011 (restated), MJB-LLR-012, MJB-LLR-013 (new)
Derived:      MJB-DR-002 (resolved; five of seven now open)
Verified:     cargo build; clippy --all-targets -D warnings clean;
              cargo test 302 passing; ./scripts/check-trace.sh 101/101/101;
              cargo llvm-cov 96.50% of src/buffer/**

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Validate change sets in invert, where the guard was unreachable</title>
<updated>2026-08-20T05:16:18+00:00</updated>
<author>
<name>rottedfm</name>
<email>rottedfm@proton.me</email>
</author>
<published>2026-08-20T05:16:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.mojibake.wiki/mojibake/commit/?id=e9f2c21b16bfd851e809c03503089020b3c1037f'/>
<id>e9f2c21b16bfd851e809c03503089020b3c1037f</id>
<content type='text'>
MJB-LLR-044 promises that a change set whose operation boundaries do not
fall on char boundaries is reported as an error rather than panicking
inside the rope. `ChangeSet::apply` does exactly that, and its test
demonstrates it.

But `Document::apply` — the only path by which an edit reaches the
buffer — must compute the undo inverse against the *pre-change* rope, so
it calls `ChangeSet::invert` first. `invert` sliced the rope with no
length, bounds or boundary check at all. A mid-character delete therefore
aborted the editor inside `invert`, having never reached the guard that
exists to prevent it. In practice the guard was dead code on the only
path that mattered.

Extracts the length and boundary checks into `ChangeSet::validate` and
calls it from both `apply` and `invert`, which now returns a Result. The
error is the same `ChangeError`, so it surfaces on the status line as
before and the rope is left untouched.

Found by inspection, reading MJB-DR-002's claim that "change-set
application shall validate operation boundaries and return an error
rather than allow a rope panic" against the code meant to discharge it.
The existing MJB-LLR-044 test could not have caught this: it exercises
`ChangeSet::apply` in isolation, where the guard genuinely works. The gap
was in composition, and only a test at the `Document` level exposes it.

Class:        Defect fix
Requirements: MJB-LLR-042, MJB-LLR-044, MJB-LLR-054 (new)
Derived:      MJB-DR-002
Verified:     cargo build; clippy --all-targets -D warnings clean;
              cargo test 297 passing; ./scripts/check-trace.sh 99/99/99

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
MJB-LLR-044 promises that a change set whose operation boundaries do not
fall on char boundaries is reported as an error rather than panicking
inside the rope. `ChangeSet::apply` does exactly that, and its test
demonstrates it.

But `Document::apply` — the only path by which an edit reaches the
buffer — must compute the undo inverse against the *pre-change* rope, so
it calls `ChangeSet::invert` first. `invert` sliced the rope with no
length, bounds or boundary check at all. A mid-character delete therefore
aborted the editor inside `invert`, having never reached the guard that
exists to prevent it. In practice the guard was dead code on the only
path that mattered.

Extracts the length and boundary checks into `ChangeSet::validate` and
calls it from both `apply` and `invert`, which now returns a Result. The
error is the same `ChangeError`, so it surfaces on the status line as
before and the rope is left untouched.

Found by inspection, reading MJB-DR-002's claim that "change-set
application shall validate operation boundaries and return an error
rather than allow a rope panic" against the code meant to discharge it.
The existing MJB-LLR-044 test could not have caught this: it exercises
`ChangeSet::apply` in isolation, where the guard genuinely works. The gap
was in composition, and only a test at the `Document` level exposes it.

Class:        Defect fix
Requirements: MJB-LLR-042, MJB-LLR-044, MJB-LLR-054 (new)
Derived:      MJB-DR-002
Verified:     cargo build; clippy --all-targets -D warnings clean;
              cargo test 297 passing; ./scripts/check-trace.sh 99/99/99

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>feat: implement Helix-style modal buffer under DO-178C DAL-C</title>
<updated>2026-08-19T15:21:47+00:00</updated>
<author>
<name>rottedfm</name>
<email>rottedfm@proton.me</email>
</author>
<published>2026-08-19T15:21:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.mojibake.wiki/mojibake/commit/?id=ea0bd36167b684c0accdb5ce2b2e21b8d84aeb25'/>
<id>ea0bd36167b684c0accdb5ce2b2e21b8d84aeb25</id>
<content type='text'>
The repository was an unmodified ratatui component template: no editor
code, JSON5 config, and placeholder widgets. This establishes the first
working baseline — `moji &lt;file&gt;` 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) &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The repository was an unmodified ratatui component template: no editor
code, JSON5 config, and placeholder widgets. This establishes the first
working baseline — `moji &lt;file&gt;` 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) &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
