diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-07 19:57:03 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-07 19:57:03 -0500 |
| commit | 58ec12480c92525dea505045ca6bc78f384b7a85 (patch) | |
| tree | b14e1dccc8be4c31e345e45e4739924f236f303f /docs | |
| parent | 7955be70ea7751c68d6a9b2ca3683ee697a38726 (diff) | |
| download | archsetup-58ec12480c92525dea505045ca6bc78f384b7a85.tar.gz archsetup-58ec12480c92525dea505045ca6bc78f384b7a85.zip | |
docs: maintenance-console spec to DOING + build decomposition
The spec encodes the converged 2026-07-06/07 design as 13 decisions and 13 phases (CLI-first maint package, four-layer test strategy) and carries its full review trail inside, live-verified on ratio. Late calls landed during authoring: the bar glyph replaces custom/sysmon, doubles as the laptop battery display, and keeps the btop scratchpad on right-click.
todo.org now carries the build: 13 phase tasks under a SPEC_ID-stamped parent, the manual-testing collector, the flip-to-IMPLEMENTED closer, and the vNext deferral.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/specs/2026-07-07-maintenance-console-spec.org | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/docs/specs/2026-07-07-maintenance-console-spec.org b/docs/specs/2026-07-07-maintenance-console-spec.org new file mode 100644 index 0000000..008faeb --- /dev/null +++ b/docs/specs/2026-07-07-maintenance-console-spec.org @@ -0,0 +1,346 @@ +#+TITLE: Maintenance Console — Spec +#+AUTHOR: Craig Jennings +#+DATE: 2026-07-07 +#+TODO: TODO | DONE +#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED + +* DOING Maintenance console +:PROPERTIES: +:ID: 9d9df833-c592-4aec-a7df-50d588e943ce +:END: +- 2026-07-07 Tue @ 19:54:04 -0500 — DOING — decomposed into the build: 13 phase tasks + manual-testing + flip task under the todo.org parent "Maintenance console build" (:SPEC_ID: stamped); vNext logged [#D]. +- 2026-07-07 Tue @ 19:05:00 -0500 — READY — round-2 spec-review passed (all 10 round-1 findings resolved, no new blockers); Craig approved the config-paths proposal and the sysmon right-click re-homing, closing the last open decision (cookies 13/13, 10/10). +- 2026-07-07 Tue @ 18:30:04 -0500 — DRAFT — drafted from the completed design arc (docs/design/maintenance-console-design-ideas.org, all decisions dated 2026-07-06/07) and the converged E5 interactive prototype. + +* Metadata + +| Field | Value | +|----------+-----------------------------------------------------------------------------------------------------------| +| Status | doing | +|----------+-----------------------------------------------------------------------------------------------------------| +| Owner | Craig Jennings | +|----------+-----------------------------------------------------------------------------------------------------------| +| Reviewer | Claude Code (archsetup) | +|----------+-----------------------------------------------------------------------------------------------------------| +| Repo | dotfiles (=maint/= package); archsetup owns lifecycle, thresholds TOML, VM test harness | +|----------+-----------------------------------------------------------------------------------------------------------| +| Related | [[file:../design/maintenance-console-design-ideas.org][design doc (source of decisions)]] ; [[file:../prototypes/2026-07-07-maint-console-E5-selector-subpanel.html][E5 prototype (converged UI)]] ; [[file:2026-07-03-instrument-console-panels-spec.org][instrument-console-panels-spec]] ; | +| | todo.org "Maintenance console" | +|----------+-----------------------------------------------------------------------------------------------------------| + +* Summary + +A single-host maintenance console for the two daily drivers (ratio, velox): a =maint= CLI + GTK panel in dotfiles (fourth panel sibling beside net/bt/audio) that surfaces every Arch health metric and runs the safe remediations behind Confirm/arm guards, streaming results to a live output wall. archsetup owns the severity thresholds (one TOML both the console and the system-health-check workflow read) and the VM harness that tests the destructive remedies. + +* Problem / Context + +Routine maintenance on these machines today is either the ~1000-line agent-driven =system-health-check.org= workflow (forensic, slow, requires a session) or ad-hoc habit — and things fall through: ratio ran at ~75% unclean shutdowns for weeks unnoticed (2026-06-08), a snapper TIMELINE misconfig nearly filled =/home=, a live =-Syu= swapped mesa+hyprland under the running session and crashed the desktop (2026-06-07), and =rust= sat looking orphaned until a manual review found it was intentional. There is no glanceable surface that says "your machine is healthy" or "these three things need a press." + +The console owns the routine-maintenance and at-a-glance slice; the workflow remains the escalation path for forensic investigation. The design converged over 2026-07-06/07 through five divergent prototypes to E5 (selector + dense subpanels + results wall); every open question in the design doc is resolved and dated there. This spec makes that design buildable. + +* Goals and Non-Goals + +** Goals +1. Every metric in the design doc's tables collected read-only on both hosts, capability-dispatched (btrfs/AMD on ratio, ZFS/Intel/battery on velox), exposed as =maint status --json=. +2. Every determinate remedy as =maint fix <thing>= — allowlisted exact argv, global =--dry-run=, privileged verbs through a validated priv table. +3. Two doctor actions — CLEAN UP (all Auto metrics, unattended) and REVIEW & FIX (Confirm metrics, per-item approval) — streaming one lamp per action to the results wall. +4. The E5 GUI: category selector, dense faceplate subpanels, rotary band selectors on tall subpanels, evidence digests, curation lifecycles (MARK KNOWN / KEEP / MARK EXPECTED with UNMARK/CLEAR), arm-to-fire guards, updates strip with the live-update guard. +5. Severity thresholds in one archsetup-owned TOML; user curation in a separate layer a template sync can never eat. +6. Waybar glyph (replacing =custom/sysmon=) tracking the worst *Diagnostic* state, fed by a background timer so the bar stays honest with the panel closed; on battery hosts the glyph doubles as the bar's battery level display. +7. Four-layer test coverage: unit fakes, read-only live, VM remedies via archsetup's harness with qcow2 snapshot/restore, AT-SPI smoke on fixtures derived from the E5 GOOD/BAD snapshots. + +** Non-Goals +- *No AI/workflow assistance from the panel.* Workflow buttons were removed in prototyping (2026-07-07); metrics that need judgment render read-only. vLater. +- *No auto-updates, ever.* Updates run only via the guarded Confirm levers; the guard arms, the user fires. +- *No file-deletion keys in the disk top-consumers digest* — evidence only. +- *No SIGKILL escalation* on the KILL lever (SIGTERM only; escalation is vLater). +- *No btrfs device-error counter reset button* — resetting without diagnosis masks a dying drive; stays manual. +- *No per-ecosystem update metrics* (cargo/npm/pipx/…): one topgrade-freshness metric + the fwupd count instead. +- *No net/bt doctor retrofit here* — separate todo task; only the results-wall idiom is shared. + +** Scope tiers +- v1: everything under Goals — all metrics from the design-doc tables including the full-sweep findings ("all committed to v1", 2026-07-07), both hosts, CLI + GUI + glyph + timers + four test layers + archsetup TOML install. +- Out of scope: the Non-Goals above. +- vNext (logged to todo.org at hand-off): AI-assist actions on read-only metrics; SIGKILL escalation; per-row live meters where evidence rows could stream. + +* Design + +** For the user + +Click the bar glyph (it replaced the old sysmon readout; its color is the worst diagnostic state — green/amber/red; on the laptop its text is the live battery level). The console opens: a faceplate row of category keys (STORAGE · SNAPSHOTS · PACKAGES · SYSTEMD · LOGS · MEM·PWR · NETWORK · SERVICES), each carrying its own status lamp and a "N fixable · M watch" split so you know your agency before reading a cell. Selecting a category renders its dense subpanel: every metric a row with lamp, value, and — only when off-nominal — a contextual lever. Tall subpanels (Packages, Logs, Services) carry a rotary band selector (click to cycle: e.g. ORPHANS · PACNEW · ADVISORIES), one band rendered at a time at full width, each band showing its own lamp + count on the dial. + +Counts never hide detail: orphans list name + size with per-package REMOVE (armed) and KEEP; journal errors group by identifier with first/last seen and the exact =journalctl= command; listeners show process · port · bind address. Where "expected" is knowledge, it's curated: MARK KNOWN / MARK EXPECTED / KEEP move items to a dim section (never vanish) with UNMARK per row and CLEAR for the layer; every mark logs to the wall. + +Anything consequential arms before it fires: first press turns the key red and shows exactly what will run (the dry-run argv); second press runs it. The two doctor keys — CLEAN UP and REVIEW & FIX — and every remedy stream into the results wall at the bottom: one lamp per action, amber while running, green with the result inline (reclaimed MBs, restarted unit), red on failure; date+time stamped, HIDE/COPY, capped at 3.5 visible entries with the half-entry as scroll cue. The updates strip shows pending/AUR/firmware/CVE counts with a state-tiered border; UPDATE and TOPGRADE sit behind the live-update guard — when mesa/hyprland/wayland runtime is in the pending set the key arms with "press again to run anyway — or apply from a TTY". After an update lands, a REBOOT key (armed) appears. + +Temps, memory, and throttle re-read every ~3 s while visible; local probes every ~30 s and immediately after any action that touches them; network-tier counts (updates, CVEs, AUR, firmware) come from an hourly timer cache with their age shown. Close the panel and nothing keeps running except the light half-hourly background scan that feeds the glyph. + +** For the implementer + +*Package shape.* =maint/= in dotfiles mirrors the sibling humble-object stack (=net/=, =audio/=): =src/maint/= with =probes/= (one module per category, read-only collectors that parse command output), =schema.py= (the status envelope), =status.py= (assembles =maint status --json=), =thresholds.py= (TOML load + user-layer merge), =curation.py= (marks/keeps/expected stores, read+write), =remedies.py= (every lever as an allowlisted exact argv), =priv.py= (the closed privileged-verb table, validated args, =sudo= via =MAINT_SUDO= env — the net =priv.py= pattern verbatim), =guard.py= (pure live-update guard over a package list), =doctor.py= (CLEAN UP / REVIEW & FIX sequencing, streaming), =cli.py=, =viewmodel.py= (pure render helpers), =gui.py= (thin GTK4 view), =indicator.py= (glyph state). Tests in dotfiles =tests/maint/= with fake binaries on PATH (the fake-pactl/fake-parec pattern) — fake-smartctl, fake-btrfs, fake-zpool, fake-pacman, fake-journalctl, fake-ss, fake-docker, fake-systemctl, etc. + +*The contract.* =maint status --json= emits one envelope: host capabilities (fs type, pstate driver, battery present, docker/libvirt present — "battery present" means a =/sys/class/power_supply/= entry with =type == Battery=, never a non-empty dir: ratio carries Mains + USB-C PD entries with no battery), then per-category metric dicts each carrying =id=, =value=, =severity= (=ok=/=warn=/=crit=), =label=, optional =evidence= (list of digest rows), optional =levers= (list of remedy ids applicable right now), =probed_at=, and =cache_age= for cached metrics (network-tier and slow-local alike). Severity is always computed engine-side from the thresholds TOML — the GUI never holds a threshold. The E5 prototype's GOOD/BAD data snapshots are reshaped to conform to this schema and become the GUI fixtures (=MAINT_PANEL_FIXTURE=bad= renders the degraded board on a healthy machine). + +*Remedies.* Each remedy is =(id, argv, priv_verb_or_none, arm_required, re_probe_ids)=. =maint fix <id> [--dry-run]= prints the exact argv under =--dry-run= — the same string the GUI's arm-press displays. Collectors never elevate; only =priv.py= verbs touch =sudo=, and archsetup's install already grants NOPASSWD (=archsetup:1178=). After a remedy fires, the engine re-probes the metrics named in =re_probe_ids= (fire CLEAN → cache re-measured, not assumed). Long-running remedies (btrfs/zfs scrub, SMART self-test, rsyncshot RUN NOW) report a running-% state to the wall, not an instant reset. KILL revalidates PID + process name at fire time and refuses session-critical names (systemd, the compositor, the panel itself → disabled key). + +*Curation stores.* Shipped defaults (known-noise patterns, expected listeners sshd/mpd/tailscaled, expected containers winvm, pacnew safe-delete allowlist) live in the archsetup-shipped TOML; user marks live in the user layer, merged over it. Journal noise patterns bind to identifier + message snippet, never a whole unit. Unmarking a shipped default writes a disable flag in the user layer; CLEAR MARKS empties the user layer only. + +*Refresh tiers* (all engine-side, GUI subscribes): live group ~3 s panel-open + subpanel-visible; fast local ~30 s panel-open + post-action re-probe; hydration on open (fast reads first, process probes behind, sub-second perceived); network tier from the hourly timer cache, on-demand refresh only; *slow local tier* — probes whose cost class matches the network tier despite being local (full =pacman -Qkk=, measured 47 s on ratio; the disk top-consumers scan) ride the same hourly timer cache with age shown, on-demand refresh only, never in hydration or fast-local; closed-panel glyph fed by a ~30-min systemd user timer running =maint scan --glyph= to write the state file =waybar-maint= reads. Two user units ship with the package: =maint-scan.timer= (glyph) and =maint-net-scan.timer= (hourly network-tier + slow-local cache). + +*Waybar wiring.* =custom/maint= replaces =custom/sysmon= in the modules list and config; glyph state file + SIGRTMIN signal refresh like =custom/ptt=. Gotcha (from the PTT build): waybar runs a *generated runtime config* — the module lands via =waybar-active-config= + SIGUSR2, not just a canonical config edit. Theme CSS mirrored in both themes (dupre, hudson). + +*Cross-links, not duplicates.* DNS/NetworkManager repair deep-links to the net panel's doctor; OPEN JOURNAL launches a terminal running =journalctl -p err -b= (the NET DOCTOR delegation pattern). + +* Alternatives Considered + +** Console vs workflow-only maintenance +- Good (console), because glanceable health + one-press remedies catch the rot the workflow only finds when explicitly run; the workflow stays for forensics. +- Bad, because it's a large build (this spec's phase count) duplicating some workflow probe logic. +- Neutral, because the shared thresholds TOML keeps the two from drifting — the duplication is probes, not policy. + +** GUI-first vs CLI-first +- Good (CLI-first), because =maint status --json= + =maint fix= are testable without a display, the VM harness needs exactly that, and the GUI reduces to a face over a verified engine (the sibling panels' proven shape). +- Bad, because the GUI lands late in the phase order — no visible console until ~P7. +- Neutral, because the E5 prototype already de-risked the visual design; what's left to prove is the engine. + +** Layout: A–E divergents, then 3-column compression vs rotary bands +- Good (E5 selector + subpanel), because Craig picked E from five divergent prototypes and iterated it live through E2–E5; the selector gives every category full width. +- Bad, because one-category-at-a-time hides cross-category state — mitigated by per-key lamps + fixable/watch splits on the selector row. +- Neutral, because the rotary band selector replaced the failed 3-column compression (third-width truncation lost row detail — Craig's verdict after use); MEM·PWR keeps its 3-column strip because its rows are short. + +** Update visibility: per-ecosystem metrics vs topgrade freshness +- Good (freshness), because topgrade's step set has no cheap offline "updates available?" probe — mirroring it costs a network round-trip per registry at panel-open. +- Bad, because the panel can't say "3 cargo crates stale" — only "topgrade last ran N days ago". +- Neutral, because fwupd refreshes its own metadata cache, so firmware gets a real count for free. + +** Elevation: polkit/pkexec per action vs priv-verb table + sudo +- Good (priv table), because the net panel already proved it: a closed validated verb set, NOPASSWD covered by archsetup's install, no auth dialog stacking on top of the arm-to-fire press. +- Bad, because the sudoers grant is broad (archsetup currently writes NOPASSWD: ALL) — the verb table is the real narrow waist, so it must stay small and auditable. +- Neutral, because pkexec would add a second confirmation UI the arm press already provides. + +* Decisions [13/13] + +** DONE CLI-first architecture; GUI is a face +CLOSED: [2026-07-07 Tue] +- Context: four test layers need a display-free engine; the sibling panels' humble-object stack is proven; the VM harness can only drive a CLI. +- Decision: We will ship =maint= as engine + =cli.py= with =maint status --json= as the contract and =maint fix <id>= as every lever; =gui.py= renders the same code and never does anything the CLI can't. Global =--dry-run= prints the exact argv. +- Consequences: easier — testability, VM scenarios, the arm-press preview is the dry-run string for free; harder — the GUI arrives late in the phase order, and the JSON schema becomes a real contract that fixtures and tests pin. + +** DONE Determinate remedies only in v1 +CLOSED: [2026-07-07 Tue] +- Context: workflow buttons were prototyped and removed (E5, 2026-07-07); indeterminate fixes (failed units with unknown cause, journal errors, OOM) need judgment. +- Decision: We will ship only fixed, scriptable actions with predictable outcomes — the design doc's determinate-remedies list. Judgment metrics render read-only with expectation tags ("hardware — watch only", "evidence below"). AI/workflow assistance is vLater. +- Consequences: easier — every remedy is an exact argv, auditable and VM-testable; harder — red diagnostic cells offer no in-panel next step beyond evidence + the named command. + +** DONE Updates behind an arming live-update guard +CLOSED: [2026-07-07 Tue] +- Context: the 2026-06-07 crash was a live =-Syu= swapping mesa+hyprland under the session; the standing rule is never update the graphics/wayland runtime live. Hard-refusing infantilizes the user. +- Decision: We will put UPDATE (repo+AUR) and TOPGRADE on the updates strip as Confirm levers; when the pending set contains mesa/hyprland/wayland-runtime packages the key arms red with "press again to run anyway — or apply from a TTY". The topgrade wrapper always passes =--disable git= (its git step rebase-autostashes =~/code/*/=). After an update lands, an armed REBOOT key appears and the reboot-required metric flips. The strip border is state-tiered (green/amber/red by pending/CVE/staleness thresholds in the TOML). +- Consequences: easier — updates finally have a sanctioned in-panel path with the footgun named at press time; harder — the guard's package-set matcher must be right (pure function, heavily unit-tested), and the wrapper owns topgrade's flag discipline. + +** DONE Doctor actions stream to a results wall +CLOSED: [2026-07-06 Mon] +- Context: fire-and-forget buttons hide failures; Q1 resolved 2026-07-06. +- Decision: We will stream every doctor and every individual remedy to the wall — one lamp per action, amber running / green with result inline / red failed, always shown, date+time stamped, HIDE/COPY, 3.5-entry scroll cap. Long ops report running-%. +- Consequences: easier — failures are visible the moment they happen, and mark/unmark curation events get a durable in-session log; harder — remedies need incremental output plumbing (engine yields events; GUI appends), not just exit codes. + +** DONE Both hosts first-class; capability dispatch from day one +CLOSED: [2026-07-06 Mon] +- Context: Q2 resolved 2026-07-06 — velox travels and suspends; its GAP metrics (battery health, unclean-shutdown rate) motivated the console. +- Decision: We will probe host capabilities at startup (fs types, pstate driver, battery, docker/libvirt) and run only applicable collectors; ZFS paths (scrub, autotrim, capacity, frag) are built and tested, not stubbed; velox-only metrics ship in v1. +- Consequences: easier — one codebase, no ratio-first debt; harder — every collector's tests need both-host fixtures. ZFS remedy scenarios are possible in the VM (the harness supports =FS_PROFILE=zfs= base images — =vm-utils.sh:23,53-55=; prerequisite: confirm the zfs base image builds); the floor if that image proves troublesome is argv-construction tests + read-only live runs on velox. + +** DONE Thresholds and curation as layered TOML, archsetup-owned +CLOSED: [2026-07-06 Mon] +- Context: Q3 resolved 2026-07-06 — severity values must not drift between the console and the system-health-check workflow; template syncs must never eat user curation. +- Decision: We will put every severity value (cache trigger, disk %, scrub-age bands, snapshot limits, temp bands, staleness windows, "a lot" of updates) plus shipped curation defaults in =maintenance-thresholds.toml=, owned and installed by archsetup, seeded from the workflow's hard-won values. User marks/keeps/expected live in a separate user-layer file merged over it. Both the console and the workflow read the installed TOML. +- Consequences: easier — one source of truth, workflow and console can't disagree, thresholds tunable without touching code; harder — archsetup grows an install/update step for the TOML, and the merge semantics (including disable-flags for shipped defaults) need explicit tests. + +** DONE Evidence digests + curation lifecycles on every count metric +CLOSED: [2026-07-07 Tue] +- Context: the full-sweep audit (2026-07-07) committed all findings to v1; a bare count trains you to ignore it, and "expected" is configuration knowledge that today lives in Craig's head (the rust orphan lesson, winvm, sshd). +- Decision: We will give every count an evidence digest (orphans name+size, journal groups by identifier with first/last seen + next command, listeners process·port·bind, coredumps by binary, docker df by type, snapshots split timeline/single/pre-post, top-5 RAM, recent boots clean/unclean) and every "expected"-shaped metric the full curation lifecycle: MARK KNOWN / KEEP / MARK EXPECTED with per-row UNMARK, CLEAR for the user layer, marked items dimmed but never hidden, every transition logged to the wall. Guarded per-item remedies ride the digests (orphan REMOVE armed + REMOVE ALL skipping keeps, pacnew MERGE via terminal diff, per-unit RESTART+RESET, per-container START, per-socket STOP/KILL armed, DELETE STALE keeping newest 2). KILL carries four guards: arm shows exact victim, SIGTERM only, PID+name revalidated at fire, session-critical names disabled. +- Consequences: easier — the panel is honest and the curation encodes system knowledge durably; harder — this is most of the GUI surface area and the reason the subpanel phases are the fattest. + +** DONE Rotary band selector on tall subpanels +CLOSED: [2026-07-07 Tue] +- Context: 3-column compression on Packages/Logs/Services lost row detail to third-width truncation (Craig's verdict after use). +- Decision: We will render tall subpanels one band at a time behind a rotary selector (click to cycle; engraved band labels each with own lamp + count; gold underline + needle on the selected band): Packages ORPHANS·PACNEW·ADVISORIES, Logs SIGNAL·KNOWN NOISE·COREDUMPS·KERNEL/HW, Services CONTAINERS·DOCKER DISK·CRON & BACKUPS. MEM·PWR keeps its 3-column strip (short rows fit). +- Consequences: easier — full-width row detail restored, and each selection idiom in the panel keeps a distinct visual voice; harder — off-band state must surface on the dial lamps or a red band goes unseen. + +** DONE Four-tier refresh cadence + glyph timer; glyph replaces custom/sysmon, shows battery on laptops +CLOSED: [2026-07-07 Tue] +- Context: probe cost varies from a sysfs read to a network round-trip; the bar must stay honest with the panel closed. Craig (2026-07-07, spec session): the maint glyph replaces =custom/sysmon= rather than adding a module beside it, and on a laptop it should function as the bar's battery level display. +- Decision: We will refresh in tiers matched to probe cost (live ~3 s visible; fast local ~30 s open + post-action; hydration on open; network hourly cache with age shown, on-demand refresh; a slow-local tier for expensive local probes — full =-Qkk= at 47 s measured, disk top-consumers — riding the same hourly cache), run a ~30-min =maint-scan.timer= writing the glyph state file, and wire =custom/maint= in waybar in place of =custom/sysmon=. The glyph's *color* tracks the worst *Diagnostic* state only — actionable clutter (a fat cache) never colors the bar. On battery hosts (capability-gated, same host probe) the module's *text* is the battery level with a charging indicator, read directly by =waybar-maint= on waybar's own interval (a 30-min scan is too coarse for charge %); a critically-low charge (threshold in the TOML) feeds the diagnostic state so the color stays honest when the battery is the emergency. Any existing waybar battery module on the laptop retires in favor of this one. +- Consequences: easier — panel-open cost is bounded, the bar signal stays high-trust, and the laptop bar loses a module instead of gaining one; harder — the glyph script has two data sources (state file for diagnostics, sysfs for charge), and the old sysmon affordances resolve per Craig's 2026-07-07 ruling: the btop scratchpad (=pypr toggle monitor=) re-homes to the maint glyph's right-click; =sysmon-cycle= and the =waybar-sysmon= readout retire. + +** DONE Privileged remedies via a maint-priv verb table +CLOSED: [2026-07-07 Tue] +- Context: most remedies need root (systemctl, paccache, journalctl --vacuum, scrub, charge threshold); collectors must never elevate; the net panel's =priv.py= already proved the pattern. +- Decision: We will route every privileged remedy through =maint/priv.py=: a closed verb table, one exact command per verb, args validated by type-specific regexes before reaching sudo, =MAINT_SUDO= env override so tests run fakes directly. Read/write split is hard: probes never import priv. +- Consequences: easier — one small auditable module is the entire privilege surface, VM tests exercise the same verbs; harder — every new remedy is a deliberate verb-table addition, never an inline =sudo=. + +** DONE Four-layer test strategy; E5 snapshots become fixtures +CLOSED: [2026-07-07 Tue] +- Context: remedies mutate systems; the archsetup VM harness (=scripts/testing/run-test.sh=) exists; the sibling panels' fake-binary and AT-SPI patterns are proven. +- Decision: We will test in four layers — (1) unit with fake binaries on PATH (~90% of surface: probes as parsers, remedies as argv construction, guard as pure function), (2) read-only =maint status --json= on the live hosts, (3) remedies in the VM with scenario scripts that break things over ssh then assert post-state, systemd-nspawn for pure pacman-level tests, (4) AT-SPI GUI smoke on the host driven by fixtures conforming to the JSON schema, derived from the E5 GOOD/BAD snapshots. No GUI in the VM. VM isolation policy: the snapshot primitives already exist (=lib/vm-utils.sh:303-357= — create/restore/delete via =qemu-img=, VM must be stopped for any snapshot op; =run-test.sh= already reverts to a =clean-install= snapshot), so the new work is scenario *orchestration* only. Scenarios are grouped into non-conflicting batches that run within one VM boot (a stopped-cronie scenario doesn't contaminate a fat-cache scenario); a stop → restore → reboot cycle runs only *between destructive groups* whose post-state would contaminate the next batch. The scenario runner declares each scenario's group so the batching is explicit, not inferred. +- Consequences: easier — destructive coverage without risking real machines, and the harness work is smaller than feared (orchestration over existing primitives); harder — VM runs are slow (~40–60 min), so grouping discipline matters, and a scenario mis-grouped as non-conflicting can produce order-dependent failures (the runner can randomize in-group order to surface these). + +** DONE system-health-check workflow moves home → archsetup +CLOSED: [2026-07-06 Mon] +- Context: Craig's call (2026-07-06) — home is scoped to finances/health/personal; system maintenance is archsetup's domain, and the move collapses the TOML ownership coupling. +- Decision: We will move =system-health-check.org= + =homelab-inventory/*.org= into archsetup in the final phase, rewire the workflow to read the installed TOML instead of prose severity rules, and hand the home-side removal (originals, startup references, index) to home via an inbox note — never edited blind from here. +- Consequences: easier — TOML producer and both consumers land in one project; harder — sequencing: the move waits for the TOML to exist and ship, so it anchors the last phase. + +** DONE Config and state file locations +CLOSED: [2026-07-07 Tue] +- Context: four files need stable homes both consumers reach: the shipped thresholds+defaults TOML (archsetup-installed, design doc suggests =~/.config/archsetup/maintenance-thresholds.toml=), the user curation layer, the glyph state file, and the hourly network-tier + slow-local cache (round-1 review: this one must survive reboots, or every boot shows "no data" for up to an hour — =$XDG_RUNTIME_DIR= is wrong for it). Sibling precedent: panels read =~/.config/<pkg>/config= (audio =config.py:29=). Craig approved the proposal 2026-07-07. +- Decision: We will install the shipped TOML at =~/.config/archsetup/maintenance-thresholds.toml=; the user layer at =~/.config/maint/curation.toml= (alongside an optional =~/.config/maint/config= for GUI prefs per sibling convention); the glyph state and the hourly cache both under =~/.local/state/maint/= (XDG state — persists across reboots; the glyph file is cheap to rebuild but gains nothing from being ephemeral). Env overrides for all four so tests and fixtures never touch real files. +- Consequences: easier — XDG-conventional, stow-safe, sync-safe (curation outside any synced tree), honest counts right after boot; harder — two config roots (=archsetup/= for shipped, =maint/= for user) must be documented or they'll confuse future edits. + +* Review findings [10/10] + +** DONE Automation-tier normativity contradiction :blocking: +Round-1: the References section declared the design-doc tables normative for Automation tier, but the tables mark failed-unit restart / pending updates / AUR staleness / =-Qkk= reinstall as Workflow while the 2026-07-07 decisions give them Confirm levers — an implementer couldn't derive the REVIEW & FIX set. Resolved: References now scopes the tables to metric *existence* only; the dated decisions win on tier. + +** DONE VM scenario isolation policy undefined :blocking: +Round-1 (code fact): =vm-utils.sh:301= requires the VM stopped for snapshot ops, contradicting "restore between scenarios" + "batch per boot" as written; snapshot primitives also already exist (=vm-utils.sh:303-357=), so "new harness work" overstated. Resolved: Decision 11 now states the grouped-batch policy (non-conflicting scenarios per boot; stop/restore/reboot only between destructive groups; runner declares groups) and reframes the work as orchestration over existing primitives. + +** DONE Expensive local probes had no refresh tier :blocking: +Round-1 (measured live): full =pacman -Qkk= runs 47 s on ratio; disk top-consumers is the same cost class; neither fit fast-local (~30 s) nor hydration. Resolved: a slow-local tier added — both ride the hourly timer cache with age shown, on-demand refresh only. + +** DONE ZFS-in-VM claim verified false as a constraint +Round-1 (code fact): the harness supports =FS_PROFILE=zfs= base images (=vm-utils.sh:23,53-55=). Resolved: Decision 5 corrected — ZFS scenarios are possible (prerequisite: zfs base image builds); argv+live-read-only becomes the fallback, not the plan. + +** DONE Battery capability detection underspecified +Round-1 (verified live): ratio's =/sys/class/power_supply/= is non-empty (Mains + 2 USB entries) — a naive non-empty-dir probe false-positives. Resolved: contract now defines battery present ⇔ an entry with =type == Battery=. + +** DONE sysmon retirement collateral unnamed +Round-1: retiring =waybar-sysmon=/=sysmon-cycle= without touching =tests/waybar-sysmon/= + =tests/sysmon-cycle/= reds =make test= in the same commit; =#custom-sysmon= CSS lives in three files. Resolved: Phase 11 now names the scripts, suites, and CSS blocks as same-commit retirement collateral. + +** DONE Test-gate discovery answered +Round-1 (code fact): =make test= discovers =tests/maint/test_*.py= via glob (=Makefile:266-271=) — no enumeration needed; AT-SPI smokes ARE enumerated (=Makefile:279-286=). Resolved: Dev-tooling records both; =test-panel-maint= target added to Phase 12 deliverables. + +** DONE arch-audit absent on ratio +Round-1 (verified live): every other assumed tool present; =arch-audit= is not. Resolved: External deps flips it from verify-item to known — add to archsetup deps + install both hosts before Phase 3's CVE collector is live-testable. + +** DONE Fourth unhomed persistent file +Round-1: the hourly network-tier/slow-local cache must survive reboots (else "no data" for up to an hour after boot) — =$XDG_RUNTIME_DIR= wrong for it. Resolved: added to the open config-paths decision; proposal now puts glyph state + cache under =~/.local/state/maint/=. + +** DONE Panel-CSS "both themes" ambiguity +Round-1 (code fact): sibling panels load =themes/dupre/panel.css= hardcoded (=audio/gui.py:52-61=); hudson has no panel.css. Resolved: Phase 7 follows the sibling dupre-only panel-CSS convention; Phase 11 scopes both-themes CSS to the waybar glyph stylesheets. + +* Implementation phases + +Each phase leaves the tree working and gates on dotfiles =make test= green (archsetup phases gate on their own checks). Commit + push per phase; dotfiles note at milestones (archsetup-owns-dotfiles). + +** Phase 1 — Package skeleton, thresholds, contract +=maint/= scaffold (src layout, stow wiring, =__main__=), =thresholds.py= (TOML load + user-layer merge + disable flags, env overrides), =schema.py= severity model, capability probe, =maint status --json= envelope with three pilot collectors (disk usage, package cache size, failed-unit count), fake-binary harness in =tests/maint/=. Seed =maintenance-thresholds.toml= content in archsetup (workflow's values: snapshot limits, 10 GB cache trigger, scrub-age bands). + +** Phase 2 — Collectors: storage & snapshots +btrfs (unallocated, scrub age, per-device error counters cross-checked vs SMART), ZFS (health, capacity, frag, autotrim, scrub age), SMART (health, wear, temp, last self-test), fstrim.timer, disk top-consumers digest, snapper/ZFS snapshot counts split timeline·single·pre-post with oldest-single named. Both-host fixtures. + +** Phase 3 — Collectors: packages, security, systemd +Cache size, orphans (name+size), pacnew classified safe-delete vs needs-merge, keyring freshness, reboot-required, pacman -Qkk; network-tier cache readers (checkupdates, arch-audit CVEs named, AUR staleness, fwupd count, topgrade freshness stamp) with age; is-system-running + cause, failed-unit roster (name·since·exit·journalctl hint), maintenance-timers meta-metric, taint letters decoded. + +** Phase 4 — Collectors: logs, memory, power +Journal error digest (grouped by identifier, first/last seen, next command, two noise layers applied), coredumps by binary, kernel/hw events; memory free + top-5 consumers, OOM kills, swap/zram, temps, throttle state, EPP mode read, battery health + charge threshold read (velox), unclean-shutdown rate from recent boots. + +** Phase 5 — Collectors: network posture, services +DNS/NM reachability, firewall state (+ public-bind exposure naming when down), listeners digest with expected-layer merge and unexpected/public severity, tailscale, fail2ban + recent bans, NTP offset; rsyncshot freshness, docker system df by type + stopped containers with expected-layer, libvirt state, cron expected-entries drift. Curation read side complete. + +** Phase 6 — Remedies, priv, guard, doctor CLI +=remedies.py= (full allowlisted table with =re_probe_ids= + running-% for long ops), =priv.py= verb table, =guard.py= live-update matcher (pure), curation writes (mark/unmark/clear/keep/expected), =maint fix <id> [--dry-run]=, =maint doctor clean|review= streaming an event feed (text wall in the terminal). Topgrade wrapper with =--disable git=. KILL's four guards. Argv-construction tests for every remedy; no execution. Splits naturally into 6a (remedies/priv/guard) + 6b (curation writes + doctor + CLI) if the session runs long — 6a alone leaves a working tree. + +** Phase 7 — GUI shell +GTK4 window, faceplate, category selector row (lamps + fixable/watch splits), subpanel scaffolding, hydration tiers + refresh cadence (live/fast, visibility-gated like the audio meters), =MAINT_PANEL_FIXTURE= loading, panel CSS following the sibling convention (=themes/dupre/panel.css=, the shared panel stylesheet — hudson carries waybar CSS only, no panel.css). Renders real =status= data read-only — no levers yet. + +** Phase 8 — GUI subpanels: storage, snapshots, packages, updates strip +Evidence digests, armed per-item keys (orphan REMOVE/KEEP, pacnew MERGE, DELETE STALE, scrub with running-%), the Packages rotary band selector, updates strip with state-tiered border, UPDATE/TOPGRADE behind the arming guard, armed REBOOT on completion. + +** Phase 9 — GUI subpanels: systemd, logs, mem·pwr, network, services +Failed-unit roster keys, journal digest + MARK KNOWN lifecycle UI + OPEN JOURNAL, Logs/Services rotary bands, MEM·PWR 3-column strip + CPU-mode segmented control + velox SET 80% + guarded KILL, listeners curation + STOP/KILL, firewall ENABLE, container START, RECLAIM SPACE composite macro. The fattest phase — split by subpanel pair (9a systemd+logs, 9b mem·pwr+network+services) if a session runs long; each pair gates independently. + +** Phase 10 — Results wall + doctor keys +The wall widget (lamp stream, date+time, inline results, running-%, HIDE/COPY, 3.5-entry cap, dark scrollbar), CLEAN UP / REVIEW & FIX wired through =doctor.py=, every armed key streaming, curation events logged, post-action re-probes visible. + +** Phase 11 — Waybar glyph + timers +=indicator.py= + =waybar-maint=, =custom/maint= replacing =custom/sysmon= (canonical config + =waybar-active-config= runtime path + SIGUSR2), signal-driven refresh, =maint-scan.timer= + =maint-net-scan.timer= user units, glyph color tracks worst diagnostic from the state file; on battery hosts the module text is the live battery level (sysfs read per waybar interval, charging indicator, low-charge threshold feeding the diagnostic state) and any existing battery module retires. Glyph CSS lands in both theme waybar stylesheets (dupre, hudson). Retirement collateral in the same commit (Craig ruled 2026-07-07): =custom/maint= right-click re-homes the btop scratchpad (=pypr toggle monitor=); the =waybar-sysmon= + =sysmon-cycle= scripts and their suites (=tests/waybar-sysmon/=, =tests/sysmon-cycle/=) retire, and the =#custom-sysmon= CSS blocks come out of =waybar/style.css= and both themes. + +** Phase 12 — VM remedy scenarios + AT-SPI smoke (archsetup + dotfiles) +Scenario orchestration over the existing snapshot primitives (=vm-utils.sh:303-357=): grouped batches per Decision 11, maint scenario scripts (stop cronie, mask fstrim, orphan packages, fill cache, break timers → =maint fix= → assert post-state); systemd-nspawn variant for pacman-level cases; AT-SPI smoke on GOOD/BAD fixtures asserting board render, arm behavior, and no leaked processes, plus the enumerated =test-panel-maint= Makefile target. May split VM work (archsetup) from AT-SPI smoke (dotfiles) into two sessions — each half gates independently. + +** Phase 13 — Install wiring, workflow move, docs +archsetup installs the TOML (idempotent, update-safe); deps added; =maint= README + keybind/user docs; =system-health-check.org= + homelab inventories moved into archsetup and rewired to the TOML; handoff note to home's inbox for the home-side removal; spec flipped IMPLEMENTED with history line. + +* Acceptance criteria +- [ ] =maint status --json= runs read-only on both hosts; ratio emits btrfs/AMD metrics and no ZFS/battery; velox emits ZFS/Intel/battery — same binary, capability-dispatched. +- [ ] Every metric's severity comes from the installed TOML; editing a threshold changes the verdict with no code change. +- [ ] =maint fix <id> --dry-run= prints the exact argv for every remedy; the GUI arm-press shows the same string. +- [ ] Probe modules import no elevation path (verifiable: nothing outside =priv.py= references sudo). +- [ ] With mesa/hyprland in the pending set, UPDATE arms with the override wording instead of running; without them it runs on confirm. Topgrade always receives =--disable git=. +- [ ] Doctor runs stream per-action lamps to the wall with results inline; a long op shows running-%; a failure shows red without aborting the remaining stream. +- [ ] Marks/keeps/expected survive a dotfiles template sync and a package update; marked items render dimmed, UNMARK restores, CLEAR empties only the user layer. +- [ ] KILL never fires on a recycled PID (name revalidated) and renders disabled for session-critical names. +- [ ] The bar glyph reflects worst diagnostic within one scan interval with the panel closed, and never colors for actionable-only findings. +- [ ] On velox the glyph shows live battery level + charging state; a charge below the TOML low threshold turns the diagnostic state (and the glyph) red; on ratio no battery text renders. +- [ ] =MAINT_PANEL_FIXTURE=bad= renders the fully degraded board on a healthy machine; AT-SPI smoke passes against it. +- [ ] VM scenarios: each broken-state → =maint fix= → asserted post-state passes from a pristine qcow2 snapshot. +- [ ] Full dotfiles =make test= green; closing the panel leaves no maint processes beyond the timers. + +* Readiness dimensions +- *Data model & ownership:* live probe data is generated, never persisted (results wall is session-only, in-memory); curation files are user-authored (user layer) vs shipped (archsetup TOML); glyph state file and hourly cache are generated state rebuilt by the timers (their home is the open config-paths decision). The JSON envelope is the one contract fixtures pin. +- *Errors, empty states & failure:* a failing probe degrades its own metric to an "unprobed" state with the failure named — never a dead board; remedy failures stream red to the wall with stderr excerpt and the metric re-probed (truth, not assumption); network-tier cache absent → counts show "no data · refresh" with the timer named; every guard names the operation and the next step at arm time. +- *Security & privacy:* privilege surface is the priv verb table alone; args regex-validated pre-sudo; listeners digest shows local process/port data only in the local GUI — never logged off-machine; no secrets read; charge/EPP writes are fixed-value verbs. +- *Observability:* the results wall is the action log; =maint status --json= + =--dry-run= make any GUI claim reproducible in a terminal; cache ages displayed; scan timers visible via systemctl --user. +- *Performance & scale:* hydration budget sub-second perceived (fast reads first); live tier only while visible; N of everything is small (tens) except journal lines — the digest groups engine-side with a top-10 cap. Long ops never block the GUI thread (worker + event feed, the sibling =bg()= pattern). +- *Reuse & lost opportunities:* reuses the sibling panels' humble-object stack, fake-binary harness, AT-SPI smoke pattern, priv.py design, console-key/lamp CSS kit from the widget gallery, the existing archsetup VM harness, and the E5 prototype as both fixture source and pixel reference. Deliberately not reused: waybar-sysmon (replaced), the workflow's prose probes (superseded by TOML-driven collectors). +- *Architecture fit & weak points:* fourth panel sibling, same layering. Weak points: subprocess sprawl (every probe shells out — bounded by tiering + hydration order); the guard matcher (a miss means a live mesa swap — mitigated by pure-function pairwise tests over package-set cases); scenario orchestration is new harness surface, though the snapshot primitives already exist in =vm-utils.sh= (mitigated: archsetup-side, tested by the scenario suite itself); ZFS scenarios depend on the =FS_PROFILE=zfs= base image building (fallback: argv tests + read-only live on velox + Confirm-tier gating). +- *Config surface:* the thresholds TOML (all severity values, shipped curation defaults, staleness windows, "a lot" threshold), user layer (marks/keeps/expected/disables), =~/.config/maint/config= optional GUI prefs (refresh rates, fixture env), =MAINT_SUDO= / =MAINT_PANEL_FIXTURE= / path-override envs for tests. All named with defaults in Phase 1/6 docs. +- *Documentation plan:* maint package README (user: what each key does, guard semantics; developer: schema, adding a remedy = verb-table discipline); archsetup docs for the TOML install + VM scenario harness; the moved workflow's own doc updates. Keybind doc deferred to the panel-chord-family task. +- *Dev tooling:* dotfiles =make test= discovers =tests/maint/test_*.py= automatically via its glob (=Makefile:266-271= — verified, no enumeration needed); the AT-SPI smoke *is* enumerated per panel (=Makefile:279-286=), so a new =test-panel-maint= target is a Phase 12 deliverable; archsetup gains a =make= target or documented invocation for the maint VM scenario; fixture regeneration script from E5 snapshots. +- *Rollout, compatibility & rollback:* additive package; stow to install, unstow to remove; waybar module swap is one config change reverted by restoring =custom/sysmon=; TOML install is idempotent and never overwrites user layer; remedies themselves are the dangerous surface — each is Confirm/armed, dry-run-previewable, and the destructive ones (snapshot delete, docker prune tiers 2–3, updates) arm individually. +- *External APIs & deps:* all local binaries, no web APIs. Verified on ratio (2026-07-07 review): =checkupdates= (1.13.1), =topgrade= (=--disable=/=--dry-run= flags confirmed), =paccache=, =snapper=, =smartctl= 7.5 with working =--json=, =coredumpctl=, =ss=, =docker=, =fwupdmgr=, =ufw=, =fail2ban-client=, =chronyc=, =tailscale= all present. Known: =arch-audit= is ABSENT on ratio — add to archsetup deps and install on both hosts before Phase 3's CVE collector is live-testable. Still prerequisites: same presence sweep on velox; =zpool status -j= support (OpenZFS ≥ 2.3) — fall back to text parsing where absent; =charge_control_end_threshold= sysfs path on velox's hardware; =coredumpctl=/=journalctl= field names pinned by fixtures from live output. =pacman -Qkk= exits 0 even with altered files (summary on stdout, warnings on stderr) — the collector parses output, never trusts the exit code. + +* Risks, Rabbit Holes, and Drawbacks +- *Scope mass* — the biggest risk is the build's sheer surface. Dodge: CLI-first phasing means every phase lands standalone value (collectors are useful from P2 via the CLI), and the GUI phases consume a verified engine. The phase order is also an abort-friendly ladder — stopping after P6 still yields a complete maintenance CLI. +- *Parsing drift* — probes parse a dozen tools' output. Dodge: prefer machine formats where they exist, pin real both-host output as fixtures, and treat a parse failure as an "unprobed" metric, never a crash. +- *Guard false-negative* — the one place a bug bricks a session. Dodge: pure function, pairwise-tested over package-name sets, and the TTY escape wording keeps the human in the loop. +- *VM harness time* — 40–60 min per run makes remedy-test iteration slow. Dodge: nspawn for pacman-level cases; scenarios grouped into non-conflicting batches per VM boot, with a stop/restore/reboot cycle only between destructive groups (Decision 11 — snapshot ops need the VM stopped, =vm-utils.sh:301=). +- *Curation-merge subtleties* — disable-flags over shipped defaults is the fiddly corner. Dodge: it's a pure merge function; exhaustive unit cases in Phase 1 before anything depends on it. +- *Results-wall event plumbing* — streaming per-action state through a worker into GTK is the panel's newest moving part. Dodge: the event feed is engine-side and CLI-proven in Phase 6 before the GUI touches it (the doctor's terminal wall is the same feed). + +* Testing / Verification / Rollout +The four-layer strategy is Decision 11; per-phase gates are in the phase list. Verification beyond tests: read-only =maint status --json= eyeballed on both hosts after P5; the doctor CLI exercised live on ratio (Auto tier only) after P6; GUI walked on fixtures + live after P10; glyph observed through a full timer cycle after P11; the E5 prototype is the pixel reference for GUI review. Manual eyeball checks (needle-level look-and-feel, arm wording, wall readability) get filed as a structured manual-testing task in todo.org at the end, per house convention. Rollout: per-phase dotfiles commits to main; archsetup commits for TOML/harness/workflow phases; both machines pick the package up via the normal stow path — velox needs a one-time stow after P11 lands (flagged in the dotfiles note). + +* References / Appendix +- Design doc (all dated decisions + the full metric tables with per-metric Automation/levers/notes): [[file:../design/maintenance-console-design-ideas.org][maintenance-console-design-ideas.org]]. The metric inventory is deliberately not duplicated here — the tables there are normative for *which metrics exist*. Where a table's Automation column disagrees with the doc's later dated decisions (failed-unit restart, pending updates, AUR staleness, and =-Qkk= reinstall all read Workflow in the tables but gained Confirm levers in the 2026-07-07 determinate-remedies and updates decisions), the dated decision prose — mirrored in Decisions 2, 3, and 7 here — wins. The Confirm set that defines REVIEW & FIX membership derives from the decisions, never from the table column. +- Converged prototype (interactive; SIM FAIL / SIM BAD DAY / SIM ZFS controls): [[file:../prototypes/2026-07-07-maint-console-E5-selector-subpanel.html][E5]]; exploration path A–E4 beside it. +- Sibling precedent: net =priv.py= (privilege pattern), audio =peak.py=/meters (visibility-gated live refresh), instrument-console-panels-spec (visual kit), PTT build (waybar runtime-config gotcha). +- Home workflow to move: =~/projects/home/.ai/project-workflows/system-health-check.org= + =homelab-inventory/*.org=. + +* Review and iteration history +** 2026-07-07 Tue @ 18:30:04 -0500 — Claude Code (archsetup) — author +- What: initial draft from the completed 2026-07-06/07 design arc; consolidated the design doc's dated decisions into twelve DONE decisions + one open (config paths), decomposed the build into thirteen phases, grounded implementer facts against the live tree (net priv.py, audio config.py:29, archsetup:1178 NOPASSWD, waybar custom/sysmon, tests/ fake-binary layout). +- Why: the [#B] todo task queued spec-create as the next step after the design converged; Craig gave the go this session and added two calls mid-authoring: the glyph replaces custom/sysmon, and on laptops it doubles as the battery level display. +- Artifacts: design doc; E5 prototype; todo.org task (cross-link updated this session). + +** 2026-07-07 Tue @ 18:50:00 -0500 — Claude Code (archsetup) — reviewer +- What: round-1 review with live read-only verification on ratio. Rubric: Not ready — three blocking findings (Automation-tier normativity contradiction, VM isolation policy undefined vs vm-utils.sh:301, no refresh tier for 47 s-class local probes) + seven non-blocking, all small spec-text fixes. Every code citation in the draft verified accurate; tool sweep on ratio held except arch-audit (absent); found the qcow2 snapshot primitives already implemented (vm-utils.sh:303-357) and make test's glob auto-discovery (Makefile:266-271). +- Why: the spec's buildability rested on harness and cadence claims that only a code read + live measurement could confirm. +- Artifacts: findings recorded in * Review findings; verified facts cited inline per finding. + +** 2026-07-07 Tue @ 18:53:51 -0500 — Claude Code (archsetup) — responder +- What: accepted all ten findings and folded them in — References normativity scoping, Decision 11 grouped-batch VM isolation policy, the slow-local refresh tier (Design + Decision 8), Decision 5 ZFS-in-VM correction, battery-detection definition in the contract, Phase 11 retirement collateral, Dev-tooling gate answers + Phase 12 test-panel-maint, External-deps verified-on-ratio state + arch-audit known-absent, the fourth file added to the open config-paths decision, and the panel-CSS convention clarification. Added session-split notes to Phases 6/9/12 per the decomposition check. +- Why: close the three blocking gaps and bank the verified facts before a re-review. +- Artifacts: * Review findings all DONE; Decisions 5/8/11 updated; one decision (config paths) remains open for Craig. + +** 2026-07-07 Tue @ 18:55:50 -0500 — Claude Code (archsetup) — reviewer + responder +- What: round-2 re-review confirmed all ten findings resolved with no substantive new gaps. Rubric: Ready pending the one open decision (config/state paths — Craig's call). Three wording nits found and fixed in the same pass: cache_age scoped to network-tier and slow-local alike, the stale "ephemeral" glyph-state wording reconciled to the open decision, the Decisions cookie recomputed to 12/13, plus a CI-that-doesn't-exist phrase softened in Decision 11. +- Why: verify the round-1 folds before handing the last decision to Craig; the spec stays DRAFT until he rules and the cookie reads complete. +- Artifacts: round-2 verdict (per-finding resolved list); Craig paged with the config-paths proposal and the sysmon-affordance re-homing question. + +** 2026-07-07 Tue @ 19:54:04 -0500 — Claude Code (archsetup) — responder +- What: folded Craig's two rulings (config/state paths as proposed — shipped TOML =~/.config/archsetup/=, user curation =~/.config/maint/=, glyph state + hourly cache =~/.local/state/maint/=; btop scratchpad re-homed to the maint glyph's right-click, sysmon-cycle + waybar-sysmon retired), flipped DRAFT → READY → DOING, and decomposed the build into todo.org: 13 phase tasks (all =:solo:=), a Manual-testing task seeded with the eyeball checks, and the flip-to-IMPLEMENTED closer, under a parent stamped =:SPEC_ID:=. vNext (AI assistance, SIGKILL escalation, evidence-row meters) logged =[#D]=. +- Why: Craig approved both recommendations ("go with your recommendations", 2026-07-07); Phase 6 of spec-response owns the READY → DOING flip when the decomposition lands. +- Artifacts: todo.org "Maintenance console build" parent + children; status heading history lines. |
