aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* test(net): add the doctor control-plane repair scenarios and runnerCraig Jennings11 hours4-0/+201
| | | | | | | | | | net Phase 1's three privileged fixes (unmask-nm, disable-rival, chmod-keyfile) can't be verified against fakes past the point where real sudo and a running NetworkManager matter, and the broken states they repair are too dangerous to stage on a daily driver. I added the live-verification harness they need. Three scenario files encode the break/fix/assert for each: mask NetworkManager, enable a rival dhcpcd, and chmod a profile keyfile to 0644, then run the real net doctor --fix and assert the repair. Each also names the read-only diagnose verdict it expects, so a run that breaks catches whether the fault is in the diagnosis or the fix. run-net-scenarios.sh rsyncs the net and panelkit trees into a target VM over ssh and drives the scenarios there. The target is a booted VM, not a container: NetworkManager needs a real kernel and network stack to actually run, so an nspawn container can only show the filesystem-level half (the mask symlink, the keyfile mode) and not "NM is active". A disposable VM you revert is the right environment. The scenario break/fix/assert logic is the reviewed part. The ssh transport plumbing hasn't been exercised against a live target yet, so it's marked first-draft and may need a shakeout on the first real run. The by-hand equivalent already lives in the manual-testing checklist.
* chore: close net Phase 1 verdicts, file the live-verification checklistCraig Jennings12 hours1-2/+36
|
* chore: record net nm-masked verdict + first panelkit consumerCraig Jennings13 hours1-2/+6
|
* chore: record the shared privilege model landing (panelkit)Craig Jennings13 hours1-2/+5
|
* chore: close bt doctor Phase 1 as a dated log entryCraig Jennings14 hours1-2/+2
|
* chore: close bt doctor Phase 0 as a dated log entryCraig Jennings14 hours1-2/+2
|
* chore: close net doctor Phase 0 as a dated log entryCraig Jennings14 hours1-2/+2
|
* chore: decompose net + bt doctor specs into build tasks, flip DOINGCraig Jennings15 hours3-4/+34
| | | | | | | | I ran spec-response Phase 6 on both READY specs. Each gets a SPEC_ID-bound parent task in todo.org with one child per implementation phase. Both specs flip from READY to DOING. Only the read-only detection phases are buildable now, so they're marked solo: net Phase 0 (the control-plane probe) and bt Phases 0-1 (the two probes plus the firmware-hint Guide verdict). The privileged phases stay lower priority and carry a note: they're gated on the shared cross-panel run-time privilege model that doesn't exist yet. Shipping them before the Confirm floor would let --fix run root ops via passwordless sudo ungated. I held off writing the manual-testing checklists. The phases that need live or reboot verification are the blocked ones, so their checklists would be planning work that can't start yet.
* chore: file follow-up task for the net diagnostics redaction gapCraig Jennings15 hours1-0/+5
| | | | Split out of the net-doctor-expansion spec review: connection names and SSIDs leak into the copyable report and --json engine-wide, so it wants one systemic pass rather than being solved per-verdict.
* docs: take net + bt doctor specs to READY after review roundsCraig Jennings15 hours2-68/+158
| | | | | | | | | | I ran spec-response on both, then two more skeptical review rounds until each reached Ready with caveats, no blocking findings. I verified every current-behavior claim against the live engine before each response. The skeptical passes earned their place by catching real blockers the first round missed. Net: my round-1 "redaction copy/--json surface" was invented. That surface doesn't exist: SSID redaction is event-log-only and --json is raw. I re-resolved it as parity with the existing link-step behavior, plus a separate task for the systemic gap. I also made all three control-plane verdicts fixable, since a terminal outcome would never run its own fix (doctor.py:181). And I repointed the auth classifier at the profile key-mgmt and scan-security signals that carry the SAE/hidden distinction. Bt: the AutoEnable default is true, not false (bluez 5.87), so round 1 had the "dead every boot" premise backwards and would false-positive on healthy machines. The fault now fires only on an explicit AutoEnable=false, a disabled service, or a TLP entry. The verdict "code" is an additive schema.step key, not one that exists today. Both specs carry one caveat: Phases 1-2 need the shared cross-panel run-time privilege model, which doesn't exist yet. A hard ordering gate now sits on each: shipping the Privileged verdicts before the Confirm floor would let --fix run root ops via passwordless sudo ungated. Only Phase 0 is buildable today.
* docs: record spec-review findings on net + bt doctor specsCraig Jennings16 hours2-0/+43
| | | | | | | | | | Reviewed both DRAFT expansion specs against the live engines. Both stay Not ready: three open decisions apiece plus one blocking finding each. Net: the Security dimension claims connection names are already redacted, but redact.py covers only SSID, MAC, IP, secret-keys, and portal_url, so the new keyfile-perms and rival-manager verdicts would leak a connection name into the wall and --json. Four non-blocking fixes alongside it: nm-restart's scope was overstated, is-enabled isn't used yet, some message text was listed as classifier verdicts, and the priv.py dispatch integration wants naming. Bt: the doctor has no named-verdict layer to hang the proposed no-adapter-firmware and powered-off-persistent verdicts on. A step carries a pass/fail/warn/info status and the run an ok/warn/fail overall, nothing more. How each new verdict is represented has to be settled before Phase 1. Three non-blocking notes cover the widening sudoers surface, the real diagnostic surface, and the bounded journalctl precedent. Both designs are sound and target verified-real gaps: neither engine reads the config or logs the specs propose to add.
* docs: catalogue net + bt failure modes and draft two doctor expansion specsCraig Jennings16 hours3-0/+815
| | | | | | | | I ran the audio doctor's design arc for the net and bluetooth doctors: a blind by-layer research catalogue of failure modes, a symptom-cluster triage, then a spec per doctor. The taxonomy holds ~74 network and ~55 bluetooth distinct root causes, each sourced to a forum or issue-tracker report, sorted into eight network and five bluetooth symptom clusters. The clusters are keyed to each doctor's existing probe ladder, so the fix-versus-guide boundary falls along the tiers the doctor already walks. Every entry carries a remedy class (auto, privileged, reboot-tail, or guide), reusing the audio doctor's four-class run-time privilege model. The two specs are expansions, not rewrites. The net doctor is already the most mature of the three: its classifier reaches six of the eight clusters today, so its spec adds only the control-plane cluster (a rival network manager, a masked NetworkManager, a bad keyfile) and sharper auth naming. The bt doctor's chain is structurally right but blind at both ends, so its spec adds a probe that names the firmware blob the kernel already logged and a probe that catches an adapter that power-on won't keep on across reboots. Both stay DRAFT with three open decisions each, headed for spec-review.
* docs: fold the failure taxonomy into the input-side specCraig Jennings17 hours1-0/+33
| | | | | | I grounded the spec in the taxonomy and its triage. A new "Verdict clusters and remedy classes" section maps the doctor's verdicts onto the nine saturation-tested symptom clusters, ties the four remedy classes to the run-time privilege decision, and names the three new probes the taxonomy implies: dmesg hints, the unmute-doesn't-stick signature, and re-probe-after-idle. v1 stays the buildable core, phases 0 through 4. The rest of the taxonomy is staged as later phases so v1 isn't blocked on it.
* docs: fold the saturation-test findings into the audio taxonomyCraig Jennings18 hours1-0/+52
| | | | | | A blind resample tested whether the nine-cluster taxonomy was representative or an artifact of the first sweep. Ten agents drew 108 fresh reports through five different doors per direction, mostly non-Arch and 2024-2026, none shown the clusters. Zero forced a new cluster. About 70 re-found existing entries. The other 34 were new distinct root causes that all fit existing clusters. I folded the 34 new entries in, 16 input and 18 output, each tagged with its cluster and remedy class, and recorded the test and its verdict. The clusters are complete and stable. Entries keep growing under them without changing the design.
* docs: triage the audio failure catalogue into remedy classesCraig Jennings18 hours1-4/+206
| | | | | | I sorted both directions into nine symptom clusters each, every entry tagged Auto, Privileged, Reboot-tail, or Guide under the privilege model. The clusters mirror the doctor's probe ladder, so the boundary between what it fixes and what it can only guide falls along the tiers it already probes. Two findings are worth keeping. The buildable core reduces to five primitives on both sides (set-default, set-card-profile, unmute, restart-services, config-drop-in), so the breadth is in the diagnosis, not the remedies. And the clusters imply three new read-only probes: dmesg-pattern hints, an unmute-doesn't-stick signature, and a re-probe-after-idle.
* docs: add the run-time privilege model as a doctor decisionCraig Jennings18 hours2-2/+19
| | | | | | | | The input and output doctor reaches firmware, ALSA saved state, modprobe, and packages, which need root. The parent spec decided "no sudo anywhere," correct when the feature was user-scope PipeWire. This supersedes that. The doctor resolves its privilege at run time from three signals: passwordless sudo (sudo -n true, which never hangs), a tty to prompt at, and whether it is the GUI panel. Four remedy classes follow. Auto is user-scope and runs anywhere. Privileged needs sudo, so it runs where passwordless sudo exists, prompts on a CLI, and degrades to Guide otherwise. Reboot-tail runs the applicable part, then instructs the reboot. Guide is physical, BIOS, or wait-for-upstream. Passwordless sudo is not consequence-free, so every Privileged and Reboot-tail remedy defaults to Confirm or Arm, never silent Auto. This was Craig's call, and he wants it as a cross-panel standard. I filed a task to factor the privilege resolution into a shared helper the net, bluetooth, maint, and audio doctors all use.
* docs: catalogue Linux audio input and output failure modesCraig Jennings21 hours1-0/+190
| | | | | | This catalogues real, user-reported ways the microphone and speakers fail on Linux, to feed the audio doctor's triage. It holds 58 input and 59 output distinct root causes, across eight layers each: kernel/driver/firmware, ALSA, PipeWire, Bluetooth, HDMI, USB, app/portal, hardware. Every entry carries its symptom, cause, concrete fix, a sudo/reboot tag, and a source. The sudo/reboot tag is the triage lever. A no-sudo, no-reboot fix is a candidate for a doctor remedy. A firmware, BIOS, or physical fix can only ever be a printed instruction. Triage into build, guide, or out is the next pass.
* docs: record the second review pass on the input-side specCraig Jennings21 hours1-4/+35
| | | | | | | | I ran a second pass across four critical lenses: buildability, technical correctness, adversarial failure-modes, and internal consistency. The technical foundations held. I verified every load-bearing code and /proc/asound claim against the real tree. I recorded eight findings, two of them blocking. The mic-unmute remedy fails open on an unreadable graph, and a privacy event must fail closed. A Bluetooth or USB-non-ALSA mic classifies as no-mic-hardware because it never appears under /proc/asound. I also fixed three mechanical inconsistencies in place: a remedy miscount, and two stray OUTPUT/INPUT doctor-key labels the SPEAKERS/MICROPHONE rename missed. The spec stays DRAFT. The blocking findings gate it from READY.
* docs: fold Craig's spec notes and a review finding into the input-side specCraig Jennings23 hours1-14/+105
| | | | | | | | | | | | Craig left four notes on the draft. This closes all of them. He ratified two open decisions: don't rebuild push-to-talk, since the source-mute is the safety property, and read /proc/asound directly for the kernel tier. Both close DONE. He flagged a naming collision. The doctor keys can't be OUTPUT/INPUT, because the CONTROLS section already carries INPUT/OUTPUT mute toggles. They become SPEAKERS and MICROPHONE, with a distinct diagnostic style held consistent across every panel. I recorded it as a new decision and left the visual treatment open for his eye. He asked for a failure-modes map, so the spec gains a table of every fault by probe tier with its verdict and remedy, output and input together. A spec-review had also left two blocking findings. The kernel-to-graph one was real: ALSA card ids and PipeWire node names are different namespaces, so "compare the sets" was undefined. I took the reviewer's coarse rule as the v1 definition. mic-unrecognized fires when the kernel capture set is non-empty and the graph has zero non-monitor hardware sources. Per-device correlation is logged as vNext. That finding closes. The open-decisions one stays until the last three land.
* docs: close the audio-doctor CLI decision, symmetric direction flagsCraig Jennings30 hours1-8/+9
| | | | audio doctor takes only --fix and --force today, so a lone --input would read as a special case on a bare command. Craig's call: add both --output and --input as explicit flags and alias bare audio doctor to --output. The directions are named symmetrically, --input has a sibling, and nothing that scripts the bare command breaks.
* docs: spec the audio doctor's input side, one doctor per directionCraig Jennings30 hours3-0/+344
| | | | | | | | | | | | The doctor never examines the microphone. diag collects default_source and default_source_present, the classifier reads neither, so a muted mic, a stale default source, and a mic the sound server can't see all classify as healthy. Chrome losing the mic this morning surfaced it: the stack was genuinely fine and the doctor had nothing useful to say. The design grew past the gap in one conversation. An empty source list is normal on a mic-less desktop and a failure on a machine that should have one, and no probe can tell those apart. The missing fact comes from the user's finger: a doctor key on each of the OUTPUTS and INPUTS section headers, where pressing the input one asserts a mic should exist. That retires the DOCTOR header key I shipped hours earlier, and it dissolves the precedence question a single classifier would have faced. A new probe tier sits below PipeWire. /proc/asound lists capture-capable cards, needs no package, spawns no process, and can't hang, so it separates "the software lost a microphone the kernel sees" from "nothing is plugged in". Push-to-talk stays as it is. It mutes the source, the one state where the server guarantees nothing is captured. A link-based push-to-talk fails open, and a crash mid-hold is a hot mic reading as muted. The doctor reads ptt.read_state() instead of guessing. Four decisions are open. Three are closed.
* docs: close the audio-doctor spec and its taskCraig Jennings34 hours2-10/+36
| | | | | | | | The doctor shipped end to end, so the spec goes to IMPLEMENTED and the task closes with a dated record of what landed. Three things the Decisions did not anticipate are written into the history rather than left for a reader to infer. A tenth verdict for missing tooling, because an absent pactl is an unobserved stack and not a broken one. A re-probed marker between a fix run's two row blocks. And DIAGNOSE as the read-only key's name, which Craig settled after asking why this panel carries two keys where the net panel carries one. I filed the wall-controls convergence as its own task. Craig wants the net panel's copy and close pair on every output wall, and today no two of the four agree.
* docs: close the audio-doctor decisions, spec is READYCraig Jennings47 hours2-19/+40
| | | | | | | | | | | | | | | | | All eight decisions resolved. DOCTOR is a section-header key; CLI-first with the GUI as a face; hung and dead are two verdicts sharing one remedy; the remedy tiers stand as drafted; a stream-active guard refuses the audible remedies and is overridable by pressing again; xruns stay out of v1. Two consequences the questions did not make obvious. Taking the guard is what lets the pipewire-pulse restart stay at Confirm — the danger lives in the state of the machine, not the identity of the remedy. And the guard must read stream state from pw-dump rather than pactl: the remedies it protects are the ones you reach for when the Pulse layer is dead, so a pactl-fed guard would go blind exactly when it is needed. Tier 4 drops its xrun probe to match the decision, rather than leaving the probe list contradicting the answer.
* docs(todo): close the minimal-tier zsh PATH gapCraig Jennings47 hours1-1/+6
| | | | | Fixed in dotfiles c6a7878. Recorded that the test asserts the tier invariant rather than the file, since a per-file fix is what missed the tier.
* docs(todo): task-review pass over the oldest-unreviewed batchCraig Jennings2 days1-7/+19
| | | | | | | | | | | | Seven tasks walked. Three carried a topic tag but no type tag, so they were invisible to any filter that asks what kind of work they are. The net/bt realtime-lamp retrofit gains :solo:. Its scope question was settled in July, and what remains is buildable and testable without a decision. Priorities held on review. The zsh PATH gap and the zfs base-image failure both already sit where the severity-by-frequency read puts them, and the keybinding family still turns on a design choice nobody has made.
* docs(todo): one manual-testing parent, and remove the last false bylinesCraig Jennings2 days5-19/+23
| | | | | | | | | | | | | | The maintenance console's in-person checklist was a second top-level "Manual testing and validation" task. It is now a child of the one parent, so the pending checks live in a single place rather than two the agenda shows apart. The audio doctor moves to [#B]. Its spec is written and Phase 0 shipped; six decisions are all that stand between it and a build, which is active backlog rather than parking lot. Four dated artifacts still credited a co-author who is not a person. They are records, but they are tracked and would publish, so they carry one author now like everything else.
* docs(todo): reconcile the open tasks against realityCraig Jennings2 days1-62/+93
| | | | | | | | | | | | | | | | | | | | Archived the nine tasks that shipped today. Verified the rest against the world rather than against memory, and recorded what moved. The telega watch item has not recurred: zero assertions in the server log, and the newest coredump predates the fix. It is a watch item with no defect behind it, so it drops to [#D] per the severity-by-frequency read. The net module's Phase 4 is filed and waiting on the dotfiles project, which already tracks it — tagged blocked rather than re-sent. The manual-testing container carried no priority and no type tag, which kept the project's largest live collection of pending checks out of the agenda entirely. It has both now, plus a check for the one thing today's work cannot verify without a reboot. The release epic gained what the docs scrub landed, and what it uncovered: four tracked files still name a co-author who is not a person.
* docs: correct the audio-doctor spec's timeout claim, close Phase 0Craig Jennings2 days2-10/+19
| | | | | | | | | | | | | | The draft said build_status had no pactl timeout and froze the panel. Driving it disproved that: pactl.run defaults to timeout=8 and raises PactlTimeout, so the panel degrades. The real defect was narrower and is now fixed. Two of build_status's four reads sat outside its degrade guard, so a server that answered the device lists and then stopped answering raised out of a function contracted to return ok:False, and waybar's audio module died rather than dimming. Corrected in place with the reasoning, rather than dropped, since a spec that quietly loses a wrong claim teaches nothing about why it was wrong.
* docs: spec the audio panel doctorCraig Jennings2 days2-1/+262
| | | | | | | | | | | | | | | | | | | | Answers the four questions the request asked, from a live survey rather than from memory. There is no PulseAudio: the stack is pipewire, wireplumber, and pipewire-pulse, all user-scope, so no remedy in this feature needs sudo — which is why it mirrors the net panel's doctor rather than the maintenance console's privileged verb table. Two findings shape the design. pactl hangs against a server that accepts and never answers, which is exactly the fault a doctor exists to diagnose, so every probe is bounded. And the panel cannot diagnose itself, because pactl is the layer most likely to be down — the probes read systemctl and pw-dump before they touch it. Filed the panel's own missing timeout as a separate bug. It freezes the audio panel today, independently of any doctor. Seven decisions left open; the spec is DRAFT and nothing gets built until they close.
* docs: remove personal paths and false co-authorship before releaseCraig Jennings2 days7-17/+17
| | | | | | | | | | | | | | | | | The repo carries an open task to publish it, and docs/ leaked things a clone could neither use nor should see. Absolute paths under one user's home become the repo root, a home-relative path, or a named variable, so the snippets stay runnable for anyone. The testinfra example takes its user from an env var instead of hardcoding one. References into a private sibling project and into the gitignored tooling directory are replaced by what they mean; as written they were dead links in any clone but the author's. Five documents claimed a co-author who is not a person. An #+AUTHOR line survives conversion into docx, a wiki page, or a PDF, so it is worth being exact about: these documents have one author.
* docs(todo): close the roam-inbox batchCraig Jennings2 days1-8/+31
| | | | | | Eight tasks from the 2026-07-09 roam sweep, each with what landed and where. The signal-count task turned out to be a semantics bug rather than a counting one, and the session-identifier task was tmux, not waybar.
* feat(installer): enable the CPU-mode restore unitCraig Jennings2 days1-1/+6
| | | | | | | | maint-epp-restore.service replays the remembered energy-performance preference at login, since the kernel resets it to the driver default on every boot. It hangs off default.target rather than timers.target; systemctl --user can't run during install, so the enablement symlink is created directly, the same idiom the scan timers already use.
* docs: move the host inventories into the org-roam knowledge baseCraig Jennings2 days8-931/+207
| | | | | | | | | | | | | | | | | | | | The four host capability inventories, the TrueNAS hardware specs, and the ratio USB/xHCI record now live as roam nodes under ~/org/roam/hardware/, linked from the Homelab Hardware Inventory index. The copies here are gone. Carried rather than pointed at. A pointer keeps one canonical copy, but a third project that discovers a durable hardware fact would then have to write into this repo to record it, which the cross-project rules forbid — so the fact would land in an inbox instead of on the device's page. Moving keeps one canonical copy without closing the write path. system-health-check.org resolves a host's inventory by its #+HOSTNAME: keyword under ${ROAM_DIR:-$HOME/org/roam}/hardware/, never by filename: a node's timestamp prefix does not survive a rename. A host with no roam clone takes the same NO INVENTORY FILE path it always did. strix-soak-watch.org lands here from home, beside the workflow whose Phase 3 invokes it. It is a workflow, not an inventory page.
* chore(todo): archive completed and cancelled tasksCraig Jennings2 days2-234/+233
|
* fix(guard): consume the override sentinel as it's honoredCraig Jennings3 days2-2/+28
| | | | One touch buys exactly one transaction. The maintenance console's forced live update sets the sentinel and clears it afterward, but a caller that dies mid-update would leave the file behind and keep the guard disarmed until reboot. The hook now deletes the sentinel at the moment it honors it, so a missed cleanup costs nothing. The env override is unchanged, and the caller's clear step stays as the belt-and-suspenders for transactions the hook never fires on.
* fix(maint): uncurate /etc/ssl/private — it was a real misconfigCraig Jennings3 days1-1/+3
| | | | pacman warned filesystem 755 vs package 700 on both hosts: a world-listable private-key directory, not runtime hardening as the previous commit claimed. pacman never resets existing directory permissions, so I chmod'd 700 on ratio and velox and removed the curation entry. Integrity reads an honest zero on both.
* chore(maint): curate runtime perm drift out of the integrity countCraig Jennings3 days1-0/+13
| | | | | | Reinstalling the owning packages reset these eight paths and the system re-drifted every one before the next -Qkk read: cupsd rewrites its three conf files mode 600, StateDirectory re-chowns /var/lib/passim on service start, tmpfiles re-groups /var/log/journal inside the reinstall transaction itself, /etc/ssl/private sits hardened below the packaged mode, and the utempter setgid helper carries ownership drift. They would count as integrity findings forever with no action that clears them. All eight are universal Arch runtime behavior, so they ship as qkk_known defaults rather than per-host curation. The utempter entry also silences a content change on that one binary, noted inline and accepted. I installed the TOML on both hosts; integrity now reads zero on each, with the real findings the metric surfaced along the way fixed for real (a stale hyprland.pc on both hosts, and /root sitting world-listable at 755 on velox, restored to the packaged 750).
* fix(guard): allow same-version reinstalls through the live-update hookCraig Jennings3 days2-15/+128
| | | | | | | | The hypr-live-update-guard hook blocked on target names alone, so a same-version reinstall of hyprland (the maintenance console's integrity REINSTALL, right after a full update) aborted identically to a real upgrade. A pure reinstall writes identical bytes over identical bytes and can't crash the live compositor: the SIGABRT hazard needs the on-disk version to actually change. The guard now compares each target's installed version (pacman -Q) against the sync-db candidate (expac -S %v). Both are read-only queries and safe under the transaction lock, pinned live with db.lck present. Targets that match pass as reinstalls, and the block message lists only the version-changing packages. Unresolvable versions (AUR targets, -U transactions, expac missing) still block conservatively. Verified live on ratio: the previously blocked six-package reinstall ran clean with Hyprland up. The block path is pinned by the test suite (13 tests, four new). I deployed the script to /usr/local/bin on both hosts by hand since the installer only copies it on fresh systems.
* chore(maint): retire cve_min_warn from the thresholdsCraig Jennings3 days1-3/+3
| | | | The maint probe now grades CVE advisories on actionability (a fixed release exists and isn't installed), not severity, so the severity floor is dead config. The section comment records the retirement. I installed it on both hosts.
* fix(install): mark pacman packages explicit after --needed installCraig Jennings3 days2-1/+101
| | | | | | pacman --needed skips a package that is already on the system as a dependency and leaves its install reason alone. A declared package can then sit as asdeps, surface as an orphan once its accidental dependent leaves, and get swept away by an orphan cleanup. expac and lm_sensors, both maintenance-console runtime deps, nearly went this way: they were asdeps on both hosts and only survived today's orphan sweep because something still depended on them. pacman_install now marks the package explicit after a successful install. The mark never runs on a failed install, and a mark failure never fails the install. I flipped expac and lm_sensors to explicit on ratio and velox by hand since the installer only runs on fresh systems.
* docs(maint): reword coredump window comment for past-window gradingCraig Jennings3 days1-1/+4
|
* chore(todo): file retention-repair GUI gap taskCraig Jennings3 days1-0/+5
|
* fix(maint): add timeline_slack key to the snapshot thresholdsCraig Jennings3 days1-0/+3
| | | | This pairs with the maint probe change: the snapper grade now warns at summed limits + snapshots.timeline_slack. Slack 2 absorbs the hourly create/:45-cleanup timer gap that kept the card amber most of every hour. I installed it on both hosts.
* fix(maint): raise pacman cache warn threshold to 20GBCraig Jennings3 days1-1/+2
| | | | | | The 10GB threshold sat below the keep-3 steady state on both hosts (ratio 12.6GB, velox 10.8GB after pruning), so the warning persisted with nothing left to clean. paccache keeps three versions per package and never expires by age, which puts the honest ceiling near 17-19GB. At 20GB the metric stays quiet in steady state but still fires within about two months if the paccache timer dies. I installed it on both hosts and the warning cleared on each.
* chore: health-check updates (workflow note, audit stamp, todo entries)Craig Jennings3 days3-2/+7
|
* chore: close maintenance-console build, flip spec to IMPLEMENTEDCraig Jennings3 days2-10/+12
|
* docs: adopt system-health-check workflow and host inventoriesCraig Jennings3 days6-0/+1948
| | | | | | | | The workflow and the four host capability inventories move here from the home project. System maintenance is archsetup's domain, and this puts the thresholds TOML and both of its consumers in one repo. The workflow's prose severity rules now cite the installed TOML keys, and the TOML wins on disagreement. Inventory paths point at docs/homelab-inventory/. A note routes routine ratio/velox checks to the maint CLI, leaving this workflow for the non-Arch hosts and forensic deep dives. Only the #+HOSTNAME: capability inventories moved (ratio, velox, mybitch, truenas, plus the truenas hardware-specs asset they link). The personal gear records stay in home, and the home-side removal is handed off through its inbox.
* feat(install): install maintenance thresholds and enable maint timersCraig Jennings3 days2-1/+75
| | | | | | | | The maint console and the system-health-check workflow grade against ~/.config/archsetup/maintenance-thresholds.toml, but nothing installed it. A fresh install got a working maint CLI and no thresholds file. user_customizations now installs the shipped TOML from the cloned repo. A re-run refreshes it, and the user layer in ~/.config/maint/ is never touched, so a reinstall can't eat curation. It also enables maint-scan.timer and maint-net-scan.timer through wants-symlinks (systemctl --user has no session bus during install, the syncthing idiom). Timer enablement is hyprland-only because the units live in that stow tier. The install lists gain the runtime tools the probes call but nothing carried: expac, lm_sensors, fwupd.
* chore: close build Phase 12 in todo, file zfs DKMS image bugCraig Jennings3 days1-2/+5
|
* test(maint): add VM and nspawn remedy scenario harnessCraig Jennings3 days13-1/+1138
| | | | | | Nine break/fix/assert scenarios run the real maint fix inside the test VM. The runner batches non-conflicting scenarios into one VM boot, restores the snapshot only between groups, and leaves the base image pristine afterwards. A systemd-nspawn fast lane runs the pacman-level group against a cached pacstrap rootfs in seconds. The plan layer validates the scenario contract without a VM and carries a 19-test unit suite. make test-maint wires the VM lane in. The zfs lane is filtered but unexercised: the FS_PROFILE=zfs base image fails to build (ZFS DKMS module not found on linux-lts 6.18). The failure is tracked in the todo.