Skip to content

Phase 3 Plan F: editor + persistence + final demo (Phase 3 complete)#18

Merged
cyyeh merged 13 commits into
mainfrom
phase3-plan-f
Apr 27, 2026
Merged

Phase 3 Plan F: editor + persistence + final demo (Phase 3 complete)#18
cyyeh merged 13 commits into
mainfrom
phase3-plan-f

Conversation

@cyyeh
Copy link
Copy Markdown
Owner

@cyyeh cyyeh commented Apr 27, 2026

Summary

Closes Phase 3 — the multi-process OS phase. After this lands, the spec's full §Definition of Done holds end-to-end.

  • edit userland binary (src/kernel/user/edit.zig, ~205 LoC) — cursor-moving text editor that finally exercises 3.E's raw-mode console arm. Loads file into a 16 KB buffer; redraw-on-every-keystroke ANSI loop; ESC [ A/B/C/D arrows, printable insert, backspace, ^S save (close + O_TRUNC re-open), ^X exit (cooked-mode restore via defer).
  • 3 new e2e testse2e-editor (43-byte fixture: edit /etc/motd → 2× right → Y → ^S^X → cat asserts heYllo from phase 3); e2e-persist (two ccc passes on copied image; pass 2 sees pass 1's writes); e2e-cancel (10-byte fixture cat\n\x03exit\n proves the kill-flag chain end-to-end — console.feedByte(0x03)proc.kill(fg_pid)killed flag → console.read returns -1 → syscall dispatch calls proc.exit).
  • No emulator or kernel changes. Plan F is userland + tests + docs only — every kernel/emulator hook was already shipped in Plans 3.A–3.E.
  • Doc updates — README bumped to "Phase 3 complete" with Plan 3.F summary block; deck (index.html) gets a Ch 3.F slide + ✓ 3.F status row replacing the prior "Next · plan 3.f" panel; closing chapter title flipped from "Phase 3 · underway" to "Phase 3 · complete".

The plan doc lives at docs/superpowers/plans/2026-04-27-phase3-plan-f-editor-persist.md (8 tasks, each landing as its own commit).

Test plan

  • zig build test — all unit tests pass
  • zig build riscv-tests — rv32ui/um/ua/mi/si -p-* (67 tests) pass
  • All Phase 1 e2e: e2e, e2e-mul, e2e-trap, e2e-hello-elf
  • Phase 2 e2e: e2e-kernel
  • All Phase 3 e2e: e2e-multiproc-stub, e2e-fork, e2e-fs, e2e-shell
  • NEW Phase 3.F e2e: e2e-editor, e2e-persist, e2e-cancel
  • Other: e2e-snake, e2e-plic-block
  • Manual: zig build run -- --disk shell-fs.img kernel-fs.elf boots to \$ prompt; edit /etc/motd works interactively; ^X returns cleanly to cooked-mode shell

Phase 3 §Definition of Done — closed

DoD bullet Status
kernel.elf builds ✅ (also kernel-fs.elf)
mkfs + fs-img produce fs.img ✅ (plus shell-fs.img)
ccc --disk … kernel.elf boots /bin/init → /bin/sh → \$ prompt ✅ via e2e-shell
Scripted session (ls / cat / echo / edit / cat / exit) ✅ split across e2e-shell, e2e-editor, e2e-persist
^C cancels foreground program ✅ via e2e-cancel
riscv-tests pass
Phase 1 + 2 e2e tests unchanged
6 new Phase 3 e2e tests pass ✅ all 6 plus the 7th (e2e-cancel)
--trace shows PLIC --- interrupt 9 (supervisor external, src N) --- markers ✅ verified

🤖 Generated with Claude Code

cyyeh added 13 commits April 27, 2026 11:48
8 tasks: edit binary skeleton + raw mode (1), file load + ^S save (2),
insert + backspace (3), horizontal arrows + ANSI redraw (4), vertical
arrows (5), e2e-editor (6), e2e-persist (7), trace polish + README +
deck updates (8). Closes Phase 3.
edit.zig is a stub today: enter raw mode, read bytes, exit on ^X,
restore cooked mode. No file load, no save, no rendering yet — but
it proves the raw-mode in/out dance works end-to-end against the
3.E console line discipline.
edit /etc/motd now loads the file into content[0..content_len] before
entering raw mode, and ^S re-opens the path with O_WRONLY|O_TRUNC|
O_CREAT and writes the buffer back. With no insert/delete logic yet,
^S^X is a no-op round-trip that leaves the file byte-identical.
Printable bytes (and \n / \r normalized to \n) insert at the cursor
offset, shifting the tail right and bumping cursor. Backspace shifts
the tail left, decrementing cursor. Buffer-full inserts drop silently;
backspace at offset 0 is a no-op. Cursor still moves only via insert
(no arrow keys yet — Tasks 4-5).
A 2-state ESC parser ingests ESC [ A/B/C/D. This task wires C (right)
and D (left); A/B are accepted but no-op until Task 5. After every
content/cursor mutation, redraw() emits \x1b[2J\x1b[H to clear, prints
the full buffer, then \x1b[<row>;<col>H to land the terminal cursor
at the byte-offset's row/col — both 1-based per ANSI semantics. Row/col
arithmetic walks newlines from the start of the buffer.
ESC [ A and ESC [ B move the cursor to the same column on the
previous/next row, clamping to the target line's length. lineStart
and lineEnd walk newlines forward/backward from a given offset.
The motd demo is one line so this is defensive plumbing, but it
makes edit usable on multi-line files.
editor_input.txt is a 43-byte binary fixture that drives the canonical
demo session: edit /etc/motd → 2× right-arrow → Y → ^S → ^X → cat
/etc/motd → exit. The harness copies shell-fs.img to zig-out/
editor-test.img so the editor's save doesn't mutate the build artifact,
spawns ccc with --input + --disk, captures stdout, and asserts the
discriminating landmark "$ cat /etc/motd\nheYllo from phase 3\n"
appears (proves the editor wrote the file and cat read it back through
a fully-restored cooked-mode shell).
Two-pass test: pass 1 writes /etc/motd via "echo replaced > /etc/motd";
pass 2 (a fresh ccc invocation on the SAME --disk image) cats
/etc/motd and the harness asserts "replaced\n" appears after the
prompt. Proves the kernel's bwrite path actually mutates the
host-backed block device file and that pass 2 reads it via a fresh
kernel + bufcache instance — the only state surviving between passes
is the on-disk image. Uses cooked-mode echo (independent of edit.zig)
so persistence regressions can't be masked by editor regressions.
Bumped README to "Phase 3 complete" with a Plan 3.F summary block;
added kernel-edit / e2e-editor / e2e-persist rows to the build
commands table; added edit.zig + the new e2e fixtures to the layout.
Deck: replaced the "Next · plan 3.f" panel with a ✓ 3.F status row
matching the 3.A-3.E rows above; flipped "Phase 3 · underway" to
"Phase 3 · complete" on the closing chapter; added a Ch 3.F slide.
Trace eyeball pass over the editor session showed the existing
markers are consistent end-to-end — no formatter changes needed.
Code review caught two issues in the index.html updates:

1. The new ✓ 3.F status row was placed AFTER </div> closing check-list,
   leaving it as a stray sibling of check-list inside two-col rather
   than as the 6th row alongside 3.A-3.E. Moved it inside check-list.

2. The new Ch 3.F slide used bare <code> tags in its body and caption
   instead of <code class="inline"> — inconsistent with every other
   slide's body text. Added the inline class to all 12 instances.
… column

Final-review nits caught two cosmetic issues:

1. zig fmt --check failed on src/kernel/user/edit.zig — the compact
   "0x08, 0x7F => { backspace(); redraw(); }," switch arms got
   expanded to multi-line by the formatter. Ran zig fmt to match the
   neighbor files' style.

2. The epilogue's <div class="two-col"> kept "grid-template-columns:
   1.25fr 1fr" even after the right-column "Next · plan 3.f" panel
   was replaced by the in-checklist 3.F row. Collapsed to 1fr so the
   reserved right column doesn't leave wasted whitespace.
Phase 3 spec §Definition of Done's "^C in the shell cancels a
foreground program (proves kill-flag)" bullet had no automated test
through Plan 3.F. The code path was fully wired (console.feedByte
catches 0x03 → proc.kill(fg_pid) → killed flag set → console.read
returns -1 → syscall dispatch calls proc.exit) but only verified by
code inspection.

This adds a 10-byte fixture "cat\n\x03exit\n" and a harness that
asserts the landmark "cat\n^C\n$ exit" appears in stdout after the
ccc run — proving cat actually got killed, the shell returned to its
prompt loop, and exit cleaned up normally.
The e2e-cancel test added in commit ad65594 closes Phase 3 §DoD's
"^C cancels foreground program" bullet, but the README's Plan 3.F
summary block and the deck's ✓ 3.F row + Ch 3.F slide didn't yet
mention it. This commit extends those doc surfaces so the kill-flag
chain is documented alongside the editor and persistence stories.
@cyyeh cyyeh merged commit beb5790 into main Apr 27, 2026
3 checks passed
@cyyeh cyyeh deleted the phase3-plan-f branch April 27, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant