aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-08-19 14:56:37 -0700
committerCraig Jennings <c@cjennings.net>2026-08-19 14:56:37 -0700
commitdc068cab17624721c274e6810391308a920f57af (patch)
tree0c98681fc7b199c9b9480d8d62c91892d67b2346
parentafd1f386c2769a1e8b3bc8f388a0dfaec0fd27e7 (diff)
downloadarchsetup-dc068cab17624721c274e6810391308a920f57af.tar.gz
archsetup-dc068cab17624721c274e6810391308a920f57af.zip
chore(tasks): archive the closed work and convert finished sub-tasksHEADmain
Routine cleanup. The net-scenarios timeout subtree moves to the archive now that it is closed, an aged clock-panel subtree goes with it, and finished sub-tasks are rewritten as dated log entries under their parents.
-rw-r--r--archive/task-archive.org3
-rw-r--r--todo.org411
2 files changed, 204 insertions, 210 deletions
diff --git a/archive/task-archive.org b/archive/task-archive.org
index 42d9653..2925180 100644
--- a/archive/task-archive.org
+++ b/archive/task-archive.org
@@ -1624,3 +1624,6 @@ Retrofit the net doctor (=~/.dotfiles/net/src/net/doctor.py=) and bluetooth doct
Scope note (Craig, 2026-07-07): realtime lamp *behavior* only. The maintenance console's wider results-wall layout (date+time stamp column, COPY, persistent history) does NOT backport — the net/bt panels are ~400px wide and lack the horizontal real estate. Their existing output wells keep their compact layout; this task just makes them stream live.
Addendum (Craig, 2026-07-07): DO backport the 3.5-entry height convention — every panel's output well caps at 3.5 visible entries, the half-visible entry being the scroll cue, with the dark slate-on-black scrollbar. Layout stays compact per above; only the height cap + scroll affordance carries over.
+** DONE [#B] Absorb the clock-panel project into the dotfiles :feature:waybar:dotfiles:
+CLOSED: [2026-07-18 Sat]
+Absorbed into =~/.dotfiles= (commit 3fab11d): package =clock/src/clock/= (renamed from clock_panel), the six PNG watchface layers packaged inside the module at =clock/src/clock/assets/=, a stowed =clock-panel= shell shim (LD_PRELOADs gtk4-layer-shell), waybar left-click now =clock-panel toggle= with the absolute path dropped, tests converted pytest→unittest into =tests/clock/= plus an asset-load guard. Kept the layer-shell overlay and the socket toggle. The standalone repo is archived (ARCHIVED.md), kept for its design history. Verified live: the bar click renders the polished watchface.
diff --git a/todo.org b/todo.org
index 6f16129..af8d75a 100644
--- a/todo.org
+++ b/todo.org
@@ -150,29 +150,6 @@ Filed as a VERIFY because the capture asks for a conversation rather than
describing a defect. What is the clock avoiding being dragged by, and is the
avoidance the bug or the intended behaviour?
-** CANCELLED [#B] agent-text relay reports success for a message that went nowhere :bug:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:CREATED: [2026-08-19 Wed]
-:LAST_REVIEWED: 2026-08-19
-:END:
-
-Not a defect. rulesets refuted it with measurements and I reproduced theirs
-before accepting: on velox, whose account store is empty,
-=signal-cli -a +15550000000 send= exits 1 with "User +15550000000 is not
-registered", and =ssh 100.71.182.1 'exit 7'= returns 7, so a non-zero code
-propagates faithfully back through the relay. The loop's
-=[ "$rc" -eq 0 ] && break= therefore advances to the next host exactly as
-intended. signal-cli fails closed.
-
-I filed this off a conditional in their handoff — ".emacs.d raised a case
-neither of you tested ... *if* signal-cli send exits zero against an empty
-account store" — and turned the "if" into a graded [#B] with a =:blocked:= tag
-on another project, without running the one command that settles it. The
-machine that proves it was in front of me the whole time. Their ask is fair and
-I am recording it rather than the outcome alone: verify before filing a defect
-against someone else's work, especially one carrying a blocking tag.
-
** TODO [#C] A failed hostname lookup takes seven seconds :bug:
:PROPERTIES:
:CREATED: [2026-08-19 Wed]
@@ -201,154 +178,6 @@ Grading: Minor severity (nothing behaves wrong, it just waits) x some users
sometimes (every failed lookup, which is occasional rather than constant) = P3 =
[#C].
-** DONE [#B] Clock/DNS bootstrap deadlock — recovery needs a second device :bug:velox:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:CREATED: [2026-08-19 Wed]
-:LAST_REVIEWED: 2026-08-19
-:END:
-
-The installer wrote both halves of a deadlock. =configure_dns= pins
-=DNSOverTLS=yes= with =DNSSEC=yes=, and both validate against the wall clock;
-the chrony step enables chronyd without writing a config, so the machine runs
-Arch's stock one whose only source is =pool 2.arch.pool.ntp.org= — a hostname.
-Boot with a wrong clock and DoT certificate validation fails, so nothing
-resolves; chrony then cannot resolve its pool, so the clock stays wrong.
-Neither side moves. It caught velox on the road 2026-08-19 and had to be
-diagnosed from a phone.
-
-Fixed at the root: the installer now writes
-=/etc/chrony.d/10-bootstrap-ip-ntp.conf= with two IP-addressed Cloudflare
-sources and points stock chrony.conf at the drop-in. An address needs no DNS
-and carries no certificate, so the escape hatch holds whatever broke the clock.
-velox has the same drop-in applied live, verified with =chronyc -n sources=
-(=162.159.200.1= selected) and =timedatectl= reporting synchronized.
-
-What is left here is the part I could not verify: the decisive test is a full
-power-down and cold boot, confirming the clock corrects itself untouched. See
-the manual-testing entry. Until that runs, the fix is sound by construction
-rather than demonstrated.
-
-Grading: Critical severity (total loss of network — no DNS means no egress, and
-recovery needs a second device) x some users sometimes (only machines that boot
-with a wrong clock, which is any RTC fault, BIOS reset, or drained cell) = P2 =
-[#B]. Graded on the being-in-it, not the getting-into-it: once the machine is in
-this state it is fully offline with no local path out.
-
-*** 2026-08-19 Wed @ 12:25:00 -0700 Reproduced it, and the mechanism was not what either of us said
-I wound velox's clock back 27 days with chronyd stopped and watched it fail.
-Resolution died outright, and plain UDP/53 to 1.1.1.1 kept answering throughout
-— the discriminator the doctor keys on, confirmed live rather than reasoned.
-
-The cause is DNSSEC, not DNS-over-TLS. resolved logged =signature-expired=
-against the root DNSKEY and every DS beneath it. The DoT handshake to
-=1.1.1.1:853= verified clean at that same clock, and the Cloudflare certificate
-runs Dec 2025 to Dec 2026, so it was never outside its window. An RRSIG window
-is days to weeks and a certificate is good for a year, so a skew that breaks
-DNSSEC normally leaves DoT untouched. The phone session blamed the certificate
-and I carried that forward into the first commit; both were wrong.
-
-=DNSSEC=allow-downgrade= does not rescue it either, which matters because it is
-the obvious reach and it is what ratio runs. resolved downgrades when a server
-lacks DNSSEC support, and a signature-window failure is a validation failure, so
-no downgrade fires. Six retries over eighteen seconds plus
-=resolvectl reset-server-features=, all dead. I briefly believed otherwise off a
-test whose success was a cache hit (=Data from: cache network=).
-
-So ratio was exposed after all, and I have given it the same drop-in. Its
-=162.159.200.1= is selected and its clock is synchronized.
-
-The fix itself is verified end to end: with the clock wound back and no DNS at
-all, chronyd reached the IP-addressed source and stepped the clock from
-2026-07-23 straight back to 2026-08-19. That is the whole claim, demonstrated
-rather than argued.
-
-Also settled: the clock landed on 2026-07-23 because that is systemd 261.2's
-build date to the minute (=/usr/lib/systemd/systemd=, 10:43:59), and systemd
-advances a garbage RTC to its own build epoch at boot. Not timesyncd's
-last-good-sync timestamp, which cannot be it — timesyncd is disabled here. That
-also confirms the RTC really was reading earlier than that, so the coin cell
-stays the prime suspect.
-
-*** 2026-08-19 Wed @ 10:12:00 -0700 Root fix, doctor verdict, and taxonomy entry landed
-The installer carries the drop-in; =post-rebuild-check= grew a sixth check that
-fails a machine whose every NTP source is a hostname; the net failure taxonomy
-gained the mode in its DNS layer plus a cluster 5 triage line, and its existing
-egress-layer clock entry now says outright that its remedy does not apply when
-DoT or DNSSEC is on.
-
-The doctor half is in dotfiles: =classify.py= reached "DNS not resolving → net
-repair dns-test" here, which cannot help, because every public resolver fails
-the same clock-sensitive validation — so the doctor sent you round a loop. It
-now emits a =clock-dns= row ahead of the generic DNS verdict. Detection is
-deliberately DNS-free: a local =timedatectl= read for sync state, and a bypass
-query addressed by IP over plain UDP/53 to tell "resolved is refusing to
-validate" apart from "DNS is genuinely dead".
-
-** DONE [#C] DNSSEC strictness on the travelling laptop :velox:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:CREATED: [2026-08-19 Wed]
-:LAST_REVIEWED: 2026-08-19
-:END:
-
-Craig chose =allow-downgrade= everywhere. Applied to velox, ratio, and the
-installer, and ratio's =DNSOverTLS= tightened from =opportunistic= to =yes= in
-the same pass, so all three now agree: encrypted DNS always, validation
-best-effort.
-
-The reasoning that settled it: the deadlock is fixed by the IP-addressed NTP
-source, and =allow-downgrade= was measured not to help with it at all. What
-=allow-downgrade= does buy is the venue-resolver case the taxonomy documents,
-where =yes= turns a resolver that mangles DNSSEC records into no answer at all.
-That is a hotel and airport problem, so it is velox's problem, and the
-encryption is the half worth being strict about.
-
-** CANCELLED [#C] Branch network policy on laptop vs desktop in the installer :feature:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:CREATED: [2026-08-19 Wed]
-:LAST_REVIEWED: 2026-08-19
-:END:
-
-Cancelled because the decision above emptied it. All three motivating cases now
-want the same value on every machine: =DNSSEC=allow-downgrade=, a stable
-per-network wifi MAC, and an IP-addressed NTP source. A branch with nothing to
-put on either side is machinery built for a divergence that does not exist, and
-it would be the kind of scaffolding that rots unread.
-
-Worth keeping the observation, which is the part with a shelf life: when a
-network default does need to differ by machine class, the test already exists.
-=ls /sys/class/power_supply/BAT*= is what =prune_waybar_battery=, the ppd mask,
-and the TLP config all key on. Reopen this then rather than building it now.
-
-** DONE [#C] Automate the clock/DNS deadlock repair in the net doctor :feature:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:CREATED: [2026-08-19 Wed]
-:LAST_REVIEWED: 2026-08-19
-:END:
-
-Shipped as the =clock-ip-ntp= repair, and the verdict is =fixable= rather than
-terminal. Both open questions got answered by driving a real deadlock instead of
-reasoning about it: =chronyc add server= returns =200 OK= against a running
-chronyd, and =makestep= needs a sample to land, so it took four calls and about
-eight seconds rather than working on the first. The repair retries accordingly.
-
-Verified end to end on velox against a genuine deadlock (wrong clock, chronyd
-running with only an unresolvable hostname source, DNS dead): the repair
-corrected the clock in 6.1 seconds and DNS came back.
-
-The live run also caught a defect no unit test would have. The doctor reported
-"Saved password for SpectrumSetup-3C was rejected" — because
-=_recent_auth_failure= greps =journalctl --since -5min=, and a clock weeks off
-windows onto a different incident's entries. It would have sent Craig to
-re-enter a password that was never wrong. Fixed twice over: the journal half is
-now skipped when the clock is untrustworthy, and the clock verdict is ordered
-above the auth verdict, since everything below it reasons over timestamps that
-only mean something once the clock is right. Airplane mode and hard rfkill stay
-above, being physical states the clock has no bearing on.
-
** TODO [#A] Reseat velox input-cover ribbon — phantom power button :bug:velox:hardware:
DEADLINE: <2026-08-14 Fri>
:PROPERTIES:
@@ -1139,42 +968,6 @@ Handoff from home (2026-07-25), originally combining the 2026-06-07 stale-compos
2. On =Upgrade= of =fontconfig=, =freetype2=, or =harfbuzz=, run =/usr/bin/fc-cache -f= after the transaction. The fontconfig 2.17→2.18 cache-format change left stale cache-9 files that crashed Qt6 apps in =FcCharSetHasChar= until the system font cache was rebuilt.
Acceptance: hook files are source-controlled and installed by archsetup; package/operation/action fields are asserted from the generated hook text; the reminder is print-only and exits successfully; the font hook runs only after successful matching upgrades and invokes the absolute =fc-cache= path. Validate with the fast installer tests plus a disposable pacman-hook parser/install check when practical.
-** DONE [#D] net-scenarios harness times out under back-to-back suite runs :test:tooling:
-CLOSED: [2026-08-19 Wed]
-:PROPERTIES:
-:LAST_REVIEWED: 2026-07-24
-:END:
-=tests/net-scenarios/test_run_net_scenarios.py= errored on all 5 tests twice during round 12, each time =subprocess.TimeoutExpired= after its 20s budget on =scripts/testing/run-net-scenarios.sh --target root@fake-vm=. Both occurrences were in =make test-unit= runs launched immediately after a previous full run. It then passed 6 runs in a row (3 on a pristine tree, 3 with the round-12 change), and standalone it finishes in 0.09s, so this is not a regression from any code change.
-
-The harness stubs =ssh=, =rsync= and =jq= onto =PATH=, so nothing should touch the network at all — which is what makes a 20s timeout suspicious rather than merely slow. Worth reproducing under load before deciding whether the fix is a larger timeout or a real hang in the script. Evidence logs from the round: =/tmp/tu.log= and =/tmp/tu2.log= (tmpfs, gone after reboot).
-
-Not graded on the bug matrix: it is test infrastructure, not the shipped codebase.
-
-*** 2026-08-08 Sat @ 05:05:00 -0500 Recurred under concurrent load, same signature
-All 5 tests hit the 20s TimeoutExpired again during a =make test-unit= run
-that overlapped two review subagents running their own suites on the box.
-Standalone immediately after: 0.095s, all pass; the following quiet-machine
-full run was clean. Confirms the load-sensitivity read — reproduce under
-deliberate load before choosing between a bigger budget and a real hang.
-
-
-*** 2026-08-19 Wed @ 14:50:00 -0700 Root-caused and fixed: inherited stdin, not load
-Not load, and not the network. The harness stubs ssh as =cat >/dev/null=, which
-drains stdin to EOF. With no explicit stdin the stub inherits whatever the test
-runner had, so it returned instantly when stdin was redirected and blocked
-forever when it was a terminal or a live pipe. All five tests then burned their
-20-second budget.
-
-That is why it looked like a load effect: a run launched immediately after
-another inherited a different stdin than a standalone invocation. A/B measured
-today — =make test-unit </dev/null= exits 0, the same target with an open pipe
-on stdin hangs on all five. The note above guessed at "a larger timeout or a
-real hang in the script" and it was neither.
-
-Fixed by pinning =stdin=subprocess.DEVNULL= in =run_script=. Verified both ways:
-the previously-failing open-pipe case and the redirected case both pass in
-0.08s, and a full =make test-unit= under a live pipe is clean across 50 suites.
-
** VERIFY Should coredump entries group as one journal-digest row per binary? :maint:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-24
@@ -3069,9 +2862,6 @@ The maintenance console's coredump metric flagged telega-server on ratio (8 core
* Archsetup Resolved
-** DONE [#B] Absorb the clock-panel project into the dotfiles :feature:waybar:dotfiles:
-CLOSED: [2026-07-18 Sat]
-Absorbed into =~/.dotfiles= (commit 3fab11d): package =clock/src/clock/= (renamed from clock_panel), the six PNG watchface layers packaged inside the module at =clock/src/clock/assets/=, a stowed =clock-panel= shell shim (LD_PRELOADs gtk4-layer-shell), waybar left-click now =clock-panel toggle= with the absolute path dropped, tests converted pytest→unittest into =tests/clock/= plus an asset-load guard. Kept the layer-shell overlay and the socket toggle. The standalone repo is archived (ARCHIVED.md), kept for its design history. Verified live: the bar click renders the polished watchface.
** DONE [#A] Velox boot recovery — no kernel in BE :bug:velox:zfs:
CLOSED: [2026-07-19 Sun]
Recovered. Velox boots linux-lts 6.18.38 and is back on the tailnet (up 1d+, /boot holds initramfs-linux-lts.img). The pre-pacman ZFS snapshot rollback restored the kernel from the ZBM recovery shell.
@@ -4431,3 +4221,204 @@ CLOSED: [2026-08-08 Sat]
Killed at the 2026-08-08 task review: an undated annual intention that never
fired — pain points get surfaced organically as they bite.
Once-yearly systematic inventory of known deficiencies and friction points in current toolset
+** CANCELLED [#B] agent-text relay reports success for a message that went nowhere :bug:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:CREATED: [2026-08-19 Wed]
+:LAST_REVIEWED: 2026-08-19
+:END:
+
+Not a defect. rulesets refuted it with measurements and I reproduced theirs
+before accepting: on velox, whose account store is empty,
+=signal-cli -a +15550000000 send= exits 1 with "User +15550000000 is not
+registered", and =ssh 100.71.182.1 'exit 7'= returns 7, so a non-zero code
+propagates faithfully back through the relay. The loop's
+=[ "$rc" -eq 0 ] && break= therefore advances to the next host exactly as
+intended. signal-cli fails closed.
+
+I filed this off a conditional in their handoff — ".emacs.d raised a case
+neither of you tested ... *if* signal-cli send exits zero against an empty
+account store" — and turned the "if" into a graded [#B] with a =:blocked:= tag
+on another project, without running the one command that settles it. The
+machine that proves it was in front of me the whole time. Their ask is fair and
+I am recording it rather than the outcome alone: verify before filing a defect
+against someone else's work, especially one carrying a blocking tag.
+** DONE [#B] Clock/DNS bootstrap deadlock — recovery needs a second device :bug:velox:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:CREATED: [2026-08-19 Wed]
+:LAST_REVIEWED: 2026-08-19
+:END:
+
+The installer wrote both halves of a deadlock. =configure_dns= pins
+=DNSOverTLS=yes= with =DNSSEC=yes=, and both validate against the wall clock;
+the chrony step enables chronyd without writing a config, so the machine runs
+Arch's stock one whose only source is =pool 2.arch.pool.ntp.org= — a hostname.
+Boot with a wrong clock and DoT certificate validation fails, so nothing
+resolves; chrony then cannot resolve its pool, so the clock stays wrong.
+Neither side moves. It caught velox on the road 2026-08-19 and had to be
+diagnosed from a phone.
+
+Fixed at the root: the installer now writes
+=/etc/chrony.d/10-bootstrap-ip-ntp.conf= with two IP-addressed Cloudflare
+sources and points stock chrony.conf at the drop-in. An address needs no DNS
+and carries no certificate, so the escape hatch holds whatever broke the clock.
+velox has the same drop-in applied live, verified with =chronyc -n sources=
+(=162.159.200.1= selected) and =timedatectl= reporting synchronized.
+
+What is left here is the part I could not verify: the decisive test is a full
+power-down and cold boot, confirming the clock corrects itself untouched. See
+the manual-testing entry. Until that runs, the fix is sound by construction
+rather than demonstrated.
+
+Grading: Critical severity (total loss of network — no DNS means no egress, and
+recovery needs a second device) x some users sometimes (only machines that boot
+with a wrong clock, which is any RTC fault, BIOS reset, or drained cell) = P2 =
+[#B]. Graded on the being-in-it, not the getting-into-it: once the machine is in
+this state it is fully offline with no local path out.
+
+*** 2026-08-19 Wed @ 12:25:00 -0700 Reproduced it, and the mechanism was not what either of us said
+I wound velox's clock back 27 days with chronyd stopped and watched it fail.
+Resolution died outright, and plain UDP/53 to 1.1.1.1 kept answering throughout
+— the discriminator the doctor keys on, confirmed live rather than reasoned.
+
+The cause is DNSSEC, not DNS-over-TLS. resolved logged =signature-expired=
+against the root DNSKEY and every DS beneath it. The DoT handshake to
+=1.1.1.1:853= verified clean at that same clock, and the Cloudflare certificate
+runs Dec 2025 to Dec 2026, so it was never outside its window. An RRSIG window
+is days to weeks and a certificate is good for a year, so a skew that breaks
+DNSSEC normally leaves DoT untouched. The phone session blamed the certificate
+and I carried that forward into the first commit; both were wrong.
+
+=DNSSEC=allow-downgrade= does not rescue it either, which matters because it is
+the obvious reach and it is what ratio runs. resolved downgrades when a server
+lacks DNSSEC support, and a signature-window failure is a validation failure, so
+no downgrade fires. Six retries over eighteen seconds plus
+=resolvectl reset-server-features=, all dead. I briefly believed otherwise off a
+test whose success was a cache hit (=Data from: cache network=).
+
+So ratio was exposed after all, and I have given it the same drop-in. Its
+=162.159.200.1= is selected and its clock is synchronized.
+
+The fix itself is verified end to end: with the clock wound back and no DNS at
+all, chronyd reached the IP-addressed source and stepped the clock from
+2026-07-23 straight back to 2026-08-19. That is the whole claim, demonstrated
+rather than argued.
+
+Also settled: the clock landed on 2026-07-23 because that is systemd 261.2's
+build date to the minute (=/usr/lib/systemd/systemd=, 10:43:59), and systemd
+advances a garbage RTC to its own build epoch at boot. Not timesyncd's
+last-good-sync timestamp, which cannot be it — timesyncd is disabled here. That
+also confirms the RTC really was reading earlier than that, so the coin cell
+stays the prime suspect.
+
+*** 2026-08-19 Wed @ 10:12:00 -0700 Root fix, doctor verdict, and taxonomy entry landed
+The installer carries the drop-in; =post-rebuild-check= grew a sixth check that
+fails a machine whose every NTP source is a hostname; the net failure taxonomy
+gained the mode in its DNS layer plus a cluster 5 triage line, and its existing
+egress-layer clock entry now says outright that its remedy does not apply when
+DoT or DNSSEC is on.
+
+The doctor half is in dotfiles: =classify.py= reached "DNS not resolving → net
+repair dns-test" here, which cannot help, because every public resolver fails
+the same clock-sensitive validation — so the doctor sent you round a loop. It
+now emits a =clock-dns= row ahead of the generic DNS verdict. Detection is
+deliberately DNS-free: a local =timedatectl= read for sync state, and a bypass
+query addressed by IP over plain UDP/53 to tell "resolved is refusing to
+validate" apart from "DNS is genuinely dead".
+** DONE [#C] DNSSEC strictness on the travelling laptop :velox:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:CREATED: [2026-08-19 Wed]
+:LAST_REVIEWED: 2026-08-19
+:END:
+
+Craig chose =allow-downgrade= everywhere. Applied to velox, ratio, and the
+installer, and ratio's =DNSOverTLS= tightened from =opportunistic= to =yes= in
+the same pass, so all three now agree: encrypted DNS always, validation
+best-effort.
+
+The reasoning that settled it: the deadlock is fixed by the IP-addressed NTP
+source, and =allow-downgrade= was measured not to help with it at all. What
+=allow-downgrade= does buy is the venue-resolver case the taxonomy documents,
+where =yes= turns a resolver that mangles DNSSEC records into no answer at all.
+That is a hotel and airport problem, so it is velox's problem, and the
+encryption is the half worth being strict about.
+** CANCELLED [#C] Branch network policy on laptop vs desktop in the installer :feature:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:CREATED: [2026-08-19 Wed]
+:LAST_REVIEWED: 2026-08-19
+:END:
+
+Cancelled because the decision above emptied it. All three motivating cases now
+want the same value on every machine: =DNSSEC=allow-downgrade=, a stable
+per-network wifi MAC, and an IP-addressed NTP source. A branch with nothing to
+put on either side is machinery built for a divergence that does not exist, and
+it would be the kind of scaffolding that rots unread.
+
+Worth keeping the observation, which is the part with a shelf life: when a
+network default does need to differ by machine class, the test already exists.
+=ls /sys/class/power_supply/BAT*= is what =prune_waybar_battery=, the ppd mask,
+and the TLP config all key on. Reopen this then rather than building it now.
+** DONE [#C] Automate the clock/DNS deadlock repair in the net doctor :feature:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:CREATED: [2026-08-19 Wed]
+:LAST_REVIEWED: 2026-08-19
+:END:
+
+Shipped as the =clock-ip-ntp= repair, and the verdict is =fixable= rather than
+terminal. Both open questions got answered by driving a real deadlock instead of
+reasoning about it: =chronyc add server= returns =200 OK= against a running
+chronyd, and =makestep= needs a sample to land, so it took four calls and about
+eight seconds rather than working on the first. The repair retries accordingly.
+
+Verified end to end on velox against a genuine deadlock (wrong clock, chronyd
+running with only an unresolvable hostname source, DNS dead): the repair
+corrected the clock in 6.1 seconds and DNS came back.
+
+The live run also caught a defect no unit test would have. The doctor reported
+"Saved password for SpectrumSetup-3C was rejected" — because
+=_recent_auth_failure= greps =journalctl --since -5min=, and a clock weeks off
+windows onto a different incident's entries. It would have sent Craig to
+re-enter a password that was never wrong. Fixed twice over: the journal half is
+now skipped when the clock is untrustworthy, and the clock verdict is ordered
+above the auth verdict, since everything below it reasons over timestamps that
+only mean something once the clock is right. Airplane mode and hard rfkill stay
+above, being physical states the clock has no bearing on.
+** DONE [#D] net-scenarios harness times out under back-to-back suite runs :test:tooling:
+CLOSED: [2026-08-19 Wed]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-24
+:END:
+=tests/net-scenarios/test_run_net_scenarios.py= errored on all 5 tests twice during round 12, each time =subprocess.TimeoutExpired= after its 20s budget on =scripts/testing/run-net-scenarios.sh --target root@fake-vm=. Both occurrences were in =make test-unit= runs launched immediately after a previous full run. It then passed 6 runs in a row (3 on a pristine tree, 3 with the round-12 change), and standalone it finishes in 0.09s, so this is not a regression from any code change.
+
+The harness stubs =ssh=, =rsync= and =jq= onto =PATH=, so nothing should touch the network at all — which is what makes a 20s timeout suspicious rather than merely slow. Worth reproducing under load before deciding whether the fix is a larger timeout or a real hang in the script. Evidence logs from the round: =/tmp/tu.log= and =/tmp/tu2.log= (tmpfs, gone after reboot).
+
+Not graded on the bug matrix: it is test infrastructure, not the shipped codebase.
+
+*** 2026-08-08 Sat @ 05:05:00 -0500 Recurred under concurrent load, same signature
+All 5 tests hit the 20s TimeoutExpired again during a =make test-unit= run
+that overlapped two review subagents running their own suites on the box.
+Standalone immediately after: 0.095s, all pass; the following quiet-machine
+full run was clean. Confirms the load-sensitivity read — reproduce under
+deliberate load before choosing between a bigger budget and a real hang.
+
+
+*** 2026-08-19 Wed @ 14:50:00 -0700 Root-caused and fixed: inherited stdin, not load
+Not load, and not the network. The harness stubs ssh as =cat >/dev/null=, which
+drains stdin to EOF. With no explicit stdin the stub inherits whatever the test
+runner had, so it returned instantly when stdin was redirected and blocked
+forever when it was a terminal or a live pipe. All five tests then burned their
+20-second budget.
+
+That is why it looked like a load effect: a run launched immediately after
+another inherited a different stdin than a standalone invocation. A/B measured
+today — =make test-unit </dev/null= exits 0, the same target with an open pipe
+on stdin hangs on all five. The note above guessed at "a larger timeout or a
+real hang in the script" and it was neither.
+
+Fixed by pinning =stdin=subprocess.DEVNULL= in =run_script=. Verified both ways:
+the previously-failing open-pipe case and the redirected case both pass in
+0.08s, and a full =make test-unit= under a live pipe is clean across 50 suites.