Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

os: block reboot while USB live media still attached, blink warning

Cross-device flash (e.g. USB→NVMe) leaves the freshly-flashed kernel on
the internal disk but the firmware will still pick the USB first at the
next boot unless the stick is pulled. Previously the screen just showed
a static "remove USB before rebooting!" notice and still accepted 'y',
so it was easy to reboot onto the stale kernel.

Now the y/enter shortcut is no-op'd (with a negative-feedback tone)
while the boot USB is still enumerated in flashTargets. The warning
blinks between amber (unplug USB) and red (reboot blocked). Since
flashTargets re-enumerates every frame, the moment the stick is pulled
the warning flips to a green "✓ USB removed, safe to reboot" and the
shortcut unlocks — no polling, no stale state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+38 -4
+38 -4
fedac/native/pieces/os.mjs
··· 66 66 // Reboot confirmation: y = reboot, n = back to prompt 67 67 if (state === "confirm-reboot") { 68 68 if (e.is("keyboard:down:y") || e.is("keyboard:down:enter") || e.is("keyboard:down:return")) { 69 + // Block reboot if USB live media still present on a cross-device flash — 70 + // firmware will boot the stale USB kernel instead of the freshly-flashed one 71 + const targets = system?.flashTargets || []; 72 + const tgt = targets[flashTargetIdx]; 73 + const flashedToBoot = !tgt || tgt.device === system?.bootDevice; 74 + if (!flashedToBoot) { 75 + const usbStillAttached = targets.some(t => t.removable && t.device === system?.bootDevice); 76 + if (usbStillAttached) { 77 + sound?.synth({ type: "square", tone: 180, duration: 0.14, volume: 0.12, attack: 0.003, decay: 0.12 }); 78 + return; 79 + } 80 + } 69 81 state = "shutting-down"; 70 82 shutdownFrame = 0; 71 83 sound?.synth({ type: "triangle", tone: 784, duration: 0.1, volume: 0.15, attack: 0.003, decay: 0.08 }); ··· 436 448 const targets = system?.flashTargets || []; 437 449 const tgt = targets[flashTargetIdx]; 438 450 const flashedToBoot = !tgt || tgt.device === system?.bootDevice; 451 + // USB still attached? flashTargets re-enumerates each frame so removal is live 452 + const usbStillAttached = !flashedToBoot && 453 + targets.some(t => t.removable && t.device === system?.bootDevice); 454 + const rebootBlocked = usbStillAttached; 439 455 if (!flashedToBoot) { 440 - ink(255, 180, 60); 441 - write("remove USB before rebooting!", { x: pad, y: stateY + 80, size: 1, font }); 456 + if (usbStillAttached) { 457 + // Blink warning until USB is removed 458 + const blink = Math.floor(frame / 12) % 2 === 0; 459 + if (blink) { 460 + ink(255, 180, 60); 461 + write("⚠ unplug USB before rebooting", { x: pad, y: stateY + 80, size: 1, font }); 462 + } else { 463 + ink(255, 60, 60); 464 + write(" reboot blocked ", { x: pad, y: stateY + 80, size: 1, font }); 465 + } 466 + } else { 467 + // Live media removed — clear the warning, show ready state 468 + ink(100, 220, 120); 469 + write("✓ USB removed, safe to reboot", { x: pad, y: stateY + 80, size: 1, font }); 470 + } 442 471 } 443 472 444 473 const hintY = flashedToBoot ? stateY + 80 : stateY + 94; 445 - ink(60, 200, 80); 446 - write("y: reboot to new os", { x: pad, y: hintY, size: 1, font }); 474 + if (rebootBlocked) { 475 + ink(120, 80, 80); 476 + write("y: disabled (remove USB)", { x: pad, y: hintY, size: 1, font }); 477 + } else { 478 + ink(60, 200, 80); 479 + write("y: reboot to new os", { x: pad, y: hintY, size: 1, font }); 480 + } 447 481 ink(140, 100, 80); 448 482 write("n: back to prompt", { x: pad, y: hintY + 14, size: 1, font }); 449 483