Adds the corruption-proofing layer for the edit/persist path:
- chunked_buffer_fuzz_test.go: differential fuzz of ChunkedBuffer
Insert/Delete against a plain []byte shadow model (arbitrary byte
positions/text, chunk sizes 1..64KB, 500-2000 ops each) verifying
FileLen, FullContent, Content probes and the chunk-size invariant
after every op; rune-aligned variant adds UTF-8 validity and an
independent RuneIndexToByte oracle.
- line_index_fuzz_test.go: differential fuzz of the incremental
LineIndex updates against a full-recomputation oracle (mixed,
no-newline, single-line, CRLF, trailing-newline shapes), plus a
trailing-empty-line structural invariant test.
- state_api_fuzz_test.go: differential fuzz of the production edit
entry points (HandleInsert/Backspace/Delete/ReplaceRange, incl.
selection variants) checking content, UTF-8 validity, chunk
invariant, line index and exact cursor after every op.
- real_file_fuzz_test.go (e2e): random edit sequences (incl.
window-relative IME replaces) against the REAL filesystem with
per-batch IME-window-consistency checks, forced-flush disk
byte-comparison, then a second logic instance (restart simulation)
must reload byte-identical content with a fresh line index;
asserts no stray temp files.
- filesystem_test.go (real): atomicity contract - exact round trip
at edge sizes, concurrent reader never sees a torn file across 150
alternating 2MB writes, failed write (read-only dir) leaves the
original byte-identical, stale temp file is consumed.
Fixes a real invariant violation the fuzzing exposed: Insert halved
an oversized spliced result once, so a large paste into a non-empty
buffer left chunks up to ~P/2 (8x target at 1MB/64KB), breaking the
documented 'no chunk > 2x target' invariant. Insert now re-chunks the
oversized result into pieces of at most chunkSize, making the
invariant hold after every edit. Mutation-tested: dropping one byte
in Insert and one entry in UpdateLineIndexAfterInsert are both
caught by the fuzz suite.
- Add WriteFile method to mock filesystem (non-atomic path, creates files)
- Implement WriteFileAtomic in mock with temp file + rename pattern
using .tmp/ directory, matching real filesystem semantics
- Update WriteFileTask.Execute() to use WriteFileAtomic
- Update FlushAll() to use WriteFileAtomic
- Fix mock to create files on write (matching os.WriteFile behavior)
- Update tests to match new semantics (create-if-not-exists)
- Define pool.FileSystem interface in internal/io/pool/filesystem.go
- Move DirEntry interface to internal/io/pool/types/types.go to break
the pool <-> mock import cycle
- Implement real.FileSystem with atomic writes (temp file + os.Rename)
- Update mock.FileSystem to satisfy pool.FileSystem interface
- Update worker pool tasks to accept pool.FileSystem interface
- Update main.go to use RealFileSystem by default, with -root flag
- Update all browser/editor/test references to types.DirEntry