# Comprehensive Survey Report: Testing & Linting Infrastructure for Multi-Tiered i18n Anti-Regression

- **Author**: `explorer_survey_tests_1`
- **Working Directory**: `c:\Projects\FreeExile\.agents\teamwork\explorer_survey_tests_1`
- **Date**: 2026-10-02
- **Target Project**: FreeExile (Native ES Modules WebApp, iOS Metal Engine, Python Server)

---

## 1. Executive Summary & Root-Cause Diagnosis

An in-depth investigation of `c:\Projects\FreeExile` reveals a fundamental architectural disconnect between the global internationalization engine and the chat system / UI modules:

1. **Hardcoded Text & Private Island in `chat_ui.js`**:
   - `client/webapp/js/ui/chat_ui.js` contains **33 lines with hardcoded Vietnamese strings**, including channel names, permission denial messages, cooldown badges, send button labels, and item tooltip inspectors.
   - It maintains an isolated, private dictionary object `CHANNEL_I18N` instead of consuming the centralized catalog (`client/webapp/js/data/i18n_catalog.js`) or registering with the global engine (`FreeExileI18n`).
   - Its local `updateLanguage()` function only modifies channel tabs and the ticker label, completely ignoring placeholders, cooldown badges, permission errors, and tooltip text.
2. **Missing Event Bus Connectivity**:
   - `FreeExileI18n` (in `client/webapp/js/data/i18n.js`) defines `onLocaleChanged` and dispatches a `freeexile:localeChanged` CustomEvent on `window`. However, `chat_ui.js` never subscribes to these events. It only attaches a DOM `change` listener to `#lang-select`. If the language is modified programmatically or via settings modals, `chat_ui.js` is never notified.
3. **Absence of Automated Anti-Regression Guardrails**:
   - While FreeExile possesses robust file-length linters (`check_code_and_doc_hygiene.py`) and matrix linters (`verify_game_design_matrix.py`), there is currently **zero static linting for i18n hygiene** to detect unlocalized strings in UI files.
   - Unit test suites in `tests/unit/` (`test_challenger_chat_m2.py`, `test_challenger_m2_chat_adversarial.py`) test message ring buffers, HMAC verification, and autoscroll, but contain zero assertions regarding reactive language updates or dictionary parity.

---

## 2. Audit of Existing Linting Tools & Test Frameworks

### 2.1 Existing Linting Infrastructure (`tools/lint/`)

| Tool | Purpose | Mechanism | Exit Code |
|------|---------|-----------|-----------|
| `tools/lint/check_code_and_doc_hygiene.py` | Enforces modularization, line length caps (Code $\le 350$ soft / $500$ hard; Docs $\le 400$ soft / $600$ hard) | Python AST + line counting with recursive walk | `0` = Pass, `1` = Strict Fail |
| `tools/lint/verify_game_design_matrix.py` | Cross-checks quests, zones, NPCs between SQLite DB, code catalogs, and docs | SQLite connection + regex inspection | `0` = Pass, `1` = Fail |
| `tools/lint/check_documentation_system.py` | Verifies Diátaxis document structure and links | File path and header validation | `0` = Pass, `1` = Fail |

**Key Finding**: Linters are standardized in Python 3.11 with CLI flags (`--strict`, `--json-out`), UTF-8 console reconfiguration, and atomic error reporting. A new tool `tools/lint/check_i18n_hygiene.py` following this exact pattern will blend seamlessly into the codebase.

### 2.2 Existing Test Infrastructure (`tests/`)

- **Python & Pytest**: Python 3.11.9, pytest 9.1.1. Running `pytest tests/unit/` executes over 894 unit tests.
- **Node.js Sub-Execution**: `test_challenger_chat_m2.py` and `test_challenger_m2_chat_adversarial.py` demonstrate an efficient pattern: launching `node --input-type=module -e <bootstrap>` with a lightweight DOM mock to execute ES modules directly in Node.js within 5.4 seconds.
- **E2E Testing**: `tests/e2e/test_ui_typography_i18n_wiki_streamlining_e2e.py` parses HTML and `i18n_catalog.js` using regular expressions, checking 60 test cases in 0.26 seconds.
- **Browser Automation**: `tools/run_playwright_validation.py` demonstrates that Playwright with `channel='msedge'` launches headlessly on Windows with 100% reliability, without requiring manual browser downloads.

---

## 3. Specification: Static Linter (`tools/lint/check_i18n_hygiene.py`)

A specialized Python linter designed to act as an unyielding pre-commit and CI gate.

### 3.1 Detection Rules
1. **Rule 1 — Hardcoded Vietnamese Text Detection**:
   - **Pattern**: Regex matching Vietnamese accented vowels and consonants:
     `[\u00C0-\u1EF9]` (covers all precomposed diacritics including `ơ`, `ư`, `đ`, tone marks).
   - **Target**: All JavaScript files in `client/webapp/js/ui/` (excluding data catalogs and allowlisted comment lines).
   - **Mechanism**: Strips single-line (`//`) and multi-line (`/* */`) comments. Scans string literals (`'...'`, `"..."`, `` `...` ``). If a string literal contains Vietnamese diacritics in UI logic, it is recorded as a violation with `(file_path, line_number, snippet)`.
   - **HTML Entrypoint (`client/webapp/index.html`)**: Scans user-facing text nodes and inputs. Any element containing Vietnamese characters must possess a valid `data-i18n`, `data-i18n-placeholder`, `data-tooltip-title-key`, or `data-tooltip-desc-key`.
2. **Rule 2 — 9-Language Dictionary Parity**:
   - Extracts all keys from `client/webapp/js/data/i18n_catalog.js`.
   - Enforces $K_{vi} = K_{en} = K_{zh} = K_{ja} = K_{ko} = K_{th} = K_{de} = K_{ru} = K_{es}$.
   - Every single key defined in Vietnamese must have a 1-to-1 match in English and all supported locales.
3. **Rule 3 — Missing Key Resolution (Dangling References)**:
   - Scans all files in `client/webapp/` for references to `data-i18n="<key>"`, `data-i18n-placeholder="<key>"`, and `i18n.t('<key>')`.
   - Compares the set of referenced keys against `I18N_CATALOG`. Any referenced key absent from the catalog triggers an immediate `ERROR`.

### 3.2 CLI Interface & Output Schema
```bash
python tools/lint/check_i18n_hygiene.py [--strict] [--json-out audit/i18n_report.json]
```
Returns exit code `0` on clean pass; `1` on any error violation when `--strict` is enabled.

---

## 4. Specification: Unit Test Suite (`tests/unit/test_i18n_event_bus.py`)

A dedicated unit test suite combining Python `unittest` with Node.js ES module evaluation to test the event bus and UI reactivity under controlled conditions.

### 4.1 Test Matrix (23 Test Cases Across 5 Groups)

#### Group 1: Observer / Event Bus Mechanics (Tests 1–5)
- `test_01_listener_registration_and_callback`: Verifies `FreeExileI18n.onLocaleChanged(cb)` receives the new locale string on `setLocale()`.
- `test_02_multiple_listeners_isolated`: Verifies multiple subscribers are invoked independently and in registration sequence.
- `test_03_listener_exception_resilience`: If one subscriber throws an error, subsequent subscribers still execute.
- `test_04_listener_unsubscription`: Calling the returned unbind function or `offLocaleChanged` prevents subsequent callback invocations.
- `test_05_custom_event_window_dispatch`: Verifies `window.dispatchEvent` emits `freeexile:localeChanged` with `{ locale, dict }`.

#### Group 2: Fallback Chaining & Parameter Interpolation (Tests 6–10)
- `test_06_exact_locale_lookup`: `t('chat_btn_send')` in `en` returns `'Send'`.
- `test_07_fallback_chain_current_to_en`: If key is missing in `ja`, engine falls back to `en`.
- `test_08_fallback_chain_en_to_vi`: If key is missing in `ja` and `en`, engine falls back to `vi`.
- `test_09_fallback_to_raw_key`: If key is missing in all dictionaries, engine returns key or explicit fallback string.
- `test_10_parameter_substitution`: `t('chat_cooldown_wait', {sec: 5})` properly interpolates `{sec}` across all languages.

#### Group 3: Dictionary Parity & Chat Key Coverage (Tests 11–15)
- `test_11_all_9_locales_present`: Asserts presence of `vi`, `en`, `zh`, `ja`, `ko`, `th`, `de`, `ru`, `es`.
- `test_12_vi_en_dictionary_parity`: Asserts `set(vi.keys()) == set(en.keys())`.
- `test_13_all_8_channel_keys_present`: Verifies channel keys `chat_channel_1` through `chat_channel_8` exist in all 9 languages.
- `test_14_chat_permission_keys_present`: Verifies keys for system warnings, guild requirement, party requirement, and level gating.
- `test_15_chat_rarity_and_element_keys_present`: Verifies keys for all 5 item rarities and 5 elemental affinities.

#### Group 4: Chat UI Reactive Integration (Tests 16–20)
- `test_16_chat_ui_subscribes_to_i18n_bus`: Confirms `chatUI.initChatUI()` registers an event bus listener.
- `test_17_language_change_updates_all_channel_tabs`: Calling `FreeExileI18n.setLocale('en')` dynamically updates all 8 tab labels in the DOM without page reload.
- `test_18_language_change_updates_placeholder_and_cooldown`: Dynamic update of `#chat-input.placeholder` and `#chat-cooldown-badge`.
- `test_19_language_change_updates_send_button`: `#chat-send-btn` updates from `'Gửi'` to `'Send'`.
- `test_20_language_change_preserves_chat_history`: Ring buffer messages remain intact during language swaps.

#### Group 5: Edge Cases & Persistence (Tests 21–23)
- `test_21_invalid_locale_handled_gracefully`: Passing `null`, `""`, or unknown locale does not crash the engine.
- `test_22_localstorage_persistence`: Locale change writes to `localStorage['freeexile_locale']`.
- `test_23_null_or_empty_key_resilience`: `t('')` or `t(null)` returns empty string or fallback without exceptions.

---

## 5. Specification: E2E Verification Script (`tests/e2e/test_i18n_reactive_switching_e2e.py`)

A full browser end-to-end integration test executed via Playwright (`channel='msedge'`) against a local web server.

### 5.1 Verification Workflow
```
[ThreadingHTTPServer 127.0.0.1:8899]
        │
        ▼
[Playwright Edge Headless] -> Open index.html
        │
        ├─ Step 1: Verify Initial Vietnamese State (VI)
        │     - Tab 1 = "Thế Giới"
        │     - Button = "Gửi"
        │     - Placeholder = "Nhập tin nhắn [Thế Giới]..."
        │
        ├─ Step 2: Switch to English (EN) via #lang-select
        │     - NO page.reload()
        │     - Await reactive event bus propagation (< 50ms)
        │
        ├─ Step 3: Assert Dynamic DOM Updates (EN)
        │     - Tab 1 = "World"
        │     - Tab 2 = "Zone"
        │     - Button = "Send"
        │     - Placeholder = "Enter message [World]..."
        │
        ├─ Step 4: Test Permission Feedback in EN
        │     - Click Tab 6 (System)
        │     - Placeholder = "[System] System channel is for server announcements only."
        │     - Cooldown badge = "Locked"
        │
        ├─ Step 5: Multi-Language Cycle (ZH -> JA -> KO -> VI)
        │     - Confirm non-ASCII CJK glyph rendering without layout shift
        │
        └─ Step 6: Console Log Gate
              - Assert 0 unhandled console errors or exceptions throughout session
```

---

## 6. Concrete Test Plan & Verification Strategy for Milestones 2 & 4

### 6.1 Milestone 2: Anti-Regression & Linter Suite Gate
- **Deliverables**:
  1. `tools/lint/check_i18n_hygiene.py`
  2. `tests/unit/test_i18n_event_bus.py`
  3. `tests/e2e/test_i18n_reactive_switching_e2e.py`
- **Execution Checklist**:
  1. Run `python tools/lint/check_i18n_hygiene.py --strict` $\rightarrow$ Exit Code `0`.
  2. Run `pytest tests/unit/test_i18n_event_bus.py` $\rightarrow$ 23/23 tests PASS.
  3. Run `pytest tests/unit/test_challenger_chat_m2.py` $\rightarrow$ 10/10 tests PASS (zero backward regressions).
  4. Run `pytest tests/unit/test_challenger_m2_chat_adversarial.py` $\rightarrow$ 10/10 tests PASS.
  5. Run `pytest tests/e2e/test_i18n_reactive_switching_e2e.py` $\rightarrow$ Real DOM language change PASS.
  6. Run `python tools/lint/check_code_and_doc_hygiene.py` $\rightarrow$ All modified files adhere to soft/hard line caps.

### 6.2 Milestone 4: Final Verification & Forensic Audit Gate
- **Scope**: Entire project regression and integrity verification before release.
- **Execution Checklist**:
  1. **Full Unit Test Suite**: `pytest tests/unit/` $\rightarrow$ All tests pass ($> 894$ tests).
  2. **Full E2E Suite**: `pytest tests/e2e/` $\rightarrow$ All E2E tests pass.
  3. **Zero Hardcoded Strings**: Run `tools/lint/check_i18n_hygiene.py --strict` across `client/webapp/`.
  4. **Security & Anti-Cheat Audit**: Run `python tools/security/run_independent_security_audit.py --build-id "RELEASE-I18N-2026" --env STAGING` $\rightarrow$ Exit Code `0`.
  5. **Forensic Check**: Confirm zero production mocks or test shims in `client/webapp/js/ui/chat_ui.js` and `client/webapp/js/data/i18n.js`.
