aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--todo.org38
1 files changed, 36 insertions, 2 deletions
diff --git a/todo.org b/todo.org
index 13c51ae..ed7af23 100644
--- a/todo.org
+++ b/todo.org
@@ -37,8 +37,10 @@ New module =net/src/net/control_plane.py= plus =diag.py= wiring, on dotfiles mai
Unblocked now that panelkit ships. =classify= gains =rival-manager=/=nm-masked=/=keyfile-perms= (all =fixable=), ordered ahead of the generic not-running rule, with the masked check reachable in the NM-down early-return path. Fixes register as =VERBS= + =ACTIONS= (=disable-rival=/=unmask-nm=/=chmod-keyfile=), each narrowly scoped. Live verification needs a real rival/masked/bad-keyfile state (a VM or a deliberately-broken host), so not solo.
**** 2026-07-11 Sat @ 04:32:06 -0500 nm-masked verdict shipped + first panelkit integration
On dotfiles main (=d73eba6=, pushed). The clean, unambiguous verdict of the three, done first to prove the panelkit-in-net rails: =classify= reads =control_plane.nm_state=; on =masked= it returns a fixable =unmask-nm= verdict (=remedy_class="privileged"=) ahead of the generic nm-service rule (=nm-restart= can't start a masked unit). New priv verb =unmask-nm= (=systemctl unmask NetworkManager=), =repair_unmask_nm= (unmask → start → verify), narration entry. The doctor resolves a privileged verdict through =panelkit.resolve()= before running: can't-elevate (GUI, no passwordless, no tty) degrades to the manual guide instead of attempting; existing tiers (no remedy_class) run unchanged. The =net= shim + test add =panelkit/src= to sys.path. Design call shipped (flagged for Craig): CLI =net doctor --fix= is the deliberate act satisfying the Confirm floor, so no CLI prompt added; panelkit contributes the run/prompt/guide degradation on the CLI; the GUI arm-press stays its confirm (panel wiring pending). 765 net tests + 65 suites green, review-code clean. Inbox note sent.
-**** TODO rival-manager + keyfile-perms verdicts (same rails)
-Held for Craig's read on two judgment calls the VM would validate: (1) verdict PRECEDENCE — a rival/bad-keyfile can coexist with a healthy link, so these must gate on an actual failure (=overall != ok= / a failing link/service) and not preempt a more-specific verdict (rfkill, auth); masked didn't have this problem. (2) whether to build the VM scenario now (Craig's pending question). =disable-rival <unit>= (=systemctl disable --now=, rival allowlist) and =chmod-keyfile=/=chown-keyfile= (600 + root, path-validated under system-connections) are straightforward; the classifier ordering is the open call.
+**** 2026-07-11 Sat @ 05:16:24 -0500 rival-manager + keyfile-perms verdicts shipped
+On dotfiles main (=c9f8604=, pushed). Craig approved proceeding with the default precedence. =classify= adds =disable-rival= (rival dhcpcd/networkd/iwd active + a failing link) and =chmod-keyfile= (bad-perms keyfile + a failing link), both =remedy_class="privileged"= through the same panelkit gate. PRECEDENCE settled: both gate on a local-link symptom (never nag next to a working link), sit behind rfkill/service (more fundamental) and ahead of the generic reset (naming the cause beats a blind reconnect); tested (rfkill beats rival, rival beats reset). Priv verbs =disable-rival= (three-unit allowlist), =chmod-keyfile=/=chown-keyfile= (paths validated under system-connections, no traversal). Repairs re-derive their target (=active_rivals= / the active connection). =fake-systemctl= gained a state dir so a disable flips the re-check. 782 net tests + 65 suites green, review-code clean. Two coverage edges deferred to the VM: the keyfile fix's root-owned pass (only reproduces under root), and whether disable-rival needs a follow-on reset to restore the link. All three Phase 1 verdicts now fake-complete; VM live-verification is the remaining piece (see the sub-task).
+**** TODO Net Phase 1 live verification — the VM/nspawn scenario
+The three fixes are software-state repairs (mask NM, start a rival, chmod a keyfile) too dangerous to test on a daily driver, so they want a disposable VM/container. Reuse maint's Layer-3 pattern (=scripts/testing/run-maint-scenarios.sh= + =maint-scenarios/*.sh= break/fix/assert; the lighter =run-maint-nspawn.sh= container variant). Blockers: (1) no base VM image exists yet (=create-base-vm.sh=, a one-time KVM build — likely Craig's hands); (2) the maint base carries pacman/systemd/btrfs, NOT NetworkManager, so a net scenario needs an NM-carrying base or an nspawn rootfs with NM installed; (3) a net-specific runner (=run-net-scenarios.sh=) mirroring maint's. Net scenario files (break/fix/assert for masked/rival/keyfile) are authorable now with no infra. See the "Manual testing and validation" checklist for the by-hand steps if the harness isn't built.
*** TODO [#C] Phase 2 — sharpened auth verdict
Auth-reason classifier reading profile key-mgmt (=manage.py=) + scanned SECURITY (=nmcli.py=): SAE / hidden / enterprise / generic. SAE and hidden become =fixable= profile-modify (Confirm-tier); the rest stay =needs-user-action= with a cause-named message. Pairwise over (reason × profile-state). The classify logic is agent-buildable with fakes; the live profile-modify fix needs a real WPA3/hidden network to verify, so that part is manual.
*** TODO [#C] Phase 3 — flip the net spec to IMPLEMENTED
@@ -684,6 +686,38 @@ Craig's standing checklist of everything that isn't agent-verifiable. Each child
Priority and type tag added by that audit: the task carried neither, which kept the project's largest live container out of the agenda entirely.
+*** Net doctor privileged fixes (net Phase 1) — run in a disposable VM/container
+What we're verifying: the three control-plane fixes actually repair a real broken state under real sudo. These states are dangerous on a daily driver (masking NM kills the network), so run them in a throwaway VM or booted nspawn with NetworkManager installed and passwordless sudo, NOT on ratio/velox. Each: break the state, run =net doctor --fix=, confirm the repair.
+**** nm-masked → unmask-nm
+What we're verifying: a masked NetworkManager is unmasked and started, not met with a doomed nm-restart.
+#+begin_src sh :results output
+sudo systemctl mask NetworkManager
+net doctor --json | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d["outcome"], d.get("next_action"))'
+#+end_src
+- Expected (diagnose): the verdict names the masked NM (outcome fixable, action unmask-nm), not the generic "NetworkManager isn't running".
+#+begin_src sh :results output
+net doctor --fix --json | python3 -c 'import sys,json; d=json.load(sys.stdin); print("fixed=",d["fixed"],"attempts=",[a["id"] for a in d["attempts"]])'
+systemctl is-enabled NetworkManager; systemctl is-active NetworkManager
+#+end_src
+Expected: the fix attempts unmask-nm, NetworkManager is no longer masked (is-enabled != masked) and is active.
+**** rival-manager → disable-rival
+What we're verifying: a rival manager active alongside NM is disabled, and the doctor names it rather than blindly resetting.
+#+begin_src sh :results output
+sudo systemctl enable --now dhcpcd # a rival that fights NM for the link
+net doctor --fix --json | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d["outcome"], [a["id"] for a in d["attempts"]])'
+systemctl is-active dhcpcd
+#+end_src
+Expected: the verdict/fix is disable-rival, and dhcpcd reads inactive afterward. Note whether the link comes back on its own or needs a follow-up =net doctor --fix= (the un-chained-reset question — if it needs the reset, chain "disable-rival": ["disable-rival","reset"] in FIX_CHAINS).
+**** keyfile-perms → chmod-keyfile
+What we're verifying: a profile keyfile with unsafe perms is set back to 0600 root so NM stops ignoring it. Needs root (the system-connections dir is 0700 root:root), so run the doctor as root or via sudo.
+- Pick an existing profile name (its keyfile is /etc/NetworkManager/system-connections/<name>.nmconnection).
+#+begin_src sh :results output
+sudo chmod 0644 /etc/NetworkManager/system-connections/<PROFILE>.nmconnection
+sudo net doctor --fix --json | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d["outcome"], [a["id"] for a in d["attempts"]])'
+sudo stat -c '%a %U' /etc/NetworkManager/system-connections/<PROFILE>.nmconnection
+#+end_src
+Expected: the verdict/fix is chmod-keyfile and the keyfile reads =600 root= afterward.
+
*** Maintenance console — in-person checklist
Re-homed here by the 2026-07-09 audit: this was a second top-level "Manual testing and validation" parent. One parent, per verification.md. Priority and the :test: tag now live on the parent.
Promoted from the build parent when it closed 2026-07-08 — Craig's checklist, runs once in person. Collects everything not agent-verifiable; populate per verification.md as phases land. Known so far: panel look-and-feel vs the E5 prototype (Craig's eyeball); arm-press wording reads right at the moment of use; results-wall readability during a real doctor run; a real UPDATE through the armed guard (and the TTY path once); REBOOT offer after an update; velox in-person glyph check (battery %, charging glyph, low-charge red); SET 80% charge limit sticks across a charge cycle; KILL on a real memory hog.