# Rust dictionary selection preserves UTF-8 configuration errors

## Reproduced baseline

Unchanged engine source at `c974489aa` passes four controls and fails nine
regressions with UTF-8 boundary panics. A scratch crate depends on that exact
checkout; all 355 tracked Rust inputs match the retained baseline manifest.
Its thirteen-case fixture is byte-identical to the candidate integration test.
The actual exit 101 and full diagnostics are retained.

`compiled_dictionary_token` tested a possible `.xml` suffix by slicing a Rust
string four bytes before its end. That offset can fall inside a Unicode scalar.
Names such as `éabc`, `€abc`, `🦀abc` and a lookalike extension `FIX44.ｘml`
unwound the public selector, session builder and existing-session configurator
instead of returning an unknown-dictionary error.

## Fix and coverage

The cold selector now tests the suffix as bytes. An actual matching ASCII
`.xml` suffix proves the subsequent string split is at a character boundary.
Other names continue through the existing normalization and error path. ASCII
case, punctuation, path separators, the single suffix rule and dictionary-off
configuration retain their existing behavior. Unicode names remain distinct;
lookalike letters do not select an ASCII dictionary. No API, dependency or
message-path work is added.

Thirteen public integration cases cover two-, three- and four-byte scalars,
combining marks, adjacent scalars, lookalike extensions, both configuration keys,
creation/configuration errors and original-name diagnostics. Four controls pin
ASCII aliases, Unicode directory names, real XML suffixes and unused selectors
when UseDataDictionary=N. A bounded 144-name matrix covers eight representative
scalars with three prefixes and six ASCII tails. It is not an exhaustive Unicode
or XML dictionary-loader qualification. These cases use closed transports and
the null store; they create no socket or persistent fixture.

## Verification

| Gate | Result |
|---|---|
| Unchanged baseline | 4 controls pass; 9 regressions fail |
| Candidate integration test | 13 pass, 0 fail, 0 ignored |
| Complete default Rust stage | 1956 passed; 0 failed; 1 existing ignored builders doctest |
| Complete open Rust stage | 1769 passed; 0 failed; the same 1 ignored doctest |
| Strict all-target Clippy, -D warnings | Passed in both editions |
| Rustfmt | Complete changed function and new test match |
| Public-export scan | 2 owned engine files, 0 findings |
| API inventory / generated coverage --check | Passed; scores unchanged |

Each selected conformance stage reports one ran, zero failed, zero skipped;
PARTIAL describes the unselected monorepo stages. The final tested candidate is
`e81a578a7`; all 359 tracked Rust and 533 shared/reference/gate path/hash pairs
match the verification inputs. Publication checks both full input sets again.

The initial candidate `38918a82c` passed the focused cases. Upstream FIXP
reset/finalization and exhausted-admission changes at `ae814cd6b` were integrated
in `5bd13a0db`, whose full suites passed 1,955/1,768 cases. Clippy then caught
an error-accessor style issue in the new test: `.err().expect()` became
`.expect_err()`. The final identical fixture was rerun against the unchanged
baseline and passed thirteen cases in both candidate editions; both strict
Clippy gates passed at `556deb60e`. A publication input check subsequently
caught the upstream reset-Logon acknowledgement fix `8fe7cd141`; it was rebased
into `e81a578a7` and both complete suites and Clippy were rerun on that source.
Earlier interrupted attempts and the failed Clippy log are retained separately.
The production dictionary selector remains byte-identical across integrations.
The gate-driver change added one C++ release self-test, leaving the Rust stage
recipes unchanged; final stage reports select one of 361 stages.

Baseline/initial focused tests and Clippy run on HP; the integrated focused test
and complete suites run on Scan after
six competing C++ build interruptions on HP. Tests run offline at Cargo -j1
with one test thread and debug symbols
disabled. Rust/Cargo 1.92.0 runs tests; isolated 1.93.1 runs Clippy. Each build
checks compiler/runtime activity and at least 8 GiB available RAM. The guard
yields to competing C++ builds and retries; those interrupted attempts are not
reported as successful gates. No heavy job runs on BEELINK2 for this slice.

Pins, commands, source patches, failing baseline, full reports/stdout/stderr,
Clippy logs and the named candidate/publication bundle are retained under
`~/archive/libhft/rust-dictionary-unicode-20261011` on both hosts with verified
SHA256SUMS, with the completed suite packet also archived on Scan. While HP
was queued, its isolated compiler was copied to Scan's shared toolchain cache;
HP completed Clippy before that copy needed to run. No duplicate Clippy gate
was started on Scan. API/session/subsystem scores remain 82 supported / 11 n/a,
27/27 yes and 43 yes / 2 partial / 1 n/a. Live production/venue and FIXP schema
certification evidence remains outstanding.

## Follow-up ownership review

`Acceptor::build_session` still merits a separate ownership audit when a factory
constructs and drops a Session before returning Err: the raw-handle cleanup
assumes the accepted connection remains open. This slice adds no factory
ownership change or reproducer and makes no claim that this boundary is fixed.
