v1.108.279 — A machine's language is not English and its bytes are not UTF-8
Two independent defects in one function, both reported and split by @lsg1103275794 under one-issue-one-verdict. Neither fix helps the other: decoding the text does not make 正在运行 contain Running, and reading the state enum does not make the detail readable.
schtasks output was decoded as UTF-8 (#468)
schtasks.exe writes in the machine's code page, so watch-status.detail came back as 40 U+FFFD characters on a Simplified-Chinese install.
⚠⚠ errors="replace" is what made it silent. Strict UTF-8 raises on those bytes; replacement turned a loud failure into a plausible string, and a plausible string is what nobody investigates.
Native output now decodes through the code page Windows reports, asking for the console output page before the ANSI one. ⚠ Those are not always the same — measured while fixing this, the dev box is 437/1252 and the reporter's is 936/936, which is why his evidence could not discriminate between them. ⚠ The order is the answer, not the strict-decode loop under it: cp936 rejects a wrong guess, while cp437 and cp1252 map nearly every byte and cannot fail. That is stated at the function so a successful decode is never read as confirmation.
⚠ locale.getpreferredencoding() is deliberately not used. Under PYTHONUTF8=1 it reports utf-8 while the child still writes CP936 — the exact case the reporter warned about, and it would have looked principled while being wrong for precisely the users this is for.
Liveness was decided by English display text (#469)
"Running" in stdout or "Ready" in stdout reports active: false on every non-English Windows while the watcher runs and reindexes normally. /FO CSV does not help — its headers and values are localized too.
The verdict now reads Get-ScheduledTask's State, an enum whose string form is invariant. Ready still counts as active: the installed task is ONLOGON and sits in Ready between logons, so fixing a locale bug must not quietly redefine what the field claims.
⚠ The fallback to the old predicate survives for a box where the enum cannot be read, and it names itself: state_source is scheduled_task_state or display_text, and state is null rather than a guess.
Three call sites shared the hazard; one was reported
_install_windows raises InstallerError carrying schtasks stderr, and _uninstall_windows reads its output too — so a localized "access denied" reached the user as mojibake, on the path taken when something is already going wrong. All three route through one decoder now, with a ratchet test that fails if a fourth appears.
⚠⚠ The reporter's own note is why both survived a green suite: searching tests/ for _status_windows, schtasks and the Running/Ready predicate returned no hits. There was no coverage to fail.
Also in this release (docs only)
No timebox we offer runs longer than 24 hours, widened the same day from the CLA-only version to every shape: signing, opening a PR already written, and taking an issue to implement. Identical across jcm/jdoc/jdata. The short clock is only fair because the default action preserves credit, and CONTRIBUTING.md now says in as many words that an extension you ask for is not the same as a default we hand out. Timeboxes already posted stand as posted.
Verification
7819 passed, 9 skipped, 0 failed · ruff check src/ clean · all 9 CI jobs green at 262ac34 · 3.13 CI-env reproduce 7813 passed / 15 skipped, the same 7828 total.
tests/test_schtasks_locale.py (20) runs on every platform because it drives the decode and the verdict directly rather than shelling out to Windows; 17 fail against 35eeb2d, and the 3 that pass both sides are controls asserting the defect itself.
Not in this release: #447, the install-pack archive confinement fix by @elfrost — reviewed, green on all eight legs, waiting only on a CLA with a timebox of 2026-08-20. A release is never blocked on an open issue; it rides the next one.