Skip to content

Improve layout reliability, personal exceptions, and benchmark coverage - #22

Merged
Hantok merged 19 commits into
masterfrom
feat/layout-reliability
Oct 20, 2026
Merged

Hantok merged 19 commits into
masterfrom
feat/layout-reliability

Conversation

@Hantok

@Hantok Hantok commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Changes

  • Add personal exceptions in Settings to protect words from automatic correction while keeping explicit hotkey and layout-switch corrections available.
  • Correct earlier words in an uninterrupted typing run when a later word confirms the intended layout.
  • Keep English paired with one Cyrillic layout, avoid Ukrainian/Russian cross-conversion, and classify Apple layouts by their primary language.
  • Add frozen benchmark corpora, replay validation, baseline gates, and correction-emission checks.
  • Prepare v0.0.12 (build 12), curated release notes, and updated README/landing-page feature highlights. Release automation publishes the checked-in notes.

Validation

  • TestRunner: 474 passed, 0 failed.
  • Optimized InputPipelineTestRunner: 1,016 passed, 0 failed.
  • Optimized LayoutBenchmark self-check: 123 passed, 0 failed.
  • Existing CI passed strict corpus validation and the development baseline gate.
  • Local ad-hoc release app built successfully; strict code-signature verification and Foundation lookup of all three packaged binary dictionaries passed.
  • Info.plist and diff checks passed.

Known issues

Issues #14 (newer Swift bundle packaging), #15 (Globe focus race), and #16 (modifier-only shortcuts) remain open and are listed in the release notes.

Hantok added 18 commits October 12, 2026 12:59
…o one Cyrillic layout

Ukrainian <-> Russian conversion is removed entirely, along with its setting.
Latin text now converts only to the active or last-used Cyrillic layout, so
typing on the Ukrainian layout can no longer produce Russian corrections.
Apple's layouts list every language their script can write, so "Ukrainian"
also declares ru. Reading the whole list made it a Ukrainian + Russian hybrid:
Russian words counted as valid on it, and "фил" (ask) was left uncorrected
because it is a Russian word. Third-party layouts keep every language they
declare.
… the layout

The layout cannot change in the middle of an uninterrupted run of typing, so
the detector now keeps a journal of the run instead of one deferred short word.
When a word is corrected, the correction reaches back over the earlier words
that also read as valid words in the target layout:

- words valid in both layouts are reachable at any length and no longer dropped
  when the next word is short ("філ me more" -> "ask me more")
- a word typed after two or more untracked backspaces is recorded, never judged
  on its own, and fixed with the rest of the run
- a word valid in both layouts stays as typed when the word before it is valid
  only as typed ("лист від google" no longer becomes "dbl google")
- the sweep never crosses Enter or a word that is valid in neither layout

The state machine owns the run, so the detector can no longer count a word
that was reopened with backspace, or skip a second space, when sizing the
deletion.
Add implementation plans for dynamic layout mapping, personal exceptions,
and comparative false-positive benchmark. Set up PROGRESS.md tracking
for plan execution and ignore benchmark results in .gitignore.

Plan-Item: 0
Add LayoutBenchmark executable target supporting layout table export
and corpus validation. Freeze 22 layout tables in Benchmarks/layout-tables/v1
for 11 standard keyboard layouts. Generate the 200-trial seed corpus
in Benchmarks/layout-corpus/v1 (100 development, 100 holdout) with
disjoint groups and SHA-256 manifest integrity verification.

Plan-Item: 03.1
…ine drain seam

Extract DetectionRequestAssembly into Core with DetectionConfiguration,
assembleDetectionRequest, and evaluateDetection. Add drainPipeline multi-queue
drain seam to InputEngine and add test coverage. Implement SwitchFixAdapter
replaying synchronously on InputStateMachine and LayoutDetector with virtual
editor, Scorer with Wilson 95% intervals, and run/summarize subcommands.

Plan-Item: 03.2
Implement SelfChecks in LayoutBenchmark with 36 assertions testing Wilson
score intervals, zero-attempt precision null handling, fake control
adapters (always-preserve, always-replace, wrong-repair, and known-good),
schema/split validation error detection, UTF-16 vs grapheme clusters,
replay delete/reset, suffix mismatch guards, source switch dynamic
rendering, and transient damage checkpoints. Add GitHub Actions CI
workflow at .github/workflows/layout-benchmark.yml on macos-15.

Plan-Item: 03.3
Add record-baseline subcommand to LayoutBenchmark and record the frozen
SwitchFix baseline v1 across all 200 trials (development, holdout, combined)
in Benchmarks/baselines/v1/baseline.{json,md}.

Combined baseline achieves 90.0% recall (36/40), 90.0% precision (36/40),
0.00% text-damage FP (0/160, 95% Wilson CI [0.0%, 2.3%]), 0.00% layout-only
FP (0/160), and 100% preservation (160/160 TN). The 4 false negatives in
EN -> UK are confirmed to be caused by static Ukrainian variant mapping
ambiguities, which will be addressed in Plan 01.

Plan-Item: 03.4
Implement PersonalExceptionsPolicy with NFC normalization, locale-independent
lowercasing, apostrophe normalization (ASCII ', U+2019, U+02BC), 1-64 character
and <=256 byte limits, Unicode letter requirement, and matching key derivation
based on detector token-splitting rules. Implement PersonalExceptionsStore
with UserDefaults persistence under SwitchFix_personalExceptions_v1, schema
versioning, 1,000-entry capacity limit, and envelope validation. Add 79 unit
tests in TestRunner.

Plan-Item: 02.1
…uration invalidation

Introduce CorrectionTrigger (.automaticBoundary, .hotkey, .layoutSwitch)
carried explicitly in DetectionRequest and CorrectionPlan. Add
invalidateCorrectionConfiguration() to CaptureStateStore to bump epoch
on configuration updates. Wire personal exceptions into LayoutDetector
for automatic current word suppression, confidence reset, and journal
backward sweep barriers on typed and display text. Add engine safeguards
for exactDetection test seams and add regression tests in TestRunner and
InputPipelineTestRunner.

Plan-Item: 02.2
…d SettingsView

Expose PersonalExceptionsStore methods and policy snapshots on
PreferencesManager, posting .preferencesDidChange on changes. Wire
AppDelegate to push personal exceptions to InputEngine on startup and
on preference updates. Add Personal Exceptions section to SettingsView
with word list, count/capacity indicator, Add field with validation
messages, and Remove Selected button. Add pipeline test for in-flight
correction cancellation upon exception modification.

Plan-Item: 02.3
Stop logging the protected typed word (SwitchFixLog is public). Derive
exception keys from the detector's own tokenizers instead of a copy.
Republish exceptions only when the set changes, so unrelated preference
edits no longer invalidate corrections or reset detector context. Keep
the next word after a configuration change: only a word in progress is
skipped to its boundary. Carry the real trigger on selected-text
corrections, reject a lone combining mark as a letter, and report an
unreadable or partly invalid stored list in Settings.

Add the detector, store and pipeline cases the first slices left
untested; the display-barrier and confidence-reset mutations that
previously passed now fail the suite.

Plan-Item: R1
Configure each trial's sources, Ukrainian variant and active-source
layouts with the app's own discovery and AppDelegate rules instead of
hardcoded standard variants, classify keys like KeyboardMonitor, and
plan edits with the engine's shared planCorrectionEdit. Stop hiding
failures: suffix mismatches, missing tables, errors and missing results
are now reported, scored as failures or coverage loss, and checkpoints
and allowed actions are scored.

Self-checks now exercise the real scorer, validator, editor, renderer
and adapter and cross-check traces against InputEngine. The validator
verifies manifests, carets and exact tables and reports label
conflicts; the generator is versioned and byte-deterministic; baselines
record measured metadata; run --baseline gates per trial; CI no longer
ignores pipeline failures.

Plan-Item: R2
v1 typed four inputs identically under contradictory labels, leaked
"apple" across splits as a reversed mapping, and had no negative whose
intended token is a word in the other layout. v2 replaces the five
affected holdout trials under new IDs and adds ten cross-layout
collision preserves per split, five typed on ABC and five on
Ukrainian. Generated byte-deterministically by 5daf653; passes
validate --strict with no warnings. v1 stays frozen.

Plan-Item: R3
Baseline v1 measured a harness misconfiguration (standard Ukrainian
variant for a legacy source, non-app key classification). Record v2
from a clean worktree at c22be93 on corpus v2 (reference) and v1
(comparison), with measured environment and runner results, and mark
v1 superseded. CI validates v2 strictly and gates the development run
per trial against baseline v2; the execution instructions point later
items at corpus v2 and baseline v2.

Plan-Item: R4
Invalidate stale detection work while publishing the new epoch before manual layout-switch corrections. Preserve benchmark quality denominators, separate layout-only checkpoint errors, reject unreachable exceptions and unsupported carets, batch exception deletion, and reject cross-layer baseline comparisons.

Tested: release build; TestRunner 459/459; InputPipelineTestRunner 1016/1016; benchmark self-check 112/112; v1/v2 validation and development baseline gates; cross-layer gate rejection

Scope-risk: narrow
@Hantok

Hantok commented Oct 20, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T15:08:00.577582Z 68873cc Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 68873cc276

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…rovements

Publish curated release notes and highlight personal exceptions, phrase correction, and safer layout pairing.

Tested: 474 core checks, 1016 pipeline checks, 123 benchmark self-checks, ad-hoc app build
@Hantok
Hantok merged commit ba95b6b into master Oct 20, 2026
2 checks passed
@Hantok
Hantok deleted the feat/layout-reliability branch October 20, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant