Three changes, each from a measured failure on 2026-10-12:
* pin_to_e_cores(mask) - the old code called ctypes' GetCurrentProcess() without declaring a
restype, so the pseudo-handle -1 was truncated to 32 bits and SetProcessAffinityMask failed with
ERROR_INVALID_HANDLE; since the call's return value was never inspected it logged
"E-core pinning: on" while the process kept every CPU (read back: 0x3FFFFFF = all 26 CPUs).
Now the prototype is declared, the mask is a parameter (--affinity-mask, default unchanged) and
the log prints the mask READ BACK from the kernel.
* --stage-dir: scratch can live on another drive; the ledger, the locks and gate_policy.json stay in
WORK (records, not cache).
* the quality gate now says WHICH criterion failed. Both tests shared one "mean X < bar Y" template,
so a frame-share refusal was filed as a mean one - ledger example: "ssim min 0.9917 mean 0.9972
(1199 frames) ... systematic: mean 0.9972 < bar 0.995, 1.67% of frames below 0.85" (0912 洞见),
a healthy mean refused by the share test. The distinction decides the fix: a mean shortfall
answers to a higher CRF, a frame-share defect does not.
Engine defaults are unchanged: a standalone run behaves exactly as before.