Round-3 removed the `"language": "en"` schema default so fresh installs stopped
reporting a fake explicit English, and dropped the `primary === 'en'` skip.
The maintainer's round-4 gate asked to complete the tri-state shape:
1. `load_settings()` now emits an explicit `None` for the unset case
(`settings.setdefault("language", None)`) so `/api/settings` carries a
real three-way signal instead of omitting the key: `null` = no
preference, "en" = explicitly saved English, any other code = chosen
locale. Stored values (including legacy pre-change English picks) still
win via the merge and are untouched.
2. New `TestReviewerTableServerTriState` regression loads the real
`static/i18n.js` in a Node `vm` sandbox and drives
`resolvePreferredLocale(s.language, localStorage.getItem('hermes-lang'),
_detectBrowserLanguageHint())` — the exact boot.js call shape — for all
four rows of the maintainer's reproduction table:
- `en` + new browser (zh-CN) -> en (was zh under round-2 skip)
- `en` + stale ja + en-US -> en (was ja under round-2 skip)
- `en` + empty + en-US -> en
- `fr` + empty + zh-CN -> fr
Verification:
- `python3 -c "import api.config"` OK
- 15/15 in test_issue7622_initial_locale_from_browser.py (14 + new table test)
- 8/8 in test_language_precedence.py
- 60/60 settings + 13/13 password-env + login-locale/autosave/sprint26
collateral tests pass
- ruff_lint --diff origin/master: 0 new violations