docs: phase 5 matrix PASSED (13/13) — design round 10, results run 2
The §5.1 dry-run audit gate passes. All 13 rows completed with a non-empty eligible half in every one, and O5 is re-measured on the fixed graph: worst recompute 67 µs across every run, 10 µs mean under deliberate churn, busy fraction 0.0006, readiness 1-2 ms with 18 binds against a 2000 ms budget. Round 10's finding, and it is the third of exactly the same shape: the pipewire-pulse PID derivation required a SINGLE repeated pipewire.sec.pid. WirePlumber repeats one too (two Clients, both sec_pid 1747), so the derivation returned None permanently on a stock desktop, key 4's suppression never fired, and every Pulse-emulated node fused into one owner. Row 1's CLEAN control forwarder and Firefox were both excluded. Fixed in pixelpass 91c4ded by deleting the heuristic: probe every distinct sec_pid and let /proc/<pid>/comm decide. Three measured rounds now, all at the observation boundary, none in the architecture -- and all three were fail-closed and silent, caught only because §5.1 requires asserting what must remain ELIGIBLE. An exclusion-only checklist would have passed every one of these builds. Rows 4-6 are closed through peerspeak's REAL tagging sites (call, mpv, notify, plus clip) with a hand-launched mpv staying eligible, so the cross-repo contract is proven end to end on live nodes. Row 10 covers the full sticky lifecycle including retirement; row 11 is provably non-vacuous (the recycled node.link-group came back byte-identical and did not inherit taint). Recorded and NOT claimed as passes: substitutions in rows 8, 9 and 13, and two reporting-only findings (the audit's sticky flag is uninformative; a bridge's named key is lost when a leg reappears under a new serial). Phase 6 remains blocked by F11-1, phases 0b/0c/0d and the Stereo Mix design call -- this file removes one gate, not all of them.
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
# Design v3: whole-desktop screen-share audio without self-echo
|
||||
|
||||
**Status:** 🟠 **v3.6 — round 9, opened by a second MEASURED finding, this time from a live
|
||||
audit run of the *fixed* observer.** v3.4's architecture is still unchanged and converged.
|
||||
Round 8 revised the **observation boundary** (§6.7); round 9 revises what stickiness is
|
||||
allowed to remember (new §6.8). Both were found by running code, not by reading it.
|
||||
**Date:** 2026-07-25 (v1: 07-19 · v2: 07-20 · Option C 07-20 · v3.1 r4 · v3.2 r5 · v3.3 r6 ·
|
||||
v3.4 r7 · v3.5 r8 · v3.6 r9)
|
||||
**Status:** 🟢 **v3.7 — round 10: the §5.1 matrix PASSED in full and the architecture is
|
||||
unchanged for the third consecutive measured round.** Round 8 revised the **observation
|
||||
boundary** (§6.7), round 9 revised what stickiness may remember (§6.8), and round 10 deletes
|
||||
the pipewire-pulse PID **derivation heuristic** (§6.1.2) after measuring that WirePlumber
|
||||
repeats a `sec_pid` too — which had switched key 4's suppression off permanently. All three
|
||||
were found by running code, not by reading it, and all three were at the *observation*
|
||||
boundary rather than in the design.
|
||||
**Date:** 2026-07-26 (v1: 07-19 · v2: 07-20 · Option C 07-20 · v3.1 r4 · v3.2 r5 · v3.3 r6 ·
|
||||
v3.4 r7 · v3.5 r8 · v3.6 r9 · v3.7 r10)
|
||||
**Origin:** Joe's suggestion — "whitelist all audio except audio coming from peerspeak."
|
||||
**Scope:** a new capture mode in pixelpass (`src/host/pipeline.rs`, `src/host/audio.rs`),
|
||||
playback tagging + AEC-identity export + teardown-ordering invariants in peerspeak.
|
||||
@@ -502,11 +505,47 @@ derive this itself; it cannot assume a value, and peerspeak can supply only a *h
|
||||
- Read `pipewire.sec.pid` from the **Client** objects of Pulse-emulated streams. Measured:
|
||||
it is `2541` for Firefox, Steam, KDE Connect, sunshine and libcanberra alike, while each
|
||||
node's own `application.process.id` differs (Firefox `11114`, sunshine `4119`).
|
||||
- Require a **single consistent** value across those clients, and validate it by reading
|
||||
- ~~Require a **single consistent** value across those clients~~ and validate it by reading
|
||||
`/proc/<pid>/comm` (or cmdline) and confirming it is `pipewire-pulse`.
|
||||
- "This PID owns implausibly many unrelated streams" is a **diagnostic**, never
|
||||
correctness logic.
|
||||
|
||||
> #### 🔴 Round 10 (MEASURED, phase-5 run 2): "a repeated `sec_pid`" does not identify pulse
|
||||
>
|
||||
> The struck rule above was implemented as *the single `sec_pid` shared by two or more
|
||||
> Clients*, on the reasoning that native clients each carry their own distinct PID so only the
|
||||
> Pulse shim repeats a value. **Measured on this host: WirePlumber repeats one too** — it holds
|
||||
> two Clients, `WirePlumber` and `WirePlumber [export]`, both `sec_pid` 1747. Two values
|
||||
> repeated, "single consistent" was unsatisfiable, and the derivation returned `None`
|
||||
> **permanently, on a stock desktop**.
|
||||
>
|
||||
> The consequence was not a missing optimisation. With the daemon PID unknown the key-4
|
||||
> exception never fires, every Pulse-emulated node fuses into one owner, and the result is the
|
||||
> machine-wide over-exclusion cascade of phase 5's F2 — reached again from a new cause, and
|
||||
> caught again only by the §5.1 requirement to assert the **eligible** half of a row.
|
||||
>
|
||||
> The rule failed in both directions, so the repetition test is **deleted** rather than
|
||||
> tightened:
|
||||
>
|
||||
> - **False ambiguity** — any second process holding two Clients defeats it, and WirePlumber
|
||||
> always does.
|
||||
> - **False absence** — a session in which pipewire-pulse holds exactly one Client (one Pulse
|
||||
> app running) repeats nothing at all, so the candidate is never even considered.
|
||||
>
|
||||
> `comm` was always the authoritative check; repetition was a heuristic standing in front of it,
|
||||
> and what it actually encoded was an assumption about *other* processes' Client counts.
|
||||
> **The rule is now: every distinct `pipewire.sec.pid` is a candidate; the daemon is the unique
|
||||
> one whose `/proc/<pid>/comm` is exactly `pipewire-pulse`.** Zero matches ⇒ `None` (nothing we
|
||||
> can prove to suppress). **Several** matches ⇒ also `None`: two live pipewire-pulse daemons (a
|
||||
> nested or sandboxed session) cannot both be suppressed by a single `Option<u32>`, and failing
|
||||
> closed there lands on the over-exclusion side, consistent with the failure-mode paragraph
|
||||
> below. Suppressing a *set* of daemon PIDs is the real answer if a multi-daemon host ever turns
|
||||
> up; it is out of v1 and recorded rather than silently approximated.
|
||||
>
|
||||
> The lesson generalises past this key: **a property of the objects we are trying to identify is
|
||||
> evidence; a property of everyone else's object count is a guess.** The `/proc` read was already
|
||||
> there and already authoritative — the heuristic in front of it only added a way to be wrong.
|
||||
|
||||
Failure modes: if pixelpass fails to identify the real pipewire-pulse PID, the result is
|
||||
broad **over-exclusion** (annoying, safe). If it wrongly suppresses a genuine app PID, the
|
||||
result is over-exclusion **for that app** — safe *only* because unresolved ancestry is
|
||||
@@ -1143,9 +1182,28 @@ Both reviewers agree on all seven. Recorded as decided; reopen only with new evi
|
||||
- **D7 — no materially simpler design exists** that still meets Joe's ask. The available
|
||||
simplification is to *narrow v1 scope*, not to change architecture. ✅
|
||||
|
||||
## 14. Readiness — 🟠 v3.6 (round 9): architecture converged; observation boundary and sticky provenance REVISED.
|
||||
## 14. Readiness — 🟢 v3.7 (round 10): the §5.1 matrix PASSED; architecture unchanged.
|
||||
|
||||
**Round 9 (2026-07-25, same day).** Phase 3r shipped §6.7 and the audit was re-run
|
||||
**Round 10 (2026-07-26).** The phase-5 matrix ran in full and **passed all 13 rows with a
|
||||
non-empty eligible half in every one** — results in
|
||||
`screenshare-audio-exclusion-phase5-results.md`. It also found a third measured defect on
|
||||
first contact, and once again the failure was fail-closed and *silent*, exposed only by the
|
||||
requirement to assert what must remain **eligible**: §6.1.2's pulse-PID derivation returned
|
||||
`None` permanently on this host, so key 4 fused every Pulse-emulated node into one owner.
|
||||
|
||||
| | verdict |
|
||||
| --- | --- |
|
||||
| Architecture — Option C, taint as a graph property, owner-key union, sticky taint, AEC identity state machine | **unchanged, three times vindicated** |
|
||||
| §6.1.2 | **revised** — the derivation heuristic is deleted; `comm` alone decides |
|
||||
| §5.1 matrix | **PASSED** — 13/13, incl. the full sticky lifecycle (row 10) and provable identifier recycling (row 11) |
|
||||
| O5 | **closed on the real graph** — worst recompute 67 µs, churn mean 10 µs, busy fraction 0.0006 |
|
||||
| Phase 5 | **machinery unchanged and correct** — three real defects caught on first contact with the live graph, none of them in the engine |
|
||||
| Phase 6 | **still blocked** — by F11-1, phases 0b/0c/0d, and the "Stereo Mix" design call, *not* by this matrix |
|
||||
|
||||
Three rows passed with recorded substitutions (8 EasyEffects, 9 Firefox's own mic/monitor
|
||||
paths, 13 a real `Audio/Duplex` device) and the third-party samples stay owed.
|
||||
|
||||
**Round 9 (2026-07-25).** Phase 3r shipped §6.7 and the audit was re-run
|
||||
immediately; it found a *second* measured defect within minutes — a permanent sticky taint
|
||||
on a hardware sink (§6.8). Both rounds share a shape worth naming: **the architecture was
|
||||
right and the instrumentation was wrong**, and only running the code against a live daemon
|
||||
@@ -1205,7 +1263,10 @@ How the blockers closed:
|
||||
| **9** | `device_props` tested for one live *Device* rather than one live *global* on the id (Codex, certain) | **fixed** in phase 3r — stale `session_device` on a contested id is an echo path |
|
||||
| **9** | `device.api` corroborated by presence, so `v4l2` under an ALSA factory passed (Codex) | **fixed** in phase 3r — the API must equal the allowlist's own |
|
||||
| **9** | hardware playback-to-capture ("Stereo Mix") defeats the `session_device` classifier (Codex, P1 worth checking) | **OPEN — design decision owed**, §6.8; pre-existing, needs ALSA control inspection |
|
||||
| **9** | the 2 s readiness budget has no calibration argument (Codex) | **OPEN — measurement owed**, §6.8; ~3 ms observed on this host |
|
||||
| **9** | the 2 s readiness budget has no calibration argument (Codex) | **OPEN — measurement owed**, §6.8; 1–2 ms observed on this host with 18 binds (phase-5 run 2) |
|
||||
| **10** | **the pulse-PID derivation required a *single* repeated `sec_pid`; WirePlumber repeats one too, so it returned `None` permanently and key 4's suppression never fired (measured, phase-5 run 2)** | **fixed** — §6.1.2 round-10 box: probe every distinct `sec_pid`, let `/proc/<pid>/comm` decide |
|
||||
| **10** | the audit's `sticky` flag means "is in the remembered set", so it is true for nearly every tainted node and does not answer "excluded only because remembered" | **OPEN — reporting only**; the evidence-only pass §6.8 already computes what is needed |
|
||||
| **10** | a bridge's named key is lost when a leg reappears under a new serial (sticky `reason_for` falls back to keyless, and `raise` will not replace a same-rank reason) | **OPEN — reporting only**; verdict unaffected |
|
||||
|
||||
### v1 scope — agreed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user