feat(33): shed our own capture cost first; the task is done

Last slice. 590 green, 0 warnings.

- **Shed before you charge the viewer.** On machine evidence the policy's first act is
  `ShedCaptureCadence`: `ScreenCaptureFrameSource` widens its conversion interval 10 ms → 33 ms (≈100
  → 30 conversions/s) through a new `ScreenCaptureManager.ShedLoad`. The output tier, the declared fps
  and the viewer's picture are untouched — we stop paying for readbacks we were throwing away. It
  costs no step budget; if the starvation persists, the fps ladder runs on the next clean window.
- The shed is reset on every stop path, so a cheap session cannot leave a later one capped.
- **The session line is a Logs-tab line, not a session-report line.** The session report is the money
  summary (Super Chats, per currency); quality chatter there would bury the numbers it exists for. The
  cause-tagged line already lands in the log at the moment of the step.

TASK 33 is complete: two ladders, budgets, floors, ambiguity, one-way with recovery behind a flag, the
relaunch, the copy, and the cost shed. 18 tests pin the ladders, the copy and the restart.
This commit is contained in:
2026-11-09 13:05:44 -08:00
parent 134103c08b
commit 0fa3006da7
7 changed files with 73 additions and 10 deletions
+9
View File
@@ -55,6 +55,15 @@ public sealed class ScreenCaptureManager : IDisposable
_uiDispatcher = uiDispatcher;
}
/// <summary>TASK 33: applies the capture-load shed to every live capture. Before this, the only
/// lever for machine starvation was the viewer's tier — the app charged them for its own cost.</summary>
public void ShedLoad(bool shed)
{
lock (_gate)
foreach (var session in _sessions.Values)
if (session.Source is ScreenCaptureFrameSource source) source.ShedLoad(shed);
}
/// <summary>Increments the refcount for a target key, starting capture the first time.</summary>
public async Task<bool> AcquireAsync(string key)
{