Records actual recipes bumped in 0.2.5 on 2026-07-02:
- Qt stack 6.8.2/6.11.0a1 -> 6.11.1 (all 6 sub-recipes, verified BLAKE3)
- Wayland/DRM/Input/expat/seatd to upstream latest stable (8 recipes,
verified BLAKE3)
- KWin 6.3.4 -> 6.7.2 + kdecoration 6.3.4 -> 6.7.2 + konsole 24.08.3
-> 26.04.3
Plus structural fix: created the missing qtshadertools recipe so the
qtdeclarative dependency chain resolves.
Documents what was deliberately NOT done:
- KF6 6.10 -> 6.27.0: 38 frameworks, 17-minor delta. Per AGENTS.md
patch-governance, no commit that bumps recipe.toml URLs without
first rebasing the local patches can be made honestly. Rebase
work (17-minor * 38 recipes) is multi-day and recorded as open.
- Mesa 24.0.8 -> 26.1.4: blocked on redox-os/mesa fork rebase
(0.3.0 work).
Includes the rebase order for the next session that plans to run
'make all CONFIG_NAME=redbear-full' against the bumped pins.
Plan-only artifact for the 0.2.5 graphics path. No recipe.toml changes,
no build attempted. Documents:
- Per-recipe upstream-latest-stable versions resolved via download.qt.io,
download.kde.org, gitlab.freedesktop.org, and git ls-remote on
invent.kde.org projects (no human guessing).
- Pre-build actions required (re-pull, re-blake3, validate-patches).
- Patch surface to re-evaluate across 17-minor KF6 bump and 1-minor
KWin bump.
- Mesa fork situation (24.0.8 vs 26.1.4): freeze at current fork,
rebase is 0.3.0 work.
- Freeze-when-green criteria for cutting 0.2.5.
- Out-of-scope items (Plasma workspace, real libepoxy, etc.).
Decisions in this plan are reversible; no recipe.toml field has been
modified. All work-in-progress from prior session is preserved as
uncommitted files in the working tree.
Prepend an UPDATE block at the top of the plan document recording:
- The 8 commits that landed Phase 0c (futex sharding, per-CPU run
queues, vruntime, work stealing, load balancing, cache-affine,
initial placement, NUMA topology, proc scheme handles, fadt fix)
- The upstream-redox kernel audit finding (upstream has none of
these features; local fork is sole implementation)
- A plan-vs-actual state table showing which claimed 'missing'
features are now present
- The kernel-side Phase 0c is complete; remaining work is
relibc-side (Phase 0e) and futex-REQUEUE/PI/robust (Phase 1)
The detailed §1–§9 analysis is preserved unchanged as historical
record. The status column 'Missing' in §1 should be re-read as
'now present in local kernel fork, pending relibc userspace wiring.'
cargo check now exits 0 with 0 errors in the local kernel fork.
The working tree had accumulated git-tracking drift across the
local/sources, local/recipes/*/source, and local/reference trees.
Restored:
- local/sources/libredox: add missing 160000 gitlink at
d01da350 (submodule/libredox). The .gitmodules entry already
declared this fork; the parent tree entry was missing so a
fresh clone of the parent would not pull the libredox source.
- .gitignore: mark the four local/recipes/*/source build-cache
trees (uutils-tar, ninja-build, sddm, sddm/source-pristine)
and the two local/reference/* entries (linux-7.1, seL4) as
ignored. These are build caches and external references, not
durable Red Bear code. The durable code for the four recipes
is recipe.toml + the corresponding patch (redox.patch).
- Note in .gitignore: do not extend local/recipes/**/source to
a blanket rule, because ~150 Red Bear fork recipes do keep
their durable source under local/recipes/<name>/source/.
Removed six broken 160000 gitlinks:
- local/recipes/archives/uutils-tar/source (e4c2affa...): on-disk
working tree was a self-clone of RedBear-OS; gitlink pointed to
a non-existent commit in the parent object database.
- local/recipes/dev/ninja-build/source (d829f42b...): gitlink
was a dangling commit on a diverged branch that has since been
rewritten; the on-disk HEAD is upstream v1.13.1 (79feac0) which
the recipe re-fetches via recipe.toml anyway. The 6.4MB
embedded .git directory was also removed.
- local/recipes/kde/sddm/source (63780fcd...): build cache for
sddm 0.21.0 re-fetched via recipe.toml. The 11MB embedded
.git directory was also removed.
- local/recipes/kde/sddm/source-pristine (63780fcd...): empty
placeholder, build cache. Removed.
- local/reference/linux-7.1 (ab9de95c...): external Linux
reference tree, gitignored by size. The on-disk directory
is preserved per AGENTS.md 'NEVER delete the reference tree'.
- local/reference/seL4 (a0b4f2d2...): empty placeholder,
gitignored.
Removed untracked pollution at repo root:
- kernel (empty 0-byte file)
- qqmljsgrammar.cpp, qqmljsgrammar_p.h, qqmljsparser.cpp,
qqmljsparser_p.h (393KB total: build artifacts that escaped
a qtdeclarative build into the working tree root; they belong
inside the recipe source tree, not at the parent level)
Added:
- local/docs/MULTITHREADING-COMPREHENSIVE-ASSESSMENT-AND-PLAN.md:
comprehensive multi-threading audit and implementation plan
covering kernel scheduler, kernel futex, syscall ABI, relibc
pthreads, and userspace threading correctness. Will drive
the next implementation cycle after the git tracking work
is wrapped.
After this commit:
- 9 submodule entries in HEAD, all of local/sources/* forks.
- All previously-existing 8 fork submodules unchanged.
- libredox is now durable across clones (was previously lost).
- No untracked files at root.
- No dangling or self-referencing gitlinks.
Per local/AGENTS.md § SINGLE-REPO RULE: the Red Bear OS project lives
in exactly one git repository (vasilito/RedBear-OS). Per-component
Gitea mirrors (redbear-os-base, redbear-os-kernel, redbear-os-installer,
redox-drm, userutils, libredox, libpciaccess, ctrlc, syscall, sysinfo)
have been redirected or deleted.
For each per-component repo with source content, the working-tree HEAD
was pushed as a 'submodule/<component>' branch on RedBear-OS:
- submodule/base
- submodule/bootloader
- submodule/installer
- submodule/kernel
- submodule/libredox
- submodule/redoxfs
- submodule/relibc
- submodule/syscall
- submodule/userutils
The .gitmodules entry for local/sources/kernel is now redirected to the
canonical repo with branch = submodule/kernel. The other submodule
.gitmodules entries remain to be added in a follow-up.
Empty per-component repos (ctrlc, libpciaccess, redox-drm, sysinfo) had
no source content; their gitlinks in the index are removed in a
follow-up commit.
Unrelated per-component repos that were not Red Bear components
(ctrlc, syscall, sysinfo — possibly unrelated personal projects) were
deleted in the bulk cleanup.
Gitea state under vasilito/ is now exactly: RedBear-OS, hiperiso.
Adds:
- local/scripts/redirect-to-submodules.sh
- local/scripts/delete-per-component-repos.sh
Updates:
- .gitmodules (kernel → RedBear-OS#submodule/kernel)
- local/AGENTS.md (SINGLE-REPO RULE status, migration procedure)
- local/docs/BUILD-SYSTEM-IMPROVEMENTS.md §11 (resolved)
- local/docs/QUIRKS-AUDIT.md (drop dead links)
- local/docs/SLEEP-IMPLEMENTATION-PLAN.md (mark historical)
- CHANGELOG.md (mark historical references)
Documents the full S3 state machine, modeled after
Linux 7.1's `arch/x86/kernel/acpi/wakeup_64.S` and
`arch/x86/kernel/acpi/sleep.c`. The S3 round-trip
is now fully wired:
1. acpid's enter_sleep_state(3) does the AML prep
(\\_TTS(3), \\_PTS(3), \\_SST(3))
2. acpid's kstop_enter_s3(0) writes the kernel's
s3_trampoline address to FACS.xfirmware_waking_vector
via the new SetS3WakingVector AcPiVerb
3. acpid writes 's3<SLP_TYP>' to /scheme/sys/kstop
4. kernel stop::enter_s3 reads S3_SLP_TYP, writes
SLP_TYP|SLP_EN to PM1a_CNT
5. firmware enters S3
6. on wake, firmware jumps to FACS.waking_vector
(the s3_trampoline)
7. kernel s3_trampoline restores state, jumps to
kmain_resume_from_s3
8. acpid receives kstop_reason=3, runs the standard
S3 wake AML sequence (\\_SST(2) -> \\_WAK(3) ->
\\_SST(1))
Hardware-agnostic: works on any x86_64 system with
standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
The status table at the top of this file is also updated
to reflect the latest Phase II.X.W completion and the
Phase K deferral (submodule conversion of remaining local
sources).
Update the SLEEP-IMPLEMENTATION-PLAN.md to reflect Phase J
completion: the local libredox fork and local syscall fork
are now both in place, the [patch.crates-io] and
[patch.'<URL>'] overrides are correctly wired in both
the base and kernel workspaces, and the typed-AcpiVerb
path (EnterS2Idle / ExitS2Idle) is the primary path.
The kstop string-arg path remains as a fallback for
older acpid builds. Both paths work end-to-end; the
build succeeds; the ISO is produced.
Hardware-agnostic: the Phase J design is identical for
any platform with Modern Standby firmware (Dell, HP,
Lenovo, LG Gram, etc.).
Phase I (LG Gram 16 (2025) / Arrow Lake-H S-state support)
is complete and built. The plan doc captures:
* Status table: which subsystems are done (acpid AML, kernel
kstop handler, redbear-quirks LG Gram flags) and which
limitations remain (S3 resume trampoline, s2idle wake
interrupt handler — both Phase II).
* Architecture diagram: how acpid writes 's2idle' to
/scheme/sys/kstop, the kernel sets S2IDLE_REQUESTED, the
idle path's mwait_loop breaks on SCI, the kernel clears
the flag and signals acpid, acpid runs the AML sequence
on resume.
* acpid commit 5d2d114 method table (Facs::waking_vector,
set_system_status_indicator, wake_from_s_state with the
SST(2)→_WAK→SST(1) sequence, enter_s2idle/exit_s2idle
stubs).
* Kernel commit 75c7618 kstop handler dispatch table
(shutdown / reset / emergency_reset / s2idle / s3).
* Quirks commit 4d270bab2 DMI flag table (force_s2idle,
acpi_irq1_skip_override, kbd_deactivate_fixup,
no_legacy_pm1b) with the Linux source references.
* Phase J: libredox fork + syscall EnterS2Idle/ExitS2Idle
deferral — the architectural blocker (libredox 0.1.17
has its own vendored redox_syscall dep; [patch.crates-io]
doesn't reach transitive deps). The patch file
local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch
is preserved as a durable artifact for Phase J.
* Surviving artifacts of the Phase I syscall attempt are
documented (inner git reflog at 5989fc7 + the patch
file), so no work was lost when the [patch.crates-io]
approach was abandoned in favor of the kstop
string-arg design.
Hardware-agnostic: the same plan applies to Dell, HP,
Lenovo systems. The LG Gram specifics are just one
target.
- AGENTS.md: add cache system to STRUCTURE, WHERE TO LOOK, BUILD FLOW,
BUILD COMMANDS (--force-rebuild), and CONVENTIONS (dep_hashes.toml,
binary store restore, package_groups syntax)
- CHANGELOG.md: comprehensive entry for Phase 1-3 + kernel MWAIT +
ninja-build Redox support
- local/AGENTS.md: note installer fork adds package groups support
- BUILD-CACHE-PLAN.md: fix TOML syntax (underscores not hyphens),
update all phases to COMPLETE with implementation details, add cache
flow diagram, add verification results
Phase 1 — Hash-based cache invalidation:
- DepHashes struct: BLAKE3 hash of each build dep stored in dep_hashes.toml
- collect_current_dep_hashes(): reads blake3 from dep .toml metadata
- dep_hashes_changed(): compares stored vs current hashes
- Replaces mtime comparison as primary cache invalidation check
- Mtime fallback preserved for backward compatibility (no dep_hashes.toml)
- --force-rebuild CLI flag bypasses cache entirely
Phase 2 — Binary store cache lookup:
- repo_builder publishes dep_hashes.toml alongside .pkgar/.toml in repo/
- When target/ is missing but repo/ has the package, restores stage
artifacts by extracting pkgar, copying toml + dep_hashes.toml
- Auto-generates auto_deps.toml from repo depends field
- Only applies to non-remote, non-force-rebuild builds
See local/docs/BUILD-CACHE-PLAN.md for full architecture.
- CHANGELOG.md: added Phase E entry describing the new
transition_to_s_state / wake_from_s_state / enter_sleep_state
methods on AcpiContext, and the opt-in DMAR init with hard
cap. Includes the final gap-closure status table showing
9 closed, 1 closed-in-part, 2 still open (both require
hardware-specific work).
- local/docs/ACPI-FORK-SYNC-STRATEGY-2026-06-30.md: added
Phase E outcome section with the changes applied and
out-of-scope items.
- CHANGELOG.md: added comprehensive 2026-06-30 entry covering
the full ACPI fork-sync (Phases A-D) and the redbear-sessiond
consumer port. Lists the 7 critical gaps that are now fully closed
and the 2 still open (DMAR + _WAK infrastructure).
- local/docs/ACPI-FORK-SYNC-STRATEGY-2026-06-30.md: added Phase D
outcome section with the Linux 7.1 cross-reference findings
(acpi_enter_sleep_state pattern), the changes applied to acpid
and redbear-sessiond, and the final gap-closure table.
- local/docs/ACPI-IMPROVEMENT-PLAN.md: updated "Current Truthful
Status" to reflect that acpid now follows the Linux 7.1 sleep
pattern with _PTS/_SST evaluation, thermal/power enumeration
works, AML mutex is real, parse_lnk_irc validates ranges, and
S5 works end-to-end. S1-S4 paths still need _WAK + wakeup
vector + P-state preservation (Gap #4b scaffolded but not
implemented). DMAR init still disabled (Gap #2 needs real-HW
investigation).
- local/docs/boot-logs/README.md: added
REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md to the inventory.
Also:
- Removed scratch file local/docs/ACPI-FIX-PLAN-2026-06-30.md
(superseded by the longer ACPI-FORK-SYNC-STRATEGY-2026-06-30.md).
Updates six documentation files to point readers to the new
input-stack observability evidence and changelog entry. No
content claims are altered — only "see also" pointers and inventory
updates.
- CHANGELOG.md: new 2026-06-30 entry documenting the input-stack
fix (commit de9d1f4 in local/sources/base/), the git server
docs rewrite (commit 0c60adc6b), and the build-system
hardening addendum (commit 41045fd2f). Includes the verified
end-to-end interactive login evidence from the rebuilt ISO.
- local/docs/boot-logs/README.md: add the new
REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md file to the
inventory table.
- local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md:
see-also pointer in the inputd validation deliverables
section.
- local/docs/REDBEAR-BOOT-EXPERIENCE-PLAN.md: see-also pointer
in the bootanim handoff section, noting that step 6
(REBIND_DISPLAY drm to inputd) now has companion
observability via the new inputd startup log.
- local/docs/USB-IMPLEMENTATION-PLAN.md: see-also pointer in
the HID producer modernization section, noting that the fix
lets operators distinguish "usbhidd dead" from
"usbhidd alive but not enumerated by XHCI".
- local/docs/GREETER-LOGIN-IMPLEMENTATION-PLAN.md: see-also
pointer in the service wiring section, noting that the greeter
can rely on the new inputd/ps2d startup lines being present
before inputd -A 3 runs.
Update REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md with the actual
runtime verification evidence captured at 2026-06-30T00:06:16Z:
- Both new startup log lines appear in initfs at the exact source
line numbers (@inputd:661, @ps2d:96), proving the fix is baked
into the running image.
- End-to-end interactive login succeeded: operator typed root +
password at the Red Bear login: prompt and reached a
redbear# shell (Red Bear OS v0.2.4 "Liliya").
This conclusively confirms the diagnosis: the input chain
(ps2d -> inputd -> fbcond -> getty -> login -> shell) was working
all along. The previous "freeze" was a test-harness issue (no
keystrokes sent to the guest), not an OS bug. The new log::info!()
lines make the input stack health visible in future boot logs.
Two documentation changes:
1. New file local/docs/boot-logs/REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md
captures the 2026-06-30 diagnosis of why the mini boot appeared to
freeze at the login prompt. Records:
- The actual root cause (test harness not injecting keystrokes, not
an OS bug — ps2d/inputd were working silently).
- The committed fix (de9d1f4 in local/sources/base/ adds two
log::info!() startup messages so operators can verify the input
stack is alive from the boot log).
- The expected post-fix boot log lines and how to interpret them.
- Verification status (source-inspected; clean post-fix QEMU boot
pending due to slow bootloader streaming under -nographic).
2. Addendum appended to local/docs/BUILD-SYSTEM-IMPROVEMENTS.md
documenting four build-system ergonomics issues observed during
the diagnosis session:
- #11: local/sources/base/ inner git repo origin points to
upstream Redox instead of Red Bear gitea.
- #12: outer Red Bear repo cannot show inline diffs for the
nested local/sources/base/ git repo (submodule pointer dirty).
- #13: no preflight warning for stale local-fork source (a
4-line edit caused a 30+ min rebuild with no advance notice).
- #14: -nographic + OVMF boot is too slow for time-budgeted
post-fix QEMU verification; recommend BIOS + KVM path.
Both items are S-sized and could be picked up in any future hardening
session. No code changes in this commit.
Following a full repo-wide grep and source review of all quirks
files (mod.rs, pci_table.rs, usb_table.rs, dmi.rs, toml_loader.rs,
pci.rs, i2c-hidd/quirks.rs), this companion to QUIRKS-SYSTEM.md
documents the current ground-truth state of the subsystem as of
2026-06-29.
Findings:
- 11 Rust + 6 C consumers of the redox-driver-sys quirks engine
- 1 separate DMI quirks engine in i2c-hidd (uses RON, not TOML)
- 6 dead-code entry points that return empty flags:
* lookup_pci_quirks_full actions (Vec::new)
* lookup_xhci_controller_quirks_full (stub)
* lookup_hid_quirks (stub)
* load_dmi_acpi_quirks (stub)
* load_platform_dmi_quirks (stub, empty PLATFORM_RULES)
* load_drm_panel_orientation (empty PANEL_ORIENTATION_TABLE)
- 4 PciQuirkFlags bits (22-25) have no entry in PCI_FLAG_NAMES,
so they cannot be set via /etc/quirks.d/*.toml
- DMI is consumed in two incompatible ways:
* redox-driver-sys reads /scheme/acpi/dmi as a flat key=value file
* i2c-hidd reads /scheme/acpi/dmi/{system_vendor,...} as per-field
subpaths, and matches case-insensitive
- redbear-quirks stages 10 TOML files to /etc/quirks.d/; the
TOML layer is fully live (this corrects an earlier claim
that it was dormant)
The audit concludes that the system is correctly initialized and
utilized per its design. No redesign is needed; the existing
QUIRKS-SYSTEM.md is mostly accurate but understates how many
defined functions return empty data and how many flag names
are missing from the TOML schema.
Reference analysis for Red Bear OS integration based on running the
latest CachyOS desktop ISO (28 Jun 2026) under QEMU/KVM. Documents
the hardware-enumeration and kernel-init sequences a reference Linux
distro produces on the i440FX + PIIX machine type that Red Bear OS
also targets, with line-by-line mapping to Red Bear OS subsystems
(pcid, ided, e1000d, vesad, xhcid, hwd/acpid).
- local/docs/CACHYOS-INTEGRATION.md: Cross-cutting analysis
(executive summary, hardware inventory, ACPI table coverage,
PCI quirks, boot-phase ordering, init system comparison, action
items).
- local/docs/boot-logs/cachyos-kernel-20260629-0520.log: Captured
441-line dmesg-grade log from the CachyOS kernel boot (SeaBIOS
handover through ACPI, PCI, USB, ATA, network enumeration and
up to a rootfs shell prompt).
- local/docs/boot-logs/cachyos-boot-20260629-0520.md: Pointer
document with the capture command and the rationale for the
indirect-kernel invocation (the QEMU + CachyOS + KVM boot stalled
at the ISOLINUX EDD probe when run from CD; bypassing with
-kernel/-initrd and an explicit console=ttyS0 earlyprintk command
line recovered the full log).
Sources used: https://cachyos.org/ (release info) and the on-disk
ISOLINUX + archiso boot path extracted from
cachyos-desktop-linux-260628.iso.
- msr.rs: add all Intel RAPL MSR addresses (0x606-0x64D) and AMD Zen
equivalents (0xC0010299-0xC001029B), RaplUnit struct for unit register
parsing with energy_to_uj/power_to_w conversion, read_rapl_energy()
and read_rapl_energy_uj() functions
- acpi.rs: read_rapl_package_energy() now tries MSR first (Intel then
AMD PKG energy MSRs) with unit-based µJ conversion, falls back to
Linux powercap sysfs
- local/docs/RAPL-IMPLEMENTATION-PLAN.md: comprehensive 3-phase plan
based on Linux 7.1 kernel analysis, Intel SDM, Fuchsia RFC-0203
patterns. Documents P0 blocker: /scheme/sys/msr/ not implemented
in kernel
Adds §68 (v1.44 plan) and revises §67.7 (v1.43
deferred list) to reflect the v1.43 audit findings.
Key corrections captured in the doc:
- The Redox kernel ALREADY implements
sched_setaffinity and sched_getaffinity
(local/sources/kernel/src/syscall/process.rs:322-382).
Only the relibc POSIX <sched.h> wrapper is missing.
- The existing P7-pthread-affinity relibc patch
provides cpu_set_t, cpuset_to_u64, copy_u64_to_cpuset
— v1.44 can reuse these instead of duplicating.
- Kernel pid=0 limitation is documented at
kernel/src/syscall/process.rs:336-338 as a TODO;
v1.44's UX is honest about this ('pin redbear-power's
own TUI to this process's CPU list, not the
highlighted process's').
- Per-thread CPU% is REJECTED for v1.44 because the
Redox proc scheme doesn't expose /proc/<pid>/task/<tid>/stat.
Same trap as v1.41 read_thread_io. Tracked as a kernel
follow-up, not a redbear-power feature.
- disk_history cap is REJECTED for v1.44 because the
natural bound on block device count (~4-8 typical)
makes the cap moot. Drive-by include it elsewhere.
Implementation plan documented:
1. relibc patch P12-sched-setaffinity.patch (~110 LoC,
reusing P7 helpers)
2. redbear-power affinity.rs module (~30 minutes)
3. main.rs key binding (capital A) + PID detail
popup integration (~1.5 hours)
4. 5-7 tests (round-trip, pid=0 limitation, parse/format,
integration against /proc/self/status)
5. Doc update §69 on what shipped
Effort estimate: ~1 working day, end-to-end. The
relibc patch alone is <3 hours given P7 reuse.
Downstream recipe impact audited (LOW):
- Mesa and xz both wrap sched_getaffinity in
defensive probes — they go from ENOSYS to
'current process mask works' (strict improvement,
no break).
No code changes — planning-only commit.
Add local/scripts/cleanup-build.sh - a git-aware cleanup script that
uses 'git ls-files' to whitelist tracked files before deletion.
Prevents the class of cleanup disasters that deleted local recipe
sources and local fork sources.
Update AGENTS.md with the new safe cleanup procedure.
Update CONSOLE-TO-KDE-DESKTOP-PLAN.md to v5.8 with the qtdeclarative
qfeatures.h fix and the new safe cleanup script.
The first item from the v1.42 deferred list: a
configurable LRU cap on the per-PID history maps.
On long uptimes with thousands of short-lived procs
(build servers, CI runners), the maps would grow
without bound, eventually consuming significant
memory. v1.43 caps the maps at 500 PIDs by default
and evicts the LRU entry on overflow.
The cap
- App::max_history_pids: usize (default 500)
- 0 = disable (reaper still prunes exited PIDs)
- Shared across the 3 per-PID maps (io_history,
cpu_history, rss_history). They are always
populated in lockstep so a per-PID CPU history
without the corresponding IO history would be
a 'ghost' entry that confuses the renderer.
- disk_history is NOT capped (keyed by disk name,
natural bound on block device count).
LRU tagging
- New App::pid_last_seen: BTreeMap<u32, u64>
- New App::refresh_tick: u64 (incremented on every
update_io_history call)
- We use a refresh counter, not Frame::count(),
because the history update happens during
refresh (not during render). Frame::count would
tag currently-visible PIDs rather than
recently-updated PIDs — a different (and
incorrect) notion.
Eviction algorithm
1. Increment refresh_tick
2. Reap exited PIDs from all 3 maps and
pid_last_seen
3. If pid_last_seen.len() > cap: sort by tick
ascending, take the first overflow entries,
remove from all 3 maps + pid_last_seen
4. Continue with the existing pipeline
Cost: O(n log n) per refresh, n bounded by 500.
At 500 PIDs: ~4500 comparisons per refresh,
<100µs. Memory budget: ~28 KB at cap, vs
unbounded growth without the cap (~5.5 MB at
100k PIDs).
Tests
- 3 new tests (eviction removes oldest, cap=0
disables, no-op under cap).
- 186/186 tests pass (was 183 in v1.42).
The improvement plan doc is also updated with §67
covering the v1.43 architecture, the cap policy,
the LRU tagging, the eviction algorithm, the
memory budget, and the v1.44 deferred list.
The next item from the v1.41 deferred list: read
/proc/<pid>/status:Cpus_allowed_list and display it as
both a single-char row indicator and a full expanded
list in the PID detail popup. htop parity.
Kernel format
The kernel emits the list as comma-separated ranges:
"0-3,5,7-11" means CPUs 0, 1, 2, 3, 5, 7, 8, 9,
10, 11
Cpus_allowed_list is the HARD affinity mask (settable
via sched_setaffinity(2)). v1.42 reads it because it
matches what an operator sees with 'taskset'.
New functions
- read_cpu_affinity(pid): parses the kernel string
- parse_cpu_list(s): public, testable parser
- format_cpu_list(ids): inverse of parse_cpu_list
- read_cpu_affinity_for_pid(pid): pub wrapper for the
PID detail popup
Two display modes
- Process panel row: '*' (subset), ' ' (all CPUs),
'?' (unknown). Single char so COMM stays visible.
- PID detail popup: full range string + expanded
Vec (truncated to 8 items on large machines).
New field on ProcessInfo
- cpu_affinity: Option<Vec<u32>>
Robustness
- Whitespace tolerated
- Out-of-order or duplicate IDs deduped and sorted
- Non-numeric chunks silently dropped
- Reversed ranges (start > end) silently dropped
- Empty input returns empty Vec (popup distinguishes
'no data' / None vs 'explicitly empty' / Some(empty))
Tests
- 13 new tests (11 in process.rs for parse/format/
read, 1 self-affinity test, 1 missing-pid test).
- 183/183 tests pass (was 170 in v1.41).
The improvement plan doc is also updated with §66
covering the v1.42 architecture, kernel format, the
two display modes, the parse/format inverse pair, and
the v1.43 deferred list.
The next item from the v1.40 deferred list: walk
/proc/<pid>/task/*/io for every process and sum
read_bytes and write_bytes across all TIDs. Surfaces
'is one thread of this 32-thread process hammering
disk?' which the process total hides.
Linux kernel attribution quirk
/proc/<pid>/io:read_bytes is the process total,
NOT the per-thread sum — the kernel attributes all
IO to the process even when threads initiate it.
So the two surfaces can match, diverge, or be
independently None depending on kernel version and
permission model. We never compare or subtract them.
New fields on ProcessInfo
- thread_io_read_kb, thread_io_write_kb (summed bytes)
- thread_io_read_rate_kbs, thread_io_write_rate_kbs
(delta-based rate via the same compute_rate_kbs
helper used for the process-total rates)
New sort modes
- ThreadIo: read+write total
- ThreadIoR: read only
- ThreadIoW: write only
The cycle reaches all 3 via a back-door arm of next()
that returns to Rss (not Name) to avoid breaking the
main loop.
New column in Process panel
T-IO between the per-thread rate and the MEM column.
Now 12 columns total. PID detail popup gets a
[thread_io] section that re-reads the task dir on
open for a current value.
Tests
- 6 new tests (3 in process.rs for read_thread_io,
3 for the sort modes + cycle).
- 170/170 tests pass (was 164 in v1.40).
The improvement plan doc is also updated with §65
covering the v1.41 architecture, the Linux kernel
attribution quirk, the new fields, the sort cycle
back-door, the cost analysis (~500 reads/sec at
typical desktop loads, ~7500 at 128-thread server
loads — well within budget), and the v1.42
deferred list.
The first item from the v1.39 deferred list: the user's
tab, sort mode, sort direction, tree mode, filter, and
fold set now survive a restart of redbear-power.
Architecture
- New module session.rs (separate from config.rs which
is read-only system-wide config).
- config.rs: behavior config (refresh interval, theme,
keybindings) — read once at startup, never written.
- session.rs: mutable per-user runtime state — read at
startup AND written on every tab change and on quit.
Storage
- $XDG_CONFIG_HOME/redbear-power/session.toml (preferred)
- ~/.config/redbear-power/session.toml (fallback)
- Writes are atomic: temp file + rename(). A crash
between write and rename leaves the prior session
intact.
Save hooks
- Every set_tab() call: tab is the high-signal event
the user explicitly opted into.
- On graceful quit (q/Esc): captures the final sort,
filter, and fold set.
Failure modes
- load() never errors. Missing file = defaults.
Corrupt file = defaults + one-line warning.
- save() never errors. Permission denied = eprintln!
warning. The next launch reads the prior session
(or defaults) and starts from there.
Tests
- 6 new tests in session.rs (round-trip, missing-file,
malformed-toml, atomic-save, default-state, end-to-end
via App::save_session()).
- 164/164 tests pass (was 158 in v1.39).
The improvement plan doc is also updated with §64
covering the v1.40 architecture, storage paths, save
policy, failure modes, and the v1.41 deferred list.
v1.39 lands three htop/btop parity features plus the
audit-fix discipline introduced in v1.37/v1.38:
1. Cursor preservation across sort
o and i keypresses no longer reset the cursor to row 0.
The cursor follows the currently-selected PID through the
re-sort. Implemented as App::remember_and_restore_cursor()
which walks the post-filter visible list. Three regression
tests: follows PID, respects filter, falls back when PID
exits.
2. Per-thread IO rate (T-IO column)
New ProcessInfo::io_per_thread_rate_kbs() returns the
aggregate IO rate divided by num_threads, surfacing
thread-pool pressure that's hidden in the aggregate. A
32-thread process at 320 KiB/s is the same as a 1-thread
process at 10 KiB/s in aggregate, but very different
in operator-relevant 'IO per worker' terms. Returns None
when num_threads <= 0 (data error, not '0 KiB/s per
thread' which would mislead the operator). Three unit
tests cover the divide, the missing-total case, and the
zero-threads case.
3. Process environ in PID detail
/proc/<pid>/environ read as NUL-separated KEY=VALUE
pairs, sorted by key for stable popup rendering.
Rendered as the first 8 vars in the PID detail popup
with a '(N variables)' header. htop F7 parity. Three
unit tests: parse self environ, missing PID, value
containing '=' (must split on FIRST '=' only).
The improvement plan doc is also updated with sections
56-63 covering v1.32 (sparklines) through v1.39
(per-thread IO + environ) since the doc previously
stopped at v1.31.
Test count: 158/158 pass (was 149 in v1.38.1).
Activates the v1.25-deferred 'persistent rate sparkline' future-use.
Each process in the Process tab now shows a 12-sample sparkline
of its IO rate history (last 78 seconds at the 6.5s process
refresh cadence).
- New App.io_history: BTreeMap<u32, VecDeque<u64>>
Per-PID history of raw f64-bit rate samples. BTreeMap for
stable iteration; VecDeque for O(1) push-back + pop-front.
- PROCESS_IO_HISTORY_LEN = 12 (12 samples * 6.5s = 78s of history)
- App::update_io_history() runs after sort_tree + apply_fold
on every process refresh. Three-pass algorithm:
1. Reap: drop history for PIDs that exited
2. Append: push new f64-bit sample for PIDs with known rate
(PIDs with None rate are skipped, no entry created)
3. Normalize: divide each sample by the per-history max,
scale to u8 0..=255. Separate pass so max is computed
once per history, not per sample.
- render::io_rate_sparkline(&[u8]) helper maps 0..=255 to
Unicode chars (\u2581\u2582... matches existing load sparkline)
- New 'IO-RATE' column in Process panel between RSS/VSZ and
COMM. 12 chars wide. Empty spaces for PIDs with no history
yet (first tick after startup).
- Why u64 storage of f64 bits: normalization needs the full
f64 range; clamping to u8 before normalize would lose
precision for high-rate PIDs.
Test count 117 -> 121 (+4):
- update_io_history_reaps_exited_pids
- update_io_history_normalizes_against_max (100/200*255=127.5
rounds to 128; 200/200*255=255)
- update_io_history_handles_all_zero (no div by zero)
- update_io_history_skips_pids_without_rate (None rate \u2192
no entry created; no panic)
Redox stripped binary: 4,201,320 bytes (+4 KiB from v1.30).
Memory: ~91 KiB for 600 PIDs (negligible).
Compile warnings: 55 (unchanged).
Notes:
- CPU% sparkline per process: defer (same pattern, separate work)
- RSS sparkline per process: defer
- Variable sparkline length: defer (header is 'IO-RATE' not
'IO-RATE 12' so a future change to PROCESS_IO_HISTORY_LEN
doesn't need a header update)
- Per-PID scaling (not global): each PID's max is 255. A
long-running PID at 5 KiB/s steady shows full bars; a
bursty PID that just started at 50 KiB/s also shows full
bars. Global scaling would flatten the long-running one.
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA755
Activates the v1.29-deferred cursor-navigation future-use. The
Process tab is now fully interactive: j/k and arrow keys move
the cursor, PageUp/PageDown scroll by 8 rows, the visible row
is bolded as the cursor, Enter and Space operate on the
cursor's PID.
- move_selection is now tab-aware:
PerCpu -> move_cpu_selection (old behavior)
Process -> move_process_selection (new)
other -> no-op
- move_process_selection clamps process_cursor to
[0, visible.len()-1] (saturating_add for big deltas)
- page_selection is also tab-aware (8 rows per page for Process)
- j/k hotkeys (vim-style) call move_selection
- visible_processes() helper extracted (deduplicates the
filter+collect logic that move_process_selection and
selected_pid both needed)
- selected_pid() now uses process_cursor directly (was using
table_state.selected() which was the Per-CPU widget's
selection — wrong tab indirection)
- theme::CURSOR: bold style for the cursor row (no background
color; background flickers on rapid style changes on some
terminals; bold is stable)
- Render layer applies theme::CURSOR when current_tab==Process
AND the visible_index matches process_cursor AND focused
Test count 111 -> 117 (+6 in new app::tests mod):
- move_process_selection_down_clamps_to_last
- move_process_selection_up_clamps_to_zero
- move_process_selection_empty_list_is_noop
- move_process_selection_respects_filter
- selected_pid_returns_none_when_empty
- selected_pid_returns_none_when_filter_excludes
make_app_with_processes(n) helper clears App::new()'s /proc
read first so the test fixtures don't mix with real procs.
Redox stripped binary: 4,197,224 bytes (+16 KiB from v1.29).
Compile warnings: 55 (unchanged).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA754
Activates the v1.27-deferred fold/expand feature. The tree
view from v1.27 is now interactive: pressing Space on a
parent row toggles whether its descendants are visible.
- New App.folded: BTreeSet<u32> (PIDs whose subtrees are
collapsed; stable iteration order for future debug dumps)
- New App.process_cursor: usize (Process-tab cursor; distinct
from table_state which tracks the Per-CPU tab)
- New process::apply_fold(processes, folded) -> Vec<ProcessInfo>
Hides descendants of any PID in . The fold target
itself stays visible. Roots are never hidden. Cycles
tolerated (sort_tree's visited set prevents infinite loops).
- Fold indicator in tree_prefix: \u25b6 for folded, \u25bc for
expanded, no marker for leaf rows
- Space keypress (in tree mode only) toggles fold on the
cursor's selected PID; flashes 'folded PID N' or
'unfolded PID N' (or 'has no children to fold' for leaves)
- sort_tree kept pure; apply_fold is a separate post-step
applied in app.rs after sort_tree
Test count 107 -> 111 (+4):
- apply_fold_empty_set_is_identity
- apply_fold_hides_descendants_of_folded_root (folds root)
- apply_fold_hides_subtree_of_folded_child (folds middle;
sibling of folded node stays visible)
- apply_fold_unfold_restores (toggle off)
Redox stripped binary: 4,180,840 bytes (-8 KiB from v1.28;
linker dedup'd some shared code).
Compile warnings: 55 (unchanged).
Notes:
- No cursor navigation yet (j/k, down/up). Default cursor
is row 0, so user can fold the first process but cannot
yet move down. Defer to v1.30.
- No persist of fold state across redbear-power restarts.
Would require a config file. Defer.
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA753
Closes the v1.23-deferred 'vsize_kb' future-use. Adds SortMode::VSize
that sorts by virtual size, and swaps the Process panel's MEM
column to show VSZ (instead of RSS) when that sort is active.
The column-swap pattern (the column being sorted IS the column
being shown) keeps the panel at 10 columns instead of growing
to 11. htop uses the same pattern: when you sort by a field,
that field's column expands to show the data. No new column
means no terminal-width pressure at 1280x720 framebuffer.
The 'ppid' field's #[allow(dead_code)] is also removed (now
actively read by sort_tree + tree_prefix from v1.27). Both
fields now have proper doc comments explaining their use
(vs the v1.23 'reserved for future use' placeholder).
VSZ is a virtual address-space metric (mmap'd libraries, heap,
stack, reserved-but-uncommitted) and is often much larger than
RSS. Useful for 'who is using the most address space' but NOT
for 'who is using the most physical memory' (use RSS for that).
This caveat is now documented in the field's doc comment to
prevent operator confusion.
Test count 105 -> 107 (+2):
- sort_by_vsize_descending (basic)
- sort_by_vsize_uses_vsize_not_rss (contract test: huge VSZ +
tiny RSS sorts above tiny VSZ + huge RSS; catches any
'optimization' that uses the larger of the two fields)
- sort_cycle and io_name_is_io updated for VSize
Redox stripped binary: 4,189,032 bytes (+4 KiB from v1.27).
Compile warnings: 55 (no net change; the 2 removed
#[allow(dead_code)] annotations cancel against 2 new
warnings that did not exist before because the fields were
only accessed from the parse path).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA752
Activates the v1.23-deferred 'ppid' future-use: parents render
above their children with ASCII tree connectors. The default
view is flat (no behavior change); the 'T' hotkey toggles
tree mode and flashes the status line.
Algorithm (in process::sort_tree):
1. Build pid -> index map
2. Group children by ppid (ppid -> Vec<index>)
3. Roots = ppid==0 or ppid not in pid set
4. Sort each sibling group by current SortMode (so e.g. RSS
sort still shows top-RSS child first within a parent)
5. DFS from each root, emitting parent + descendants pre-order
6. Defensive: append unvisited procs at end (cycle fallback)
Cycle protection: visited set; revisiting a PID stops recursion
(its children are still emitted once).
Render: tree_prefix(pid, ppid, all) returns
'' (root)
' \u2514\u2500 ' (last child)
' \u251c\u2500 ' (non-last child)
Walks ppid chain to compute depth (max 64 hops).
Status line: 'view: tree' shown when on; help text mentions 'T'.
Test count 101 -> 105 (+4):
- sort_tree_emits_parents_before_children (4-proc tree)
- sort_tree_handles_orphans (ppid not in list)
- sort_tree_handles_cycles (1->2->1 cycle)
- sort_tree_empty_input
Redox stripped binary: 4,184,936 bytes (+16 KiB from v1.26).
Compile warnings: 55 (unchanged).
Notes:
- vsize_kb still has #[allow(dead_code)]; will be activated in
a future memory-detail panel release.
- Tree is static (no fold/expand); defer to a v1.28 if needed.
- ppid's #[allow(dead_code)] can be removed in a follow-up
(now actively read by sort_tree and tree_prefix).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA751
Completes the v1.22 audit W2 cleanup. v1.23 deferred this for a
CHANGELOG note; this release documents the breaking change.
REMOVED (no callers anywhere in the source tree):
- ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)
Was a 1-line wrapper around read_with_cpu_pct_sorted(..., Rss)
that no caller actually used. Migration: call
read_with_cpu_pct_sorted(prev, dt, ncpu, SortMode::default())
inline (or just use ProcInfo::read() if RSS is fine).
- ProcInfo::available() -> bool
Was a pre-flight check ('is /proc mounted?') that no caller
used. read() already returns ProcInfo::default() when /proc
is absent, so the empty result is the same signal. Migration:
check !proc.is_empty() after a read, or call read() and
handle the empty case.
OTHER CHANGES:
- Removed unused 'use std::path::Path;' (was only used by the
removed ProcInfo::available).
- Updated read_with_cpu_pct_sorted doc comment to mention
'CPU% and IO rates' (reflects the v1.25 addition).
BREAKING: any external consumer of redbear-power's process module
that called either of these methods will fail to compile. The
recipe's own source (the only known consumer) is updated.
Test count: 101 (unchanged; removed methods were untested).
Compile warnings: 55 -> 54 (the unused Path import is gone).
Redox stripped binary: 4,168,552 bytes (unchanged; the removed
code was tiny and the linker dedup'd the wrapper body).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA750
Per-process IO is now also a throughput metric (KiB/s), not just
cumulative. Cumulative bytes favor long-lived processes regardless
of activity; rate is what operators actually want for 'what is
hammering the disk right now'.
- New fields on ProcessInfo: io_read_rate_kbs, io_write_rate_kbs
(Option<f64>; None when prev missing, current None, or dt<=0)
- New method: io_total_rate_kbs() (sum; same None semantics)
- New helper: compute_rate_kbs(prev, now, dt) -> Option<f64>
uses saturating_sub for clock-reset safety
- read_with_cpu_pct_sorted now also computes the two rate fields
(negligible cost: 2 subs + 2 divs per process per refresh)
- New SortMode variants: IoRate, IoReadRate, IoWriteRate
inserted in cycle after IoWrite
- name() returns 'IO/s', 'R/s', 'W/s' for status line
- New sort_by_io_rate_field() helper (Option<f64> partial_cmp)
- New format_rate_kbs() on ProcessInfo (KiB/s, MiB/s, GiB/s, TiB/s;
saturates negative to 0)
- New RATE column in the Process panel between IO and RSS
Test count 87 -> 101 (+14):
- 6 compute_rate_kbs edge cases (basic, None prev/now, dt<=0,
saturating underflow, idle = zero)
- 2 io_total_rate_kbs (sum, None)
- 2 sort-by-rate (total, read-pushes-missing)
- 4 format_rate_kbs (sub-KiB, 1 MiB, 1 GiB, negative)
- sort_cycle and io_name_is_io updated for new variants
Redox stripped binary: 4,168,552 bytes (+49 KiB from v1.24;
14 new tests + 2 sort modes + 2 fields + render column + 3 helpers).
Compile warnings: 55 (unchanged).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA749
htop has had separate read/write sort modes since 2.0; v1.22
conflated them via io_total_kb(). v1.24 splits SortMode::Io
into three variants so operators can find read-heavy (DB
servers) vs write-heavy (log shippers) processes.
- New variants: SortMode::IoRead, SortMode::IoWrite
- Cycle updated: Rss -> Cpu -> Io -> IoRead -> IoWrite -> Pid
-> Name -> Rss
- name() returns 'IO', 'IO-R', 'IO-W' for disambiguation
(shown in status flash on 'o' keypress)
- Extracted sort_by_io_field() helper: shared 4-arm comparator
for (Some, Some) descending, (Some, None) Less,
(None, Some) Greater, (None, None) Equal. Eliminates the
DRY violation of repeating the 4-arm match in three places.
- Sentinel semantics preserved: None still sorts below Some;
column still renders em-dash for unreadable /proc/[pid]/io
- Column header unchanged: 'IO' column shows per-process
total; sort direction is in the status line. Minimal change;
adding separate R/W columns would push the panel past 100
chars and lose comm truncation.
Test count 83 -> 87:
- sort_by_io_read_ignores_writes
- sort_by_io_write_ignores_reads
- sort_by_io_read_pushes_missing_to_bottom
- sort_by_io_write_pushes_missing_to_bottom
- io_name_is_io now also locks IO-R and IO-W strings
- sort_cycle and sort_cycle_includes_io updated for new cycle
Redox stripped binary: 4,119,400 bytes (-8 KiB from v1.23;
the helper dedup actually shrunk the binary).
Compile warnings: 55 (unchanged; all new variants are used).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA748
The v1.22 audit + htop cross-reference surfaced a real defect:
on Redox, daemons whose /proc/[pid]/io is not exposed (permission
denied, proc scheme gap) silently clustered at 0 B in the IO
column, indistinguishable from genuinely idle processes. This
made SortMode::Io unreliable for finding the real IO hogs.
This release promotes the IO column to a proper sentinel model:
- io_read_kb / io_write_kb change from u64 to Option<u64>
- io_total_kb() returns Option<u64>; None when either field is None
- SortMode::Io uses 4-arm match on (a_total, b_total):
* both Some -> descending by total
* Some/None -> known sorts above unknown
* None/None -> stable tie (input order)
- Render layer shows em-dash (\u2014) when total is None
- Single-pass /proc/[pid]/io parse replaces two-helper double-read
(read_io_file returns Option<(u64, u64)> in bytes; caller /1024s
to KiB so the None sentinel propagates end-to-end)
- #[allow(dead_code)] on ppid, vsize_kb with documented future use
(process tree view, memory detail panel) per project warning policy
Test count 80 -> 83:
- Replaced misleading 'io_total_saturates_on_underflow' (tested
normal sum) with 'io_total_saturates_at_u64_max' (genuine edge)
- Added 'io_total_returns_none_when_fields_missing'
- Added 'sort_by_io_pushes_missing_to_bottom'
- Added 'io_name_is_io' to lock the SortMode::Io.name() string
Compile warnings 56 -> 55 (the ppid/vsize_kb dead_code warning
is now suppressed; remaining 55 are pre-existing in other modules).
Redox stripped binary: 4,127,592 bytes (+4 KiB from v1.22).
Linux smoke test confirms em-dash renders for kscreenlocker_g,
kwin_wayland, tailscaled, polkit-kde-auth (all owned-UID procs
that the kernel hides /proc/[pid]/io from on this dev host).
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA747
Process tab gains a new IO column sourced from /proc/[pid]/io
read_bytes and write_bytes, summing them as a single sortable value.
- New fields on ProcessInfo: io_read_kb, io_write_kb
- New method: ProcessInfo::io_total_kb()
- New helpers: read_io_bytes, write_io_bytes (silent on failure;
/proc/[pid]/io may require CAP_SYS_PTRACE for owned UID)
- New SortMode::Io variant inserted into the cycle
(Rss -> Cpu -> Io -> Pid -> Name)
- Updated render header: VIRT replaced by IO (RSS preserved)
- 4 new unit tests; total 80 pass
- Redox stripped binary: 4123496 bytes
- Linux smoke test confirms opencode dominates IO, kscreenlocker_g shows 0.0 KiB
Docs: local/docs/redbear-power-improvement-plan.md \xC2\xA746
Wires the v1.20 SMART data module into the Storage tab UI.
Each disk now shows a health badge (✓ PASSED / ✗ FAILED / error).
Implementation:
- App.smart: SmartInfo field + 11-tick refresh (paired with Storage)
- Conditional refresh (if self.smart.available guard — avoids
re-running smartctl if we already know it's missing)
- render_storage_panel: 4 SMART badge states
1. !available → '(SMART: install smartmontools)'
2. health.passed → ' ✓ PASSED'
3. !health.passed → ' ✗ FAILED'
4. health.error → ' (SMART: <error>)'
Linux host smoke test (this dev host without smartctl):
- Each disk shows '(SMART: install smartmontools)' hint
- No panic, graceful degradation
- Storage tab still works (no regression)
Performance: smartctl subprocess ~5-50ms per disk, 3 disks = 15-150ms
per 11-tick refresh (5.5 sec), well within budget.
76/76 tests pass (no new tests — UI integration only).
Cross-compile SHA256: ed804710fa834f4453a236aa034d50668b948b391ec1d2ccea294d438016d855.
Docs: improvement plan §45, CONSOLE-TO-KDE §3.3.2 v1.21,
RATATUI-APP-PATTERNS §13.14 + §14 (6400 LoC, 21 modules, 76 tests).
Adds the smart.rs module for disk health monitoring. Since
smartctl is not installed on most systems (this dev host has it
absent), v1.20 implements the module with three-tier graceful
degradation per the zero-stub policy.
New module smart.rs (222 lines, 7 unit tests):
- SmartInfo struct with available + per-disk health records
- SmartHealth struct with passed + attributes + error
- SmartAttribute struct with id + name + value + worst + threshold + raw
- SmartInfo::smartctl_available() — checks smartctl --version
- SmartInfo::read(disks) — orchestrates per-disk smartctl -A -H
- parse_smartctl_output(text) — extracts passed/failed + attrs
- parse_attribute_line(line) — single 10-field SMART attribute
- parse_smart_value(s) — handles both hex (0x33) and decimal
- health_for(disk_name) — convenience accessor
Three-tier graceful degradation:
1. smartctl missing → available=false, disks=[]
2. smartctl errors per disk → error captured in SmartHealth
3. NVMe permission issues → error message, no fabrication
Updated main.rs: mod smart declaration.
76/76 tests pass (5 bench + 12 sensor + 13 network + 12 storage +
20 process + 7 pid_detail + 7 smart).
Linux host smoke test (this dev host without smartctl):
- available=false (graceful, no panic)
- Storage tab still works (no regression)
Cross-compile SHA256 unchanged from v1.19 (smart.rs is dead code
on Redox — compiles but never called).
Docs: improvement plan §44, CONSOLE-TO-KDE §3.3.2 v1.20,
RATATUI-APP-PATTERNS §13.14 + §14 (6360 LoC, 21 modules, 76 tests).
Closes the v1.13 §37.6 forward-work item (the last one). Process
tab now supports case-insensitive substring filtering on the
process name (comm).
Implementation summary:
- New App.process_filter: String field
- Hotkey 'f' opens text-input mode (pattern reused from refresh-
interval input):
- chars → push to filter buffer
- Backspace → pop
- Enter → commit filter, flash match count
- Esc → discard buffer + clear filter
- Filter applied in render_process_panel:
- For each process, skip if non-empty filter doesn't match
(case-insensitive substring on comm)
- Header line shows filter indicator + hint
- Helper proc_filter_match_count(app) for status message
- 4 new unit tests (case insensitive + substring + no match + empty)
- 62/62 tests pass
Build: clean
Cross-compile SHA256: 12913dedc9b0ea58ed3e7418527da34c903f70be703b8676e4273042c73ac875
Docs: improvement plan §42, CONSOLE-TO-KDE §3.3.2 v1.18,
RATATUI-APP-PATTERNS §13.14 + §14 (5840 LoC, 62 tests).
Closes the v1.13 §37.6 forward-work item. Process tab now shows
real-time CPU usage per process, computed from the delta of
total CPU ticks between successive 13th-tick refreshes.
Source code already landed (process.rs + app.rs + render.rs).
This commit captures full v1.14 docs:
- Improvement plan §38 (CPU% in Process Tab)
- CONSOLE-TO-KDE §3.3.2 v1.14
- RATATUI-APP-PATTERNS §13.14 (audit table +14) + §14 (19 modules, 47 tests)
Implementation summary:
- New cpu_pct: f64 field on ProcessInfo
- New ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)
- Wall-clock dt (SystemTime) — accurate even when TUI pauses
- saturating_sub on ticks prevents underflow if now < prev
- num_cpus from self.cpus.len() (Per-CPU detection result)
- 4 new unit tests (formula + zero + underflow + dt=0)
- 47/47 total tests pass
Math sanity check (verified by unit test):
utime=100→200, stime=50→80, dt=2sec, num_cpus=4
delta = 280-150 = 130 ticks / 2 sec = 65 ticks/sec
CPU% = 65 / 4 cpus * 100 = 1625.0%
Cross-compile SHA256: d46cd66b8e158e2327839ef502879951877a5500d4a40807d3dbc72ed7397231.