Skip to content

A round's AccessContext is rebuilt without Locale — every user-facing round message renders in the default language #948

Closed

Description

@rbuergi

Found while reviewing PR #944 (the localization follow-up for #476). A reviewer flagged that ModelSubstitutionTest compares the round's failure summary against the English catalog entry with locale: null, so the assertion would also pass against a hard-coded English literal. I made the test set a German submitter locale and assert the German text — and it failed, with the round producing English. So the vacuous assertion was hiding a real defect.

Mechanism (exact)

ThreadExecution resolves user-facing round text correctly:

var noModelError = LocalizationCatalog.Get(key, userAccessContext?.Locale, …);

The problem is upstream, in ThreadSubmission. The submission watcher runs on a Throttle/Subscribe continuation where the AsyncLocal AccessContext is wiped — the code says so itself — so the round's identity is reconstructed from the submitter rider on the pending message:

if (submitter?.SubmitterObjectId is { } sid)
    userCtx = new AccessContext { ObjectId = sid, Name = submitter.SubmitterName ?? sid };

Only ObjectId and Name survive. Locale is dropped, and the same is true of the CreatedBy fallback below it. Per that code's own comment the reconstruction path is the USUAL one, not an edge case.

Consequence

Every user-facing string a round resolves from userAccessContext?.Locale renders in the default (English) locale for every viewer, regardless of their language — the exact failure mode AGENTS.md's i18n rule exists to prevent ("a hard-coded UI string is a bug, because it renders English for every viewer"). It is not specific to #476's message; it applies to any round-emitted localized text, present and future.

Suggested fix

Carry the locale on the same rider that already carries the identity: add a SubmitterLocale alongside SubmitterObjectId on the pending message, stamp it in hub.SubmitMessage / StartThread / SubmitComposer where the submitter is captured (the context IS live there), and use it when reconstructing userCtx. The CreatedBy fallback has no locale available and should stay default — worth a comment saying so.

Test to un-vacuum

ModelSubstitutionTest.NoUsableModelAnywhere_FailsRoundWithSpeakingError should set a non-default submitter locale and assert the German catalog text (with a sanity check that EN and DE differ, so the assertion cannot go vacuous again). I have that change working locally and it currently fails against main — I reverted it so it would not block PR #944, which is otherwise correct.

🤖 Generated with Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions