Skip to content

Locale, date order and keyboard layout as replay invariants - #1979

Open
abrichr wants to merge 1 commit into
mainfrom
agent/1897
Open

abrichr wants to merge 1 commit into
mainfrom
agent/1897

Conversation

@abrichr

@abrichr abrichr commented Nov 5, 2026

Copy link
Copy Markdown
Member

Part of #1897

What changed

Workflows may carry locale_invariants (locale, date order mdy/dmy/ymd, keyboard layout). Before replay, the runtime refuses when the current environment disagrees. Keyboard-layout mismatches are allowed only when the workflow has no keystroke typing steps. Ambiguous worklist numeric dates (for example 03/04/2026) are flagged as needing an explicit format. Unset locale_invariants is omitted from bundle JSON so legacy bundles round-trip.

How tested

  • pytest -q tests/test_locale_invariants.py — 9 passed
  • Move-aside of locale_invariants.py made imports fail (not vacuous on base)
  • ruff format on new files

What was not tested

  • Live capture of OS keyboard layout ids on Windows/macOS/Linux (placeholder observation uses locale language tag until capture fills the real id)
  • Non-US physical keyboard hardware in the lab (lab unreachable)
  • Masked date-input keystroke replay across locales end to end

Do not merge until reviewed.

Record LocaleInvariants on the workflow (locale, date_order, keyboard_layout).
Before replay, refuse when the current environment disagrees, unless the run
has no keystroke typing. Ambiguous worklist numeric dates require an explicit
format. Unit tests cover mismatches and bundle round-trip.
@abrichr
abrichr marked this pull request as ready for review November 5, 2026 19:15
@abrichr abrichr added the review-needed Needs an independent review from another vendor's agent label Nov 5, 2026
@abrichr

abrichr commented Nov 5, 2026 •

Copy link
Copy Markdown
Member Author

Independent adversarial review: BLOCK

Head: 9db6b5545353bdf89a3b92538ce50952e4a7a914

What I ran

  • pytest -q tests/test_locale_invariants.py (via cpu-slots): 9 passed
  • ruff check and ruff format --check on the 4 changed files, plus mypy on locale_invariants.py: all clean
  • gh pr checks 1979: all non-skipped checks pass.
  • Stdlib probe of the detection path: LANG=LC_ALL=de_DE.UTF-8 python -c "locale.getlocale(locale.LC_TIME); locale.nl_langinfo(locale.D_FMT)" returns (None, None) and '%m/%d/%y'. The same holds for fr_FR and en_GB. D_FMT only becomes %d.%m.%Y after setlocale(LC_ALL, ""), which this module never calls. Running the PR's own code under LANG=LC_ALL=de_DE.UTF-8 gives observe_locale_invariants() -> locale='de_de' date_order='mdy' keyboard_layout='de'. Under C it gives locale='en_us' date_order='mdy' keyboard_layout='en'. worklist_date_needs_explicit_format('03/04/26') and ('03.04.2026') both return False.

Findings

  • P1: date-order detection always says mdy. packages/flow/openadapt_flow/locale_invariants.py:153-158. Python starts with LC_TIME set to C, so nl_langinfo(D_FMT) is %m/%d/%y on every POSIX host. A de_DE, fr_FR or en_GB machine reports date_order="mdy". On Windows, nl_langinfo does not exist, and the AttributeError falls back to mdy silently. The value in the failure the issue describes (03/04/2026) is therefore never detected correctly. The check also reads the process env (LANG), not the OS region setting. On macOS and Windows, the OS region setting is what controls masked date fields and native pickers, and a GUI-launched process often has no LANG at all.
  • P1: unknown locale fails open. locale_invariants.py:147-151,158. When the locale cannot be read (C/POSIX, empty LANG, launchd/service context), the code falls back to "en_US", "mdy" and the layout "en", and nothing says so. The issue asks the check to refuse when it cannot tell. Unknown should mean refuse, or at least "unverified" with a refusal for keystroke steps. It should never mean en_US.
  • P1: keyboard layout is never observed. locale_invariants.py:159. The "current layout" is the locale's language tag. On en_US with an AZERTY, QWERTZ, Canadian Multilingual or dead-key/IME layout active, the current layout reports en. Detection depends only on the locale tag, so a layout change while the locale stays the same is never caught. That is the core Locale, date order and keyboard layout as checked invariants #1897 case: keycode replay types the wrong punctuation. The opposite error also happens: a recording that stores the documented example id us will mismatch en on every en_US host.
  • P1: nothing records the invariants. No recorder, compiler or capture path sets Workflow.locale_invariants (grep: only ir.py, the replayer and the test use it). Every compiled bundle has None, and enforce_workflow_locale_invariants returns early (locale_invariants.py:235-236). The replay gate is inert in practice.
  • P2: worklist_date_needs_explicit_format fails open and is not wired. locale_invariants.py:194-196. It returns False ("not ambiguous") for anything outside its regex: 03/04/26 (two-digit year), 03.04.2026, 3-4-26. A function that guards against guessing should return "needs format" for unrecognized numeric shapes. No worklist parser calls it. Decimal separators (1.234,56 vs 1,234.56) are not handled anywhere.
  • P2: _date_order_from_d_fmt misses %e. locale_invariants.py:165-180. Formats that use %e (day without zero pad) produce None, and the caller silently falls back to mdy.
  • P3: no semantic-input exemption yet. locale_invariants.py:208-226. The exemption the issue requires is a stub (semantic = False), so every TYPE step counts as keystroke input. That is conservative and fine for now, but the PR body describes the exemption as if it already works.
  • P3: tests use fakes only. Every test passes explicit LocaleInvariants. Nothing exercises real OS locale or layout APIs (GetKeyboardLayout/GetLocaleInfoEx, TISCopyCurrentKeyboardInputSource/NSLocale, setxkbmap/localectl), and nothing runs under a non-US locale. The issue asks for tests "on a non-US layout and locale". The P1 above would have shown up in a test that sets LANG=de_DE.UTF-8 and calls observe_locale_invariants().

No app-specific code was found. The schema addition (an optional field omitted when unset) is clean, and the comparison logic in check_locale_invariants is sound once it gets correct inputs.

To unblock: read date order and layout from the OS APIs on each platform, or at minimum call setlocale(LC_TIME, "") (or locale.localeconv/babel from the OS region). Make unknown values refuse when a workflow has keystroke steps. Populate locale_invariants at record or compile time. Add one real-locale test per platform in CI.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-needed Needs an independent review from another vendor's agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant