docs: round 9 — uncertainty is not history (design v3.6)
Phase 3r landed §6.7 and the phase-5 audit was re-run against it immediately.
It found a second measured defect within minutes: a real hardware sink
carrying `unresolved-ancestry` permanently, from one link observed while its
output node was still unbound during enumeration. Round 8 made that
systematic rather than rare, because every node is now withheld until its
bind resolves.
New §6.8: sticky taint is a claim about history, and uncertainty is not
history. Retiring by reason code would not be enough — an unresolved node
propagates `TaintedUpstream`, which is indistinguishable from real
contamination once recorded — so the split is by provenance: the engine runs
its fixpoint twice, and only the evidence-only pass may feed sticky state.
Decisions are unchanged and still fail closed.
Also recorded in §6.8, both from Codex's round-9 review and both pre-existing:
hardware playback-to-capture paths ("Stereo Mix") defeat the `session_device`
classifier in a way the driver denylist cannot detect — a real echo path
needing a design call — and the 2 s readiness budget has no calibration
argument beyond one measurement on one idle desktop.
Impl plan: phase 3r marked built and merged with its gate results, including
the extra Device-side live gate and why row 1 alone could not cover it.
This commit is contained in:
@@ -100,11 +100,21 @@ if it differs, failing closed.
|
||||
1 (r8 carriers) ──────────────────────────────────────► 5 (re-run)
|
||||
```
|
||||
|
||||
⚠️ **Status 2026-07-25: the phase-5 gate FAILED on its first live run** and put 3r into the
|
||||
DAG. Phase 5's machinery is built and correct — it is the audit that found the defect — so
|
||||
"5 (re-run)" is a *re-run of the matrix*, not a rebuild. **Phase 6 does not start** until a
|
||||
passing results file exists. Phases 2 and 4 need no change; the defect is entirely in 3.
|
||||
**Phase 1 is now a hard prerequisite of the re-run for both carriers** (plan §3).
|
||||
⚠️ **Status 2026-07-25 (evening): 3r is BUILT AND MERGED; the re-run has not happened yet.**
|
||||
The phase-5 gate failed on its first live run and put 3r into the DAG; 3r's own four-part
|
||||
gate now passes, including the live prop-recovery row on this host. Phase 5's machinery is
|
||||
built and correct — it is the audit that found the defect, twice — so "5 (re-run)" is a
|
||||
*re-run of the matrix*, not a rebuild. **Phase 6 still does not start** until a passing
|
||||
results file exists. **Phase 1 is a hard prerequisite of the re-run for both carriers**
|
||||
(plan §3).
|
||||
|
||||
⚠️ **A smoke run of the audit against the fixed observer immediately found a second defect
|
||||
(design v3.6 §6.8): a fail-closed `unresolved-ancestry` mark was being promoted to permanent
|
||||
sticky taint.** Fixed in the taint engine (evidence-only sticky pass, 3 new tests,
|
||||
mutation-verified) and merged. Decisions were unaffected — all 57 phase-2 tests passed
|
||||
untouched — so this is a change to what stickiness *remembers*, not to what it *decides*.
|
||||
Note the pattern for the re-run: the matrix rows assert exact partitions, and a stale sticky
|
||||
entry from enumeration would have contaminated every one of them.
|
||||
|
||||
- **0b strictly precedes 6.** v2/v3 drew 0b with no continuing edge. Phase 6 is the first phase
|
||||
that creates objects whose lifetime is tied to pixelpass being alive, so the teardown-ordering
|
||||
@@ -356,11 +366,26 @@ removals are ignored, the fallback is dead code, and readiness releases early:
|
||||
| **PID derivation matrix** (round-2): consistent valid PID · inconsistent PIDs · missing client property · `/proc` entry missing · `comm` mismatch · PID reuse — **every failure makes owner-bridge key 4 unusable** | the pure engine can be correct on a wrong context; this is where the context is built |
|
||||
| **live**: create and destroy a controlled node/link topology; diff Nodes, **Ports**, Links and Clients before/during/after | the adapter tracks a *changing* graph, not a static one |
|
||||
|
||||
### Phase 3 revision (round 8) — bind every Node and Device 🔴 blocks the phase-5 re-run
|
||||
### Phase 3 revision (round 8) — bind every Node and Device ✅ BUILT AND MERGED 2026-07-25
|
||||
v3.5 §6.7. Phase 3 shipped reading node properties off the registry `global` event, where
|
||||
**eight of them are never announced**. This is the fix. Scope is the observer only — phases 2
|
||||
and 4 are unaffected, and the phase-5 audit machinery is already correct.
|
||||
|
||||
> **🟢 Done.** Pure core + adapter, split-seam with mutual review as in phase 3 (mine and
|
||||
> Codex's respectively, each reviewing the other). All four gate rows below pass, the live
|
||||
> row on this host. Codex's review of the core found no certain P1; two findings taken and
|
||||
> mutation-verified (a `device_props` ambiguity test that checked for one live *Device*
|
||||
> rather than one live *global*, and `device.api` corroborating by presence). Two findings
|
||||
> left open as design items, both pre-existing — hardware playback-to-capture paths and the
|
||||
> readiness-budget calibration, both recorded in design §6.8.
|
||||
>
|
||||
> **Added beyond the spec: a second live gate for the Device-side path.** Row 1's
|
||||
> `session_device` assertion is satisfied by a union, and WirePlumber 0.5.15 copies
|
||||
> `device.api`/`alsa.driver_name` onto ALSA nodes on this host — so row 1 passes through the
|
||||
> node fallback and would keep passing if the Device bind delivered nothing at all, leaving
|
||||
> §6.7 decision 4 ungated on the development machine. Verified by mutation: breaking the
|
||||
> Device-side driver read fails the new test while row 1 still passes.
|
||||
|
||||
**Requirements.**
|
||||
|
||||
1. **Bind every `Node` global**, unconditionally, no `media.class` filter. Retain the proxy
|
||||
|
||||
Reference in New Issue
Block a user