aboutsummaryrefslogtreecommitdiff
path: root/docs/specs
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-09 17:57:29 -0500
committerCraig Jennings <c@cjennings.net>2026-07-09 17:57:29 -0500
commit186b27d3ab10ffa40fff8103f35bf1226e05ab3a (patch)
tree4d77fdb834d6dd60cd5c61ea24c9f90c6d1744a7 /docs/specs
parentcb0a67ee022619251747e18edfcb63cf6e5f9fd9 (diff)
downloadarchsetup-186b27d3ab10ffa40fff8103f35bf1226e05ab3a.tar.gz
archsetup-186b27d3ab10ffa40fff8103f35bf1226e05ab3a.zip
docs: close the audio-doctor decisions, spec is READYHEADmain
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.
Diffstat (limited to 'docs/specs')
-rw-r--r--docs/specs/2026-07-09-audio-doctor-spec.org57
1 files changed, 39 insertions, 18 deletions
diff --git a/docs/specs/2026-07-09-audio-doctor-spec.org b/docs/specs/2026-07-09-audio-doctor-spec.org
index 5c4f844..028fe8c 100644
--- a/docs/specs/2026-07-09-audio-doctor-spec.org
+++ b/docs/specs/2026-07-09-audio-doctor-spec.org
@@ -4,16 +4,19 @@
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
-* DRAFT Audio Doctor — diagnose and repair a broken sound stack
+* READY Audio Doctor — diagnose and repair a broken sound stack
:PROPERTIES:
:ID: 6ce3f2ef-052e-40cb-9067-42f8e665f813
:END:
-- [2026-07-09 Thu] DRAFT — written from the roam-inbox ask "look into a doctor button for the audio panel: what happens if pulseaudio or its equivalent is borked? would we know? how could we test for it? what remedies would we have?" Probe set, classifier, and remedy tiers below are grounded in a live survey of ratio's stack. Six Decisions are open; Phase 0 shipped ahead of the rest.
+- [2026-07-09 Thu] READY — all eight Decisions closed. Written from the roam-inbox ask "look into a doctor button for the audio panel: what happens if pulseaudio or its equivalent is borked? would we know? how could we test for it? what remedies would we have?" Probe set, classifier, and remedy tiers are grounded in a live survey of ratio's stack. Phase 0 shipped ahead of the rest.
-* DRAFT Status
+* READY Status
:PROPERTIES:
:ID: 1cc2b355-9450-4ace-bdb3-b935f7051918
:END:
+- [2026-07-09 Thu] READY — Craig closed the six open Decisions, all to the recommended option: DOCTOR is a section-header key; CLI-first with the GUI as a face; hung and dead are two verdicts sharing one remedy; tiers stay Auto/Confirm/Arm as drafted; a stream-active guard refuses the audible remedies and is overridable by pressing again; xruns are out of v1.
+
+ Two consequences worth naming, because they were not obvious when the questions were asked. Taking the guard is *why* remedy 4 can stay at Confirm — the danger lives in the state of the machine, not the identity of the remedy. And the guard must read its stream state from =pw-dump= rather than =pactl=, because the remedies it protects are exactly the ones you reach for when the Pulse layer is dead; a =pactl=-fed guard would go blind precisely when it is needed.
- [2026-07-09 Thu] DRAFT — Phase 0 shipped (dotfiles =4d42eb3=) and its Decision closed. The first draft claimed =build_status()= had no timeout and froze the panel; driving it disproved that — =pactl.run()= already times out. The real bug was narrower: two of the four reads sat outside the degrade guard, killing waybar's audio module. The claim is corrected in place rather than quietly dropped.
- [2026-07-09 Thu] DRAFT — spine complete, Decisions open. Not ready to build: the remedy tiers touch a running meeting's audio, and the classifier's precedence needs Craig's call before code.
@@ -21,7 +24,7 @@
| Field | Value |
|--------+-------------------------------------------------------------------------------------------------------------|
-| Status | draft |
+| Status | ready |
|--------+-------------------------------------------------------------------------------------------------------------|
| Owner | Craig Jennings |
|--------+-------------------------------------------------------------------------------------------------------------|
@@ -80,7 +83,9 @@ Four probe tiers, cheapest and most reliable first. Each is independently useful
=pactl info= under a hard timeout. Three outcomes, and they are *different faults*: answered / refused / hung. The hung case is the one that currently freezes tooling, and it is invisible to a naive "does pactl work" check because that check hangs too.
*** Tier 4 — semantic health (only meaningful once 1–3 pass)
-Default sink and source resolve to devices that exist; at least one sink is not =SUSPENDED= when a stream is playing; xrun and suspend lines in the unit journal since boot. Ratio's sink states today are =RUNNING= and =SUSPENDED=; =SUSPENDED= alone is normal idle, not a fault.
+Default sink and source resolve to devices that exist; at least one sink is not =SUSPENDED= when a stream is playing. Ratio's sink states today are =RUNNING= and =SUSPENDED=; =SUSPENDED= alone is normal idle, not a fault.
+
+Xruns are deliberately *not* probed in v1 — see the Decision "Do xruns belong in v1?" below. They are a quality signal with no remedy in this feature's vocabulary, and a finding nobody can act on trains the user to ignore the wall.
** "What remedies would we have?"
@@ -213,16 +218,21 @@ Rejected on evidence. =pactl= hangs against a hung server, and it is dead when t
Rejected. Remedies 3 through 5 are audible and, in a meeting, disruptive. Diagnosis is free and should be; repair is a press.
-* Decisions [2/8]
+* Decisions [8/8]
-** TODO Where does the DOCTOR key live, and does it replace anything?
-The net panel puts diagnose and repair behind one console key. The audio panel's console row already carries INPUT, OUTPUT, and PUSH TALK. Does DOCTOR join that row, or sit in a section header like the maint console's CLEAN UP / REVIEW & FIX pair?
+** DONE Where does the DOCTOR key live, and does it replace anything?
+CLOSED: [2026-07-09 Thu]
+Resolved: a section-header key, the maint console's CLEAN UP / REVIEW & FIX shape. Diagnose and fix are two presses with different consequences, and the header is where the console already teaches that distinction. The console row (INPUT / OUTPUT / PUSH TALK) stays device controls; a doctor is not one. Nothing is replaced.
-** TODO Is there an =audio doctor= CLI, and is it the same code path?
-The maint console is CLI-first with the GUI as a face; the net panel ships =net doctor= and =net-doctor= as a make target. A CLI here makes the whole feature testable without a display and usable from a TTY when the desktop's sound is dead — arguably the most valuable case. Confirm CLI-first, and whether it prints the same wall text.
+** DONE Is there an =audio doctor= CLI, and is it the same code path?
+CLOSED: [2026-07-09 Thu]
+Resolved: CLI-first, GUI as a face, one code path — the maint console's architecture. =audio doctor= (read-only) and =audio doctor --fix= print the same findings the wall streams.
-** TODO Confirm the classifier precedence above, particularly rows 3 and 4.
-Should "pulse compat hung" and "pulse compat down" be one verdict with a detail line, or two verdicts? They share a remedy (restart =pipewire-pulse=) but they are different failures, and the hung case is the one that freezes other tooling.
+The deciding reason: when the desktop's sound is dead, a TTY is where you will be standing. A GUI-only doctor cannot serve the case that motivates the whole feature. It also makes the classifier testable without a display.
+
+** DONE Confirm the classifier precedence above, particularly rows 3 and 4.
+CLOSED: [2026-07-09 Thu]
+Resolved: precedence as drafted, and rows 3 and 4 stay *two verdicts sharing one remedy*. They share a fix but not a diagnosis, and the hung case is the one that freezes other tooling — collapsing them would hide the more interesting fault behind the more common one.
** DONE Does the panel's own status build get the same timeout treatment?
CLOSED: [2026-07-09 Thu]
@@ -230,14 +240,25 @@ Investigated and answered: it already had one. =pactl.run()= defaults to =timeou
What the investigation *did* find is a narrower, real bug, now fixed (dotfiles =4d42eb3=): =build_status()= guarded its two device-list reads but called =default_sink()= and =default_source()= outside the guard, so a server that answered the lists and then stopped answering raised =PactlTimeout= out of a function whose contract is to degrade. =waybar-audio= calls it with nothing catching it, so the bar's audio module died rather than dimming. A server that hangs on the *first* call never exposed this — =list_sinks= raised inside the guard and masked everything behind it, which is why the obvious test would have passed against broken code.
-** TODO Which remedies are Auto, which Confirm, which Arm?
-The table above proposes 1–2 Auto, 3–4 Confirm, 5 Arm. Remedy 4 drops Pulse clients briefly — is a Confirm enough, or does it want the arm treatment too when a stream is currently playing?
+** DONE Which remedies are Auto, which Confirm, which Arm?
+CLOSED: [2026-07-09 Thu]
+Resolved: the table as drafted. Remedies 1–2 (restart wireplumber, set a present default) are Auto; 3–4 (unmute + restore volume, restart pipewire-pulse) are Confirm; 5 (restart pipewire) is Arm.
-** TODO Should the doctor refuse to run a disruptive remedy while audio is playing?
-A "a stream is active — this will interrupt it" refusal, overridable by pressing again, would mirror the maintenance console's live-update guard. It is the same shape of problem: an operation that is fine when idle and destructive mid-use. Worth the machinery, or is the arm line's wording enough?
+Remedy 4 stays at Confirm rather than Arm *because* the stream guard below was taken. The guard, not the tier, is what catches the mid-meeting case — which is the honest place for it, since the danger is the state of the machine, not the identity of the remedy. Had the guard been declined, remedy 4 would have moved to Arm.
-** TODO Do xruns belong in v1?
-Tier 4 can count xrun and suspend lines in the unit journal. That is a *quality* signal, not a broken/working one, and it has no remedy in this feature's vocabulary. Report it as a note, or leave it out entirely until there is something to do about it?
+** DONE Should the doctor refuse to run a disruptive remedy while audio is playing?
+CLOSED: [2026-07-09 Thu]
+Resolved: yes. A stream-active guard refuses a disruptive remedy and is overridable by pressing again, mirroring the maintenance console's live-update guard. Same shape of problem: an operation that is fine when idle and destructive mid-use, where wording alone does not stop a press.
+
+Implementation note for the builder: the guard's input is "is any node running" — and it must come from *Tier 2* (=pw-dump=), not from =pactl=. The remedies the guard protects are precisely the ones you reach for when the Pulse layer is dead or hung, so a guard that asked =pactl= would go blind exactly when it is needed, and a PipeWire-native client can be mid-playback while =pactl= answers nothing at all.
+
+A cold or unreadable graph means the guard cannot see a stream. Treat that as *no stream* and let the remedy proceed: refusing on an unreadable graph would wedge the doctor shut in the worst case, which is the fault it exists to repair. Say so in the wall, so the user knows the guard abstained rather than passed.
+
+The guard applies to remedies 3, 4, and 5 — the audible ones. Its refusal names what is playing, not just the remedy.
+
+** DONE Do xruns belong in v1?
+CLOSED: [2026-07-09 Thu]
+Resolved: left out. They are a quality signal, not a broken/working one, and they have no remedy in this feature's vocabulary. A finding you cannot act on trains you to ignore the wall — the opposite of what a doctor is for. Revisit when there is something to do about them.
* Implementation phases