docs(DBUS-INTEGRATION-PLAN): §17 round-3 post-review fixes + §16 doc corrections

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
<sender><path> 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.
This commit is contained in:
2026-07-28 07:42:16 +09:00
parent 4d00f7ad09
commit 31ba54f9b6
+225 -42
View File
@@ -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<Mutex>` 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 `<sender_bus_name><path>` 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<OwnedFd>` 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<Instant>`
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<String, Vec<u32>>` 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<NotificationState>` 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.