Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:
- D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
invisible until moved across by hand.
- Development happens from Windows and from Linux, and each looks in a
different place for the same checkout.
Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.
The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
2.5 KiB
| name | description | metadata | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| schunix-framebuffer-device | /dev/fb0 is a real device with the Linux fbdev ABI; nothing may assume where the framebuffer is. Test with bootcheck.ps1 -Fb and tools/fbshot.ps1 |
|
/dev/fb0 (major 2) is a real framebuffer device as of 2026-08-04:
kernel/dev/fb.c (read/write by byte offset, plus fb_ioctl), declared in
include/fb.h. Before this it was wired to { nullread, nullwrite } — writing
pixels to it succeeded and did nothing.
include/fb.h holds Linux's fbdev request numbers and struct layouts
(FBIOGET_VSCREENINFO 0x4600, fb_var_screeninfo 160 bytes,
fb_fix_screeninfo 80 bytes). Same reasoning as
schunix-linux-open-flags: a shared number must mean the shared thing, or a
program gets a differently-shaped answer to a request it thought it
understood.
Nothing outside vga.c may assume where the framebuffer is. Ask
vga_fb_phys(), vga_fb_size(), vga_fb_stride(), vga_fb_bpp(),
vga_fb_ptr(). Three places used to hardcode 0xfd000000, two of them also
hardcoding 800*600*4 — right only on QEMU stdvga at one resolution.
Two traps that cost time here:
setupkvm()runs beforevga_init(), so it cannot know the framebuffer address. It used to identity-map one anyway, user-writable, into every process ever created. That mapping is now gone entirely — nothing used it, since userspace reaches the framebuffer throughmmap("/dev/fb0").sys_ioctlanswers every non-tty request with 0. Any new device ioctl must be routed before that catch-all, or the caller is told "success" and reads an untouched buffer — a 0x0 screen that looks like a driver bug rather than a missing one.
To check it:
pwsh -File tools/bootcheck.ps1 -Fb # 9 assertions on the reported geometry
pwsh -File tools/fbshot.ps1 -Mode fill # gradient via mmap, screendumped
pwsh -File tools/fbshot.ps1 -Mode bars # colour bars via write()
cmd/fbinfo is the program those drive. fill goes through mmap and bars
through the device's write() on purpose: when one works and the other
does not, the failure says which half broke.
Serial output cannot catch a stride bug — a framebuffer written with the wrong
row pitch reports every byte accepted and merely shears the picture. That is
why fbshot takes an actual screenshot; see
schunix-verify-by-driving-shell. Convert with convert in the WSL box,
not magick.