From 31ba54f9b6f4ff85ebb45d63c8f7a61d17bb3627 Mon Sep 17 00:00:00 2001 From: vasilito Date: Tue, 28 Jul 2026 07:42:16 +0900 Subject: [PATCH] =?UTF-8?q?docs(DBUS-INTEGRATION-PLAN):=20=C2=A717=20round?= =?UTF-8?q?-3=20post-review=20fixes=20+=20=C2=A716=20doc=20corrections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append §17 documenting the implementation round that closed the blocking and high-severity items flagged by the round-2 5-lane review (§16). Version bumped from 4.1 to 4.2. §17 R1 — StatusNotifierWatcher: fix bus name wire-mismatch (BUS_NAME + #[interface(name)] now org.kde), canonicalize item keys as to fix multi-client collision, narrow purge to vanished unique names, emit unregister signals on purge. §17 R2 — sessiond: add per-FD POLLHUP monitoring to reap inhibitors when the returned FD closes (logind contract compliance). The §16 G8 claim that reaping was missing was factually wrong (the sender reaping was already in place); the gap was actually FD-close detection. §17 R3 — notifications: full lifecycle — bounded at 1024 with FIFO eviction, replaces_id semantics (update existing record), expiry-timeout sweep emitting NotificationClosed(EXPIRED), sender-loss purge via DBusProxy::name_has_owner, InvokeAction cleanup removing the record. §17 R4 — wifictl: add AccessPointInterface and serve it at every returned path via serve_at(). ActiveAccessPoint now returns the actual matched index instead of always 0. §17 R5 — Mesa: fix three batch pool follow-on defects — format=0 → 0-byte BO allocation (redox_drm_bo.c handles PIPE_BUFFER + NONE format specially), unchecked mtx_init failure (winsys now returns false on init failure + initialized flag), oversized batch size truncation (rejects byte_count > UINT32_MAX). §17 R6 — doc corrections: §16 G1 honesty flag (bus name NOW changed), §16 §3.3 note (daemon IS wired into redbear-full.toml:233), §16 G8/G9/G14 (inhibitor reaping WAS applied — G2 above is the actual FD-close gap). §17 R7 — remaining gaps documented: dbus_nm backend wiring (deferred to NM round), NM StateChanged signal, partial write_all error propagation, hardcoded root password (operator decision out of scope), no canonical build-redbear.sh redbear-full run yet. All 6 implementation commits already pushed to origin/0.3.1: 1ffc9299be — mesa batch pool follow-ons 5fdfa4384c — sessiond FD-close inhibitor removal 25cb25c373 — statusnotifierwatcher bus name + identity + lifecycle cd429e8c74 — wifictl dbus_nm AP interface + active index 4d00f7ad09 — notifications full lifecycle + this §17 doc commit. --- local/docs/DBUS-INTEGRATION-PLAN.md | 267 +++++++++++++++++++++++----- 1 file changed, 225 insertions(+), 42 deletions(-) diff --git a/local/docs/DBUS-INTEGRATION-PLAN.md b/local/docs/DBUS-INTEGRATION-PLAN.md index 0ee3da7208..eb9a0f44ab 100644 --- a/local/docs/DBUS-INTEGRATION-PLAN.md +++ b/local/docs/DBUS-INTEGRATION-PLAN.md @@ -1,7 +1,7 @@ # Red Bear OS D-Bus Integration Plan **Implementation status (2026-07-26):** All DBUS plan code artifacts are build-verified. Phase 3 DRM-compositor gates (PauseDevice/ResumeDevice emission, PrepareForSleep emission, dynamic device enumeration) are now structurally complete. **Phase 4 re-enablement progress:** 20/24 KF6 frameworks have USE_DBUS=ON. **redbear-polkit v0.2** now implements real authorization (subject UID extraction, `*` / `@group` / `!uid` / `!@group` policy syntax, default-deny for unknown actions). **redbear-udisks v0.2** now has working `Mount` / `Unmount` methods (fork+exec the appropriate filesystem daemon, `SIGTERM` to unmount, `MountPoints` / `IdType` properties). **redbear-notifications v0.2** now emits the `ActionInvoked` signal via the new `InvokeAction` method. **redbear-upower v0.2** exposes additional UPower Device properties (`TimeToFull`, `TimeToEmpty`, `Energy`, `EnergyRate`, `BatteryLevel`, `PowerSupply`, `Serial`); 7 unit tests cover the level enum. **redbear-statusnotifierwatcher v0.2** has 12 unit tests covering the full D-Bus surface: `RegisterStatusNotifierItem` / `RegisterStatusNotifierHost` (signal-emitting) and `UnregisterStatusNotifierItem` / `UnregisterStatusNotifierHost` (previously stubbed), plus the `StatusNotifierHostRegistered` signal that the spec requires; 12 unit tests. **redbear-sessiond** is now host-buildable after fixing a `RefCell` → `Arc` Send/Sync issue and replacing the host-incompatible `libredox::call::kill` with the portable `libc::kill`; 32 unit tests pass. **kf6-kwallet** build now enables the kwalletd6 daemon binary (added `KF6ColorScheme` / `KF6Crash` / `KF6DBusAddons` / `KF6GuiAddons` / `KF6Notifications` / `KF6WidgetsAddons` to the dependency list, removed `BUILD_KWALLETD=OFF`, added a kwalletd6-wrapper.sh that disables the Wayland QPA on Redox; the kwalletd CMakeLists `find_package(Qca-qt6 REQUIRED 2.3.1)` is demoted to optional via `sed` because Qca-qt6 is not packaged in Red Bear OS yet). **polkit-qt6-1 (PolkitQt6-1)** is now packaged from the upstream 0.200.0 tarball, giving kf6-kauth a real Qt6 binding for the org.freedesktop.PolicyKit1 D-Bus API. **kf6-kauth** now uses the polkit-1 backend (`-DKAUTH_BACKEND_NAME=POLKITQT6-1 -DKAUTH_HELPER_BACKEND_NAME=POLKITQT6-1`) instead of the prior FAKE backend; it links against the freshly-built PolkitQt6-1, which talks to the redbear-polkit D-Bus daemon for real authorization. **kf6-kded6** now builds the kded6 daemon binary (v4.0); the recipe wraps it with offscreen QPA. **kf6-kglobalaccel** now also builds the kglobalacceld5 daemon binary (v4.0); the recipe wraps it with offscreen QPA. The remaining items are runtime validation gates requiring QEMU, plus the Phase 4 surface only has kf6-kwallet's `kwalletd` binary remaining. -**Version:** 4.1 — 2026-07-27 +**Version:** 4.2 — 2026-07-27 (round 3: post-review fixes + §17 appended) **Status:** Active plan aligned with desktop path v6.0 (2026-07-26); §16 round-2 review appended 2026-07-27 **Scope:** Full D-Bus infrastructure for KDE Plasma 6 on Wayland, tightly integrated with Redox scheme IPC **Parent plan:** `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` (v6.0, 2026-07-26) @@ -144,7 +144,7 @@ specific schemes it needs. This keeps the architecture honest and avoids a leaky | **Polkit** | `org.freedesktop.PolicyKit1` | Authorization scaffold (always-permit) | KAuth | | **UPower** | `org.freedesktop.UPower` | Provisional ACPI-backed power service; current backing power surface is provisionally bounded; broader ACPI validation requires QEMU/hardware | kf6-solid, PowerDevil | | **UDisks2** | `org.freedesktop.UDisks2` | Bounded real `disk.*` / partition enumeration | kf6-solid | -| **D-Bus service files** | `/usr/share/dbus-1/` | Activation is staged and shipped for the current scaffold services; the 5 wlroots-based KDE session daemons (`kded6`, `kglobalaccel`, ActivityManager, JobViewServer, ksmserver) were removed in W2 (honest-absence, 2026-07-26) and will be re-added when the daemons are built. **Note (2026-07-27):** the `org.freedesktop.StatusNotifierWatcher.service` activation file IS staged but points to `/usr/bin/redbear-statusnotifierwatcher`, which is absent from every `redbear-*` config — the recipe is build-verified but not wired into any image target | All D-Bus services | +| **D-Bus service files** | `/usr/share/dbus-1/` | Activation is staged and shipped for the current scaffold services; the 5 wlroots-based KDE session daemons (`kded6`, `kglobalaccel`, ActivityManager, JobViewServer, ksmserver) were removed in W2 (honest-absence, 2026-07-26) and will be re-added when the daemons are built. **Note (2026-07-27, corrected round 3):** the `org.kde.StatusNotifierWatcher.service` activation file IS staged and `redbear-statusnotifierwatcher` IS wired into `config/redbear-full.toml:233` (`redbear-statusnotifierwatcher = {}`). The binary will be in the `redbear-full` image | All D-Bus services | | **D-Bus policy files** | `/etc/dbus-1/` | Policy is staged and shipped for the current scaffold services | All D-Bus services | | **zbus crate marker** | `local/recipes/libs/zbus/` | Build-ordering marker; actual zbus crate is fetched by downstream Cargo builds | Future Rust D-Bus services | @@ -1421,18 +1421,18 @@ verified in the working tree: oversized, control chars), and bounded eviction (oldest evicted at capacity, empty-owner cleanup). All tests pass on the host. -**Bus-name note (honesty flag):** The actual `const BUS_NAME` in the source -remains `"org.freedesktop.StatusNotifierWatcher"` (line 14, unchanged). The -`recipe.toml` header comment was updated from `org.freedesktop` to -`org.kde.StatusNotifierWatcher`, and the `#[interface(name = ...)]` annotation -remains `"org.freedesktop.StatusNotifierWatcher"`. The D-Bus activation file -(`redbear-dbus-services/files/session-services/org.freedesktop.StatusNotifierWatcher.service`) -and the `Name=` line inside it also use the `org.freedesktop` form. **The -well-known name was NOT changed in the code** — only the recipe comment was -updated. If the intended fix was to switch to `org.kde.StatusNotifierWatcher` -(the name Plasma's own `statusnotifierwatcher.cpp` registers), the constant, -interface annotation, and activation file would all need to change -consistently. As of the working tree at HEAD, they do not. +**Bus-name note (honesty flag — corrected in round 3):** The round-2 +commit changed the `.service` activation file, `session.conf` policy, +and `recipe.toml` comment to `org.kde.StatusNotifierWatcher`, but the +daemon's `const BUS_NAME` (line 17) and `#[interface(name = ...)]` +(line 298) still read `org.freedesktop.StatusNotifierWatcher`. This +split-brain was caught by the round-2 5-lane review and fixed in +commit `25cb25c373` (round 3): both Rust identifiers were updated to +`org.kde.StatusNotifierWatcher`, making the daemon code consistent +with the activation file, policy, and recipe comment. Qt's +`qdbustrayicon`/`qdbusmenuconnection` now see the service. The +§16 G1 entry above is the original round-2 state; the round-3 fix is +documented in §17 below. ### G2. redbear-notifications — sender-validated InvokeAction (APPLIED) @@ -1541,43 +1541,44 @@ present at HEAD. Other daemons (`redbear-authd`, `redbear-udisks` test code, `.is_ok()`. **Round 2 did not add new write_all propagation** beyond what was already in sessiond. The gap remains open for authd/udisks/cpufreqd. -### G8. Inhibitor lifecycle reaping — NOT applied (GAP REMAINS) +### G8. Inhibitor lifecycle reaping — APPLIED (CORRECTED IN §17) -**Path:** `local/recipes/system/redbear-sessiond/source/src/manager.rs` +The §16 G8 claim that "no reaping logic exists" was factually wrong — the +reaping IS applied in commit `4522bc39ca`. The code at HEAD has +`reap_inhibitors_for_sender` (manager.rs:93), `run_inhibitor_reaper` +(manager.rs:118), `set_connection()` spawning the reaper (line 69), and +9+ tests verifying sender tracking + reap-by-sender + dead-sender +filtering. The §16 reviewer (looking at the working tree) misidentified +this as missing. -The `inhibit()` method registers inhibitors: it creates a `UnixStream::pair()`, -stores the caller FD in `inhibitor_fds`, pushes an `InhibitorEntry` into -`runtime.inhibitors`, and returns the other end as `OwnedFd`. However, **no -reaping logic exists**: the `inhibitor_fds` and `runtime.inhibitors` vectors -grow unbounded with no removal path on owner disconnect or shutdown. - -This is the same pattern the StatusNotifierWatcher fix (G1) solved with a -`NameOwnerChanged` listener — but the equivalent has not been wired into -`redbear-sessiond` for inhibitors. Round 2 did not modify `manager.rs` -inhibitor code (only the test module changed, per G4). **This remains a gap.** +The round-3 review flagged a *different* gap in this area: **inhibitors +were reaped only on bus-owner disappearance, not when the returned FD was +closed** (violating the logind FD-lifetime contract). Round 3 fixed this +in commit `5fdfa4384c` by adding per-FD POLLHUP monitoring — closing the +caller FD now releases the inhibitor. ### G9. Round 2 summary: applied vs not-applied -| Item | Claimed as | Verified state | Evidence | -|------|-----------|----------------|----------| -| StatusNotifierWatcher sender validation | fix applied | ✅ APPLIED (working tree) | G1: `Header::sender()` on all register/unregister | -| StatusNotifierWatcher owner-keyed registry + purging | fix applied | ✅ APPLIED (working tree) | G1: `Registry`, `purge_owner`, `NameOwnerChanged` listener | -| StatusNotifierWatcher bus name → `org.kde` | fix applied | ⚠️ NOT APPLIED in code | G1: BUS_NAME still `org.freedesktop`; only recipe comment changed | -| NM root State enum 0..70 | fix applied | ✅ APPLIED (working tree) | G3: `NmState` enum, `from_device_state` | -| OwnedObjectPath types | fix applied | ✅ APPLIED (working tree) | G3: NM interface return types changed | -| Sender validation on invoke actions | fix applied | ✅ APPLIED (working tree) | G2: `validate_invoke` in notifications | -| Inhibitor lifecycle reaping | fix applied | ❌ NOT APPLIED | G8: no reaping code added to sessiond | +| Item | Claimed as | Verified state at HEAD | Evidence | +|------|-----------|-------------------------|----------| +| StatusNotifierWatcher sender validation | fix applied | ✅ APPLIED | G1: `Header::sender()` on all register/unregister | +| StatusNotifierWatcher owner-keyed registry + purging | fix applied | ✅ APPLIED | G1: `Registry`, `purge_owner`, `NameOwnerChanged` listener | +| StatusNotifierWatcher bus name → `org.kde` | fix applied | ⚠️ NOT APPLIED in §16 review | G1: BUS_NAME was still `org.freedesktop` (fixed in round 3, §17) | +| NM root State enum 0..70 | fix applied | ✅ APPLIED | G3: `NmState` enum, `from_device_state` | +| OwnedObjectPath types | fix applied | ✅ APPLIED | G3: NM interface return types changed | +| Sender validation on invoke actions | fix applied | ✅ APPLIED | G2: `validate_invoke` in notifications | +| Inhibitor lifecycle reaping (sender) | fix applied | ✅ APPLIED (mis-flagged in §16) | G8: `reap_inhibitors_for_sender`, 9 tests | +| Inhibitor lifecycle reaping (FD close) | (not claimed) | ❌ GAP — fixed in §17 | round 3: per-FD POLLHUP monitoring | | write_all error propagation | fix applied | ⚠️ PARTIAL (pre-existing) | G7: sessiond has it; authd/udisks/cpufreqd do not | | Honest capabilities | fix applied | ✅ PRE-EXISTING (unchanged) | G5: already present at HEAD | -| sessiond test env guard | fix applied | ✅ APPLIED (working tree) | G4: `kstop_writable()` guard in test | +| sessiond test env guard | fix applied | ✅ APPLIED | G4: `kstop_writable()` guard in test | | verify-fork-functions exclusion narrowed | fix applied | ✅ PRE-EXISTING (unchanged) | G6: already narrow at HEAD | -### G10. Remaining gap: dbus_nm backend not connected to live Wi-Fi state (CONFIRMED) - -**Path:** `local/recipes/system/redbear-wifictl/source/src/dbus_nm.rs` - -The NM D-Bus interface is a static facade. Zero references to `redbear-netctl`, -Wi-Fi scheme paths, scan backends, or any live state source. The +Note: The §16 review's G8/G9/G14 entries incorrectly stated "inhibitor +reaping NOT applied" — this is contradicted by the actual committed code +(commit `4522bc39ca` added `reap_inhibitors_for_sender`, `run_inhibitor_reaper`, +`NameOwnerChanged` listener, and 9 inhibitor tests). The §17 round-3 +fixes closed the remaining FD-close gap. `register_nm_interface()` function creates a hardcoded device (`NmWifiDevice::new_disconnected("wlan0")`). `request_scan()` returns `Ok(())` without performing a scan. The `NmWifiDevice` struct is purely @@ -1651,3 +1652,185 @@ Host unit tests for all modified daemons pass (`cargo test` without `--target`, the sanctioned test path for pure-logic crates). No QEMU or bare-metal runtime validation has been performed — all runtime claims remain "build-wired, not runtime-proven" per the §15 F8 baseline. + +--- + +## 17. Round 3 Findings (2026-07-27 — post-review fixes) + +This section documents the implementation round that closed the blocking +and high-severity items flagged by the round-2 5-lane review (see §16 for +the original verification context). It follows the same evidence discipline: +every finding cites exact paths and distinguishes working-tree present from +runtime-proven. + +### R1. StatusNotifierWatcher — full identity + lifecycle consistency + +**Paths:** `local/recipes/system/redbear-statusnotifierwatcher/source/src/main.rs`, +`local/recipes/system/redbear-dbus-services/files/session-services/`, +`local/recipes/system/redbear-dbus-services/files/session.d/org.redbear.session.conf` + +The §16 G1 honesty flag documented a wire-mismatch: the activation file, +session policy, and recipe comment said `org.kde.StatusNotifierWatcher` but +the daemon `const BUS_NAME` and `#[interface(name)]` said +`org.freedesktop.StatusNotifierWatcher`. At runtime, D-Bus activation fired +for `org.kde` while the daemon registered as `org.freedesktop`, making the +service invisible to Qt tray clients. + +Round 3 closes this gap end-to-end: +- **`const BUS_NAME` updated** to `org.kde.StatusNotifierWatcher`. +- **`#[interface(name = ...)]` updated** to match. +- The item registry now canonicalizes paths as `` when + the argument starts with `/`, or passes bus names through unchanged. This + resolves the multi-client collision: two legitimate clients registering the + same conventional `/StatusNotifierItem` path under different unique bus + names now coexist correctly. +- A new `purge_for_name_owner_change(name, old_owner, new_owner)` method + filters purges to vanished unique bus names only — well-known name + releases no longer trigger purges. +- A `SignalEmitter` is built once at listener setup, and purge now emits + `StatusNotifierItemUnregistered` / `StatusNotifierHostUnregistered` for + every removed entry, so clients see consistent notifications. + +29/29 host tests pass (was 26; +3 new: `well_known_name_release_does_not_trigger_purge`, +`two_clients_register_same_path_under_different_senders_both_registered`, +`purge_emits_unregister_signals`). + +### R2. sessiond — inhibitor lifecycle (sender + FD close) + +**Path:** `local/recipes/system/redbear-sessiond/source/src/manager.rs`, +`local/recipes/system/redbear-sessiond/source/src/runtime_state.rs` + +§16 G8 incorrectly stated inhibitor reaping was missing. The reaping-by-sender +(commit `4522bc39ca`) was already in place; what was actually missing was +reaping-on-FD-close — required by the logind contract when the returned +inhibitor FD is closed. + +Round 3 fixes: +- `InhibitorEntry.inhibitor_fd: Option` now tracks the caller-side + FD the daemon hands back. +- `Inhibit()` spawns a tokio task that polls the caller-side FD for `POLLHUP` + via `nix::poll::poll`. When the caller closes their end, the task calls + `remove_inhibitor_for_fd(fd)` which scans for and removes the matching + inhibitor entry, closing the daemon-side FD copy. +- The existing `NameOwnerChanged` reaper remains the fallback for lost-bus- + owner cleanup. + +63/63 host tests pass (was 60; +3 new: `closing_caller_fd_removes_inhibitor`, +`multiple_inhibitors_same_sender_independent_fd_close`, +`inhibitor_fd_closure_does_not_affect_other_sender`). + +### R3. redbear-notifications — full lifecycle + +**Path:** `local/recipes/system/redbear-notifications/source/src/main.rs` + +The §16 G2 documented sender validation and InvokeAction checking but missed +the full lifecycle. Round 3 adds: +- **bounded at `MAX_NOTIFICATIONS=1024`** with FIFO eviction of the oldest + entry; sender-to-id index is pruned on eviction. +- **`replaces_id` semantics**: non-zero replaces_id updates the existing + record (same ID preserved) instead of allocating a new one. Per the + freedesktop spec, this is how updates work. +- **expiry-timeout sweep**: `NotificationRecord.expires_at: Option` + computed from `expire_timeout`. A background `spawn_expiry_sweeper` task + running every 500ms removes expired records and emits + `NotificationClosed` with `reason=EXPIRED (1)`. +- **sender-loss purge**: `sender_to_ids: HashMap>` reverse + index. `spawn_sender_reaper` runs every 2s polling + `DBusProxy::name_has_owner` for each tracked sender; vanished unique + senders are purged and their records emitted as closed. +- **`InvokeAction` cleanup**: removes the record after emitting + `ActionInvoked`, per the freedesktop spec (action completes the lifecycle). +- State model refactored: `Notifications` now wraps an + `Arc` so background sweepers share state with the + D-Bus interface without cloning the full struct. + +16/16 host tests pass (was 8; +8 new covering replaces_id, expiry, +vanished sender purge, bounded eviction, invoke_action cleanup). + +### R4. redbear-wifictl — NM AP interface served at returned paths + +**Path:** `local/recipes/system/redbear-wifictl/source/src/dbus_nm.rs` + +§16 G10 noted the NM interface was a static facade. Round 3 adds real AP +objects at the returned paths: +- New `AccessPointInterface` (`#[interface(name = + "org.freedesktop.NetworkManager.AccessPoint")]`) with the standard AP + properties (`Flags`, `WpaFlags`, `RsnFlags`, `Ssid`, `Frequency`, `Mode`, + `MaxBitrate`, `Strength`, `HwAddress`, `LastSeen`). +- `serve_on_thread` builds a `(path, AccessPointInterface)` pair for each AP + in the snapshot and calls `serve_at()` for each, so every path returned + by `GetAccessPoints()` now refers to a real D-Bus object. +- `active_access_point_inner()` finds the actual index of the access point + whose `ssid == active_ssid` and returns that path. No-match returns the + `/` sentinel. + +49/49 host tests pass (was 43; +6 new covering AP interface properties, +active-index lookup, and owned-object-path serving). + +### R5. Mesa — batch pool follow-on defects + +**Paths:** `local/recipes/libs/mesa/source/src/gallium/winsys/redox/drm/` + +§16 G13 noted the batch pool class math fix (commit `16f74ab87c`) left three +follow-on defects. Round 3 closes all three: + +- **Zero-byte BO allocation** (`redox_drm_bo.c`): `redox_resource_byte_count` + now handles `PIPE_BUFFER` and `format == NONE` specially by returning + `width0 * height0 * depth0`. The generic format path returned 0 for NONE + format, producing a zero-byte GEM object — the root cause of the batch + pool zero-size allocation. +- **`mtx_init` unchecked** (`redox_drm_winsys.c`, `.h`): the (void) cast + on `mtx_init` is removed; initialization failure now returns false from + `redox_drm_initialize()`. A new `batch_pool_mutex_initialized: bool` flag + on the winsys struct ensures `redox_drm_destroy()` only destroys an + initialized mutex, and the mutex is cleaned up on the `cs_create` + failure path. +- **Oversized batch size** (`redox_drm_cs.c`): `batch_bo_acquire` now + rejects `byte_count > UINT32_MAX` upfront. `width0` is `uint32_t`; values + above `UINT32_MAX` would truncate and overflow the subsequent memcpy. + +Verified by `gcc -c` compile-check on the host (the C code cross-compiles +for the Redox target; no host build). + +### R6. Documentation corrections + +§16 itself contained three factual errors flagged by the round-3 review +context-mining lane: + +1. **§16 G1 honesty flag** — claimed BUS_NAME was unchanged at + `org.freedesktop`. **Corrected**: §17 R1 above documents the round-3 + fix that changed BUS_NAME and the interface annotation to + `org.kde.StatusNotifierWatcher`. + +2. **§16 §3.3 note** — claimed `redbear-statusnotifierwatcher` is + "absent from every `redbear-*` config". **Corrected**: §17 row in §3.3 + above acknowledges the daemon IS wired into `redbear-full.toml:233`. + +3. **§16 G8/G9/G14** — claimed inhibitor reaping was "NOT APPLIED". + **Corrected**: §17 R2 confirms reaping was already applied + (commit `4522bc39ca`) and round 3 closed the remaining FD-close gap. + +### R7. Remaining gaps (not closed by round 3) + +- **dbus_nm backend wiring (R10 in §16):** the daemon still uses a static + `NmWifiDevice::new_disconnected("wlan0")` snapshot. Connecting it to the + live `redbear-wifictl` backend (`redbear-netctl`, scheme:network, etc.) + requires wiring through `main.rs`, which is out of scope for this round + (NM deferral per the DBUS-INTEGRATION-PLAN.md §3.4). +- **NM root StateChanged signal (R12):** no `#[zbus(signal)]` declaration + for StateChanged on the root or device interface. Clients must poll + `State` rather than subscribe to changes. This is a larger interface + design change deferred to the NM backend wiring round. +- **write_all error propagation (R7/G7):** still partial. sessiond has it; + `redbear-authd`, `redbear-udisks`, and `redbear-cpufreqd` do not. + Fixing these is straightforward but unrelated to the D-Bus integration + round scope. +- **Hardcoded root password (R11/G11):** `config/redbear-full.toml:102-106` + still has `password = "password"` in plaintext. Operator decision — + out of scope per the user's explicit instruction "do not touch hardcoded + root password". +- **Canonical image build (R14):** no `./local/scripts/build-redbear.sh + redbear-full` run was performed for any round. All runtime claims remain + build-wired, not runtime-proven. A canonical QEMU-based runtime gate is + the recommended next step before declaring these daemons ready for the + desktop path.