Skip to content

Ergopti v1.0.5

Choose a tag to compare

@github-actions github-actions released this 22 Oct 07:02
Immutable release. Only release title and notes can be modified.

Ergopti 1.0.5

Ergopti — keyboard layout: install the ergonomic layout without running an application.

Ergopti Plus — advanced typing tools: the companion application adds remapping, shortcuts and text expansion.

Downloads

Ergopti — keyboard layout only

No Ergopti Plus application is required for these keyboard layouts.

Platform Download Installation
Windows Ergopti_windows.exe Run as Administrator, log out and back in, then select the layout in Settings → Time & Language → Language & region.
macOS Ergopti_macOS.zip Unzip, copy the .bundle folder to /Library/Keyboard Layouts/, log out and back in, then add it in System Settings → Keyboard → Input Sources.
Linux Ergopti_xkb.zip Follow the XKB installation guide. Includes standard, ANSI, plus and plus-ANSI variants.

Ergopti Plus — application with advanced typing tools

Select your platform and download the corresponding file.

Windows — Ergopti Plus

Asset Description
ErgoptiPlus.exe Compiled AutoHotkey executable (AHK v2 runtime bundled)

Installation — app:

  1. Download ErgoptiPlus.exe.
  2. Double-click to run. On first launch, resources are extracted to %LOCALAPPDATA%\Ergopti.
  3. Your personal configuration is stored in %USERPROFILE%\.config\ergopti_plus. If this folder does not exist yet (first launch), the setup wizard will open automatically.

macOS — Ergopti Plus

Asset Description
ErgoptiPlus.app.zip macOS application (Hammerspoon bundled; Karabiner-Elements installed on first run)

Installation with Homebrew:

brew tap adrienm7/ergopti
brew install --cask ergoptiplus

ergoptiplus installs the stable releases and ergoptiplus@dev the dev prereleases; this release is in ergoptiplus. brew upgrade then updates it with your other casks, and the app still updates itself from its About menu.

Installation — app, by hand:

  1. Unzip ErgoptiPlus.app.zip and move ErgoptiPlus.app to /Applications.
  2. Remove the quarantine flag (the app is not yet Apple-notarised):
xattr -dr com.apple.quarantine /Applications/ErgoptiPlus.app
  1. Launch the app. On first run, Karabiner-Elements will request a System Extension approval — required for key remapping.
  2. Your personal configuration is stored in ~/.config/ergopti_plus/. If this folder does not exist yet (first launch), the setup wizard will open automatically.

Linux — Ergopti Plus

Asset Description
ErgoptiPlus-linux-amd64.deb Driver package for Debian, Ubuntu and Mint
ErgoptiPlus-linux-x86_64.rpm Driver package for Fedora, RHEL and openSUSE
ErgoptiPlus-x86_64.AppImage Portable driver — any distribution, no root required
ErgoptiPlus-linux-x86_64.flatpak Sandboxed driver, for a distribution-agnostic install
ergopti-plus-linux.tar.gz Driver bundle with install.sh, installs under your home directory

Which file for your distribution:

  • Debian, Ubuntu, Mint, Pop!_OS, elementary → ErgoptiPlus-linux-amd64.deb
  • Fedora, RHEL, CentOS, openSUSE, Mageia → ErgoptiPlus-linux-x86_64.rpm
  • Arch, Manjaro, EndeavourOS → build it from tools/build/PKGBUILD in the repository; no Arch package is attached to this release
  • Flatpak → ErgoptiPlus-linux-x86_64.flatpak, installed with flatpak install --bundle. It requests --device=all, which is the only permission that reaches /dev/input and /dev/uinput; the sandbox still cannot grant access your user does not have on the host, so the udev rule in step 2 is required either way
  • Any other distribution, an immutable one, or no root access → ErgoptiPlus-x86_64.AppImage or ergopti-plus-linux.tar.gz

Alpha. CI exercises virtual input devices, display sessions and package launches. Physical keyboard behavior and desktop-shell integration still need validation on the target distribution.

Installation — app:

  1. Open the downloaded package with your distribution's software installer and install it.
  2. Open Ergopti Plus from the applications menu. Input permissions are prepared by the package; if additional authorization is needed, Ergopti opens a graphical administrator prompt.
  3. Follow the first-use window. If group membership changed, sign out and back in when prompted.
  4. Choose Global actions → Start at login to enable or disable automatic startup.

Removal: choose Global actions → Uninstall Ergopti, or remove the package through your software manager. The standalone archive also includes an ownership-checked uninstall script. Personal settings are preserved.

Changelog (click to expand)

Timestamps are in Paris time (Europe/Paris, CET/CEST).

Commits since v0.0.0-dev.159.

Features

  • Linux: Run copy_selected_path on the primary selection
    The action was declared for Windows and macOS only. Linux now opens what
    the X11 primary selection names through the shared open_target rule,
    handing the target to xdg-open as one argument through the desktop
    opener, and reports what it cannot open. The file-manager branch, which
    copies the selected files' paths on the other drivers, stays
    unimplemented: a file manager publishes its selection as text/uri-list,
    which the clipboard adapter cannot read by MIME type.

  • Macos: Open the shared action picker for key-combination slots
    A key pair's three slots under Shortcuts (chord, hold 1 + tap 2, hold 1 +
    hold 2) still unfolded a submenu of every remap action, the last action
    submenu of the macOS menu, while Windows opens the shared picker for a
    pair's tap and Linux for its tap and chord. Each slot now opens the
    shared picker through the same open_slot_picker as a key's tap and hold,
    titled by the slot and the pair, and saves through its set_combo_*_action
    owner with the same validation and regeneration.

    The submenu builder, its manifest frame and MenuUtils.build_action_picker
    had no caller left and are removed. The three title prefixes reuse each
    slot caption's translation in all 21 locales.

  • Macos: Open the shared action picker for a key's tap and hold
    The macOS Tap-Holds menu listed a key's tap and hold actions in nested
    submenus, while Windows and Linux already open the shared action picker
    for the tap. The maintainer wants no action submenu there: both rows now
    open the picker gestures and keyboard shortcuts use, with the remap
    catalogue the slot accepts, one heading per category, search and the
    current action marked. A pick goes through the same set_tap_action /
    set_hold_action owner, exact regeneration and refresh; an action the slot
    does not offer or a refused setter keeps the picker open.

    The shared tap row is now one command for the three drivers, the macOS
    hold row a command of its own (Windows and Linux keep their seven hold
    options). The retired macOS captions' translations move into
    tap_hold.picker.tap / hold, English until now in eleven locales.

Fix

  • Macos: Open what copy_or_open_path selects instead of searching
    is_probable_url accepted only http(s)://, www. and a dot followed by
    two letters, so a selected IP address or localhost:3000 became a
    Google search and /Users/ada/a.txt was opened as http:///Users/ada/a.txt.

    _shared/lua/open_target is the rule's macOS and Linux implementation;
    copy_or_open_path opens a URL through its handler and an existing path
    through open(1) as one argument, and notifies a missing path, a bare
    file name, a registry key or a Windows path instead of searching it.
    The search fallback is the shared default rather than a local copy.

  • Windows: Open the IP, path or URI copy_selected_path selects
    SearchPath only recognised drive paths, registry keys, scheme URLs and
    domain names whose last label is letters, so a selected 192.168.1.1,
    10.0.0.1:8080/admin or localhost:3000 went to the search engine, and a
    path that did not exist was silently searched instead of reported.

    infra/open_target.ahk now classifies the selection by the rule the
    macOS and Linux drivers share (corpus
    _shared/tests/corpus/shortcuts/open_target_vectors.json): IPv4, IPv6
    and localhost with port and path, URLs with or without a scheme, mail
    addresses, file: URIs, drive, UNC, %VARIABLE% and ~ paths, registry
    keys in their Regedit and PowerShell spellings, ms-settings: and
    shell:. A path reaches the shell quoted as one file and a URL only
    without whitespace, so no selected text becomes program arguments. A
    missing path, a bare file name or a refused launch is logged and
    notified (open_target.* in every locale), never searched.

  • Windows: Wait for clipboard readers before opening native locks
    The region custody test failed at random, and the product shares the
    cause. Windows clipboard history and clipboard managers open the
    clipboard right after every change, so a single raw OpenClipboard at
    that moment fails: the region capture threw "The region clipboard is
    busy.", CB_WriteBitmapFile refused its publication, and the receipt
    reopen after CloseClipboard returned no sequence. AutoHotkey's own
    clipboard calls wait up to their one-second ClipboardTimeout; these raw
    native opens did not.

    CB_OpenClipboardWithin retries for that same bound and is now used by
    the region capture, the bitmap publication and its receipt reopen. The
    owner and sequence checks that follow each open are unchanged.

    The region custody suite gains a held case: a second process holds the
    clipboard until the capture reports its refused open, so the contention
    is certain rather than incidental. With the single-shot open restored
    that case fails with "The region clipboard is busy."; removing the
    sequence check still fails the before and unknown cases.

    macOS and Linux have no exclusive clipboard open lock (NSPasteboard and
    the Linux selection owners are read without one), so this applies to
    Windows only.

  • Macos: Restart the word after Backspace erases what was typed
    Same rules as Windows and Linux. The keymap's Backspace never restored a
    word start, a Backspace on an empty terminal prompt made the next word
    start unknown, the idle wipe ran on the Backspace itself and forgot the
    word it was deleting, and Option+Backspace (delete the previous word)
    reset to an unknown position.

    • the Backspace that empties the buffer sets start_is_word_boundary;
    • start_is_line_start marks an empty line: a bare Return in a terminal
      (text_sender's terminal host list), Cmd+A outside a terminal, Cmd+L
      in a browser (app_filter's browser list); every reset that moves the
      caret clears it;
    • the idle wipe skips a bare Backspace and an already empty buffer at a
      word start;
    • Option+Backspace starts a word; Cmd and Ctrl with Backspace keep the
      unknown start.

    text_sender exposes the focused application's identity it already read
    for terminal pacing, and app_filter answers is_browser_bundle from its
    existing list. The keymap tests drive the real event tap.

  • Linux: Restart the word after Backspace erases what was typed
    The shared engine's Backspace never restored a word start: "ab",
    Backspace, Backspace after an unknown start left "c★" silent, and a
    Backspace on an empty terminal prompt (after Enter) made the next word
    start unknown although nothing was deleted, the case the Windows typing
    ledger of 2026-10-21 shows. Ctrl+Backspace also reset to an unknown
    position although the caret then sits where the deleted word began.

    • engine:backspace() sets the word start when it erases the last
      codepoint, and keeps it on an empty buffer known to start an empty
      line (engine:reset(true, true));
    • word_reset applies a newline typed in a terminal as such an empty
      line, Ctrl+A outside a terminal and Ctrl+L in a browser as an empty
      field, and Ctrl+Backspace (no Alt, AltGr or Super) as a word start
      whatever the options;
    • the idle pause goes through engine:reset_after_pause(), which leaves
      an empty buffer at a word start untouched, so the line survives it.

    Control keys already never applied the idle reset, so a Backspace after
    a pause still edits the word before it. Tests drive the real hook and
    loader; the source pins follow the new apply_control argument.

  • Windows: Restart the word after Backspace erases what was typed
    A word-start hotstring stayed silent after the typed text was deleted.
    The typing ledger of 2026-10-21 shows the case in Windows Terminal
    (Claude Code): Enter, a pause, "c★" expanded, six Backspaces over the
    five characters of "c’est", then "C★" was not offered. The sixth
    Backspace found the engine buffer empty and declared the left context
    unknown, although the prompt was empty and nothing was deleted.

    The word-start owner (HSE_FeedBackspace) now applies fixed rules:

    • the user's Backspace that erases the last character typed since the
      buffer started puts the caret back at a word start ("ab", Backspace,
      Backspace, "c★" expands); a correction that leaves characters keeps
      the word, and a Backspace past the buffer start stays unknown;
    • HSE_StartIsLineStart records an empty input line: a bare Enter in a
      terminal, Ctrl+A outside a terminal, Ctrl+L in a browser's address
      bar; a Backspace on an empty buffer there deletes nothing, and the
      Enter's own newline is not left-of-caret text;
    • an idle reset no longer throws away the word a following Backspace
      edits: _PrefixRestoreIdleForgottenWord gives it back while both
      context generations are unchanged;
    • Ctrl+Backspace (Shift allowed, never AltGr's fake LCtrl, Alt or Win)
      deletes the previous word and starts a new one; Alt+Backspace keeps
      the unknown start.

    The new suite replays the ledger sequence, the maintainer's "ab",
    Backspace, Backspace, "c" in a terminal and in a field, the capitalized
    triggers C★, J★ and Ct★, the three AltGr layout families, a remap's
    synthetic Backspace and the address bar; it fails on the previous
    engine. The source pins that named the old call spellings follow the
    new AtLineStart and ErasesTypedText arguments.

  • Linux: Draw the degraded tray menu with the one fallback builder
    98d41e6 built the no-keyboard menu rows by hand in input_access, which
    raised the rows-outside-renderer ratchet from 3 to 7. setFallbackMenu takes
    ordered action groups and stays the only out-of-renderer builder; the
    adapter suite pins the grouped rows and separators.

  • Linux: Name the status window type so the focus gate can prove it
    98d41e6 built the status window with gtk_window_new(WINDOW_TOPLEVEL);
    the topmost gate accepts only 0 or GTK_WINDOW_TOPLEVEL and also read the
    FFI prototype as a popup. The constants take their GTK enum names and the
    detector skips a C prototype, with clean fixtures for both spellings.

  • Windows: Describe the exe in plain English and carry the website
    The version resource said 'keyboard layout companion', which says nothing
    about the product. It now describes hotstrings and shortcuts for faster,
    more ergonomic typing (no comma: Ahk2Exe splits directive parameters on
    commas) and the copyright line carries https://ergopti.fr, because the local
    Ahk2Exe has no supported Comments directive and Windows has no website
    field. The local build reads both fields back in the version-info test.

  • Windows: Keep the JSON decode tally in a function static
    8a43bd8 added two module-level counters to infra/json.ahk for the
    release-list decode budget. They are assigned only when the auto-execute
    section reaches that file, so a decode reached earlier would read an unset
    global, and they raised the require_state drift ratchet from 27 to 28 files
    with unguarded module-level state.

    The counters now live in one object created by a function static on first
    use; each decoder captures it once in its own static, so the hot path still
    pays a single property increment and JsonDecodeTally reports the same
    counts. The ratchet is back at its baseline of 27.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Windows: Capture screenshots in process instead of a hidden PowerShell stage
    A corporate proxy flagged the v1.0.4 ErgoptiPlus.exe as a virus. The new
    region-save stage started a hidden powershell.exe that compiled C# P/Invoke
    to read clipboard images: the shape of a stealer stage, stored as plain
    text in the compiled script. The window and fullscreen captures also ran
    System.Drawing CopyFromScreen in a hidden PowerShell.

    Every screenshot now captures in the driver: BitBlt from the screen DC
    into a top-down DIB section, GDI+ for the bicubic downscale and the
    PNG/BMP encoders. The region save keeps its custody rules: it opens the
    clipboard, checks the admitted sequence, copies the borrowed CF_BITMAP
    with CopyImage, detaches it into GDI+ and releases the lock before the
    save. The in-process task keeps the worker contract (deferred start,
    exactly one completion, exact quiescence receipt only when not mid-run),
    so the cancellation, timeout, stage cleanup and publication fences are
    unchanged. File names, folders, timeouts and lifecycle logs are the same;
    a failed capture now logs its native reason.

    The region custody test drives the real capture with an external
    clipboard-lock probe, a later copy after detachment, a lost sequence and
    a delayed renderer; a source guard fails if the screenshot module
    launches a script host again.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Codegen: Emit the shared native_path basename in personal_files.lua
    b471158 moved the personal hotstring backup check onto the shared
    native_path owner by editing the generated personal_files.lua, while its
    generator still emitted the inline package.config pattern. Every
    npm run gen therefore reverted the file and the no-drift gate failed.
    The generator now emits the NativePath.basename form the committed file
    already carries.

    The architecture diagram is regenerated for the running-application
    adapter added by 0292ec4.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Linux: Show a status window and a real menu without keyboard access
    The AppImage catalogue passed v1.0.4 with a close-up of the tray icon as its
    screenshot. Its worker left-clicks the docked icon, which libayatana's
    GtkStatusIcon fallback answers by popping the menu, then right-clicks the same
    spot after 3 s: that press lands outside the grabbed menu and closes it, so
    nothing but the icon was left to capture. A popup menu is override-redirect
    and never becomes the active window the catalogue screenshots.

    The degraded no-keyboard state now opens a centred "Ergopti+" status window
    that explains the failure, which the catalogue finds on its first pass, and
    its tray menu gains a details row, Open logs folder, Report a bug and Quit,
    all from existing shared labels. The hold loop drains libuv so the desktop
    helpers those rows start can finish.

    The ubuntu:22.04 AppImage smoke now asserts the status window is mapped and
    that a left and a right click on the icon docked in stalonetray each map the
    menu popup, with xwininfo instead of a screenshot.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Windows: Refuse remote completions whose bytes are not UTF-8
    The managed curl facade reads a response body with FileRead, which
    repairs malformed UTF-8 into U+FFFD. A remote provider answer carrying
    invalid bytes therefore reached the JSON classifier as clean text and
    became a successful completion with substituted characters. The
    managed-response-utf8 controls exposed it on the real facade and remote
    consumer.

    The facade now judges the raw body bytes with HTTPIsValidUtf8 before the
    text read and records ResponseEncodingValid; ResponseText keeps its
    legacy decoding for generic callers, empty bodies, UTF-16 BOM text and
    caller-owned binary output. The remote consumer refuses a managed
    completion whose bytes were invalid with reason invalid_encoding, while a
    genuine U+FFFD (EF BF BD) stays valid. The timing-control double of the
    facade carries the new property.

    macOS and Linux keep response bodies as byte strings: nothing repairs
    them, hs.json refuses invalid UTF-8 and the shared parser asserts valid
    UTF-8 before a prediction, so only the Windows FileRead path needed this.

  • Ci: Re-read a run census whose job list moved between pages
    The site verdict of a dispatched all-lanes run refused a healthy run with
    "API census changed or is malformed" (run 39607861912). Since run
    listings are paged by 50, the jobs census of a run spans several pages,
    and the run is still creating jobs for the other lanes' reusable
    workflows while the verdict reads it: total_count grows between page one
    and page two. That pass proves nothing, since rows shift across the page
    boundary, but the run itself is fine.

    collect now restarts the census from page one when the total moves
    between pages, at most three times, and still refuses a listing that
    never holds still. Malformed pages, duplicates, overflow and truncation
    stay immediate refusals. The admission controls cover a listing that
    grows once (read again from page one, admitted) and one that never
    settles (refused).

  • Windows: Retain personal editor source consent
    Bind full forms to the bytes that produced their displayed model so an
    unchanged field or alias cannot overwrite a newer identity source.
    Retain native and web consent across deferred and recursive saves,
    without changing historical headless writes or the staging witness.

    Decode the captured bytes with native FileRead semantics and cover
    session replacement, malformed consent and decoder compatibility.
    Scoped source gates pass; native AFTER validation remains pending.

  • Windows: Retain paused Ollama script delivery
    A pause consumed deferred callbacks after readiness had detached their
    bootstrap queue, leaving the page permanently behind its operation. Keep
    unsent commands with the exact session and resume one FIFO admission per
    timer callback. Never retry an item after the native submission boundary.

    Retire old delivery owners without touching a running predecessor or its
    successor. Release consumed payloads and reset fully drained arrays while
    preserving reentrant appends.

    Keep the causal seven scenarios unchanged and add three observer reentry
    controls for append, pause and replacement.

  • Windows: Scope cold metrics retries to committed input
    Keep warm failure budgets tied to their admitted input revision. New
    committed input receives its own cold-window retry after an older budget
    exhausts, without replacing active jobs or losing pending pause ownership.

  • Windows: Resume authorized Ollama readiness flushes

  • Windows: Retain warm retries through reload and ingest
    A refused reload or committed input could discard the only recovery
    owner of a cold metrics window. Keep temporary reload exclusion on
    the existing retry timer and reconcile waiting input revisions in place.

    Preserve already admitted jobs and their requested revision. Publish
    the exact returned terminal generation before releasing launch ownership.
    Native before evidence covers both lost-debt paths; after validation
    remains pending with the unchanged regression bodies.

  • Windows: Fence retained metrics hosts after source reads
    A Source getter can retire the session while keeping its controller in
    the warm cache. Revalidate the exact entry, view, controller and document
    before revealing it or admitting first paint.

    Keep legacy entries without a navigation URL and the delayed backstop
    behavior for nonmatching sources. The native BEFORE reaches three stale
    visibility paths plus an ownership probe; AFTER receiving is pending.

  • Windows: Retain metrics warm-up store ownership
    Revalidate warm admission after diagnostics and bind the projected store through
    terminal delivery and chained warm timers. A window waiting on another store
    keeps an owned retry until that worker retires, including early timer callbacks
    and suspension, without canceling a newer window build.

    Add five recovery regressions through real scheduling, publication and window
    delivery. Preserve the six native-before admission cases and profile16; adapt
    the existing direct terminal caller to provide its store explicitly.

    Validation: scoped encoding/prechecks, format, strict conventions, loop capture,
    and lexical checks pass. Review and Config source reviews found no blocker.
    Native after validation remains unexecuted locally.

  • Config: Retain personal info source before replacement
    A completed stage could overwrite an identity changed by another writer
    or recreate a source deleted during the save. Retain classified raw
    bytes and verify them after authorization at the replacement boundary.

    Recheck the exact lease, pause and path context after the native read.
    Keep native BOM admission, explicit-path calls and the canonical stage
    cleanup and strict replacement receipt intact.

  • Windows: Retain Ollama flush queue ownership across locale reads
    Locale I/O and diagnostics can close the admitted page and publish a new
    session. Revalidate the retained epoch, view and queue before publishing
    readiness, then defer only the detached scripts. Updates arriving during
    the locale read remain on the same retained queue.

    The native BEFORE replay confirms successor queue loss with four valid
    controls in both orders. Native AFTER receiving remains unexecuted.

  • Windows: Retain cache-reset message ownership after logging

  • Windows: Authenticate native webview sender identity
    Vendor callbacks wrap the same native Core in distinct AHK objects, so
    object equality rejects valid dashboard and Ollama bridge messages.
    Compare retained canonical COM identity for genuine nonempty Core
    wrappers, then revalidate the original session before dispatch.

    Keep passive test views exact and reject pointer or URL lookalikes.
    Retain native sender probes and exercise empty and mixed Core refusal.
    Native validation of this candidate remains pending.

  • Windows: Retain metrics message page ownership
    COM getters and diagnostic callbacks can replace a dashboard before
    an admitted message resumes. Revalidate its captured page, view and
    navigation after each callout before it can affect a successor worker.

    Keep the existing sender, origin and paused range policies unchanged.

  • Windows: Preserve discoveries admitted during category reload
    Reload replaced the live map after a read callback could acknowledge a
    new app and schedule its persistence. Adopt only discoveries admitted
    after the read began, leaving earlier pending values and captured disk
    collisions under the existing authoritative reload contract.

  • Windows: Detach Ollama close ownership before cleanup
    Subscription finalizers and native controller close can reenter after an
    Ollama window begins retirement. Reading singleton globals afterward can
    destroy a replacement GUI, clear its view or retire its profile.

    Retain the old resources and revoke their public slots together before
    cleanup. Unsubscribe while the captured controller is alive, then dispose
    only that controller, GUI and profile without further singleton writes.
    Keep the existing callback and empty/repeated-close policy.

    Seven isolated real-module controls retain actual private HWND receipts.
    Controlled reentry does not claim real COM reentry was observed; native
    before and after execution remain deferred to Windows.

  • Windows: Retain Ollama message session across callouts
    Payload getters and logger I/O can resume after the download window closes
    and a replacement publishes its globals. Recheck the admitted epoch and
    retained view after each boundary before dispatching ready, retry or cancel.

    Retain the cancel callback before native teardown so cleanup cannot select
    a replacement callback. Keep subscription identity and close cleanup policy
    unchanged; the separate internal close race remains outside this fix.

    Twelve isolated child regressions exercise the actual module. Native before
    and after execution remain unqualified until Windows receives the cohort.

  • Windows: Allocate unique app category stages per attempt
    A refused cleanup can retain a stage after its writer finishes. Reusing
    an object address as a stage receipt can collide with those bytes on a
    later attempt and keep CREATE_NEW from making progress.

    Use a monotone attempt receipt in a module-specific namespace, retaining
    strict create ownership and leaving prior debris untouched.

  • Windows: Handle scoped app category dashboard edits
    The apps page emits pick and edit commands which the Windows host ignored.
    Defer those requests outside WebView callbacks, retain exact page and native
    dialog ownership, and commit only confirmed category and score changes to
    the canonical metrics store. Retire owned prompts before closing or hiding
    the page and rearm full projection recovery after durable edits.

    Add nine actual GUI controls for prompt and picker retirement, refusal and
    successor preservation. The separate host dispatcher and retry regressions
    exercise real private storage and SQLite-backed projection publication.

    Native AutoHotkey, compiler, E2E and GUI execution remain deferred to Windows.

  • Windows: Persist scoped app category edit records
    Retain legacy classification semantics while storing explicit UI score
    overrides in the canonical category file. Capture the initialized root and
    source before dialogs, and require live page admission at native publish.

    Preserve unknown JSON metadata and fresh external deletions, keep real
    reentrant discoveries pending, and project only explicit records through
    the actual metrics root in both full and partial workers.

  • Windows: Publish app categories after durable edits
    Keep explicit category edits detached until their atomic file write
    completes and return a durable acknowledgement. Share write ownership
    with deferred saves and reload so a reentrant caller cannot overwrite
    the candidate from stale RAM; refuse shutdown until that owner retires.

  • Windows: Decode global delimiter overrides as utf-8
    Match the explicit UTF-8 override writer and sibling category reader
    instead of inheriting the caller's ambient code page. Preserve native BOM
    handling and the existing line, comment and basic-string grammar.

    Exercise real raw, BOM and serializer files under local CP1252 with exact
    value, byte-preservation and encoding-restoration assertions. Native
    execution and attribution of the current boot replay failures are pending.

  • Windows: Revoke dispatcher fragments before shortcuts
    The early HookDispatcher fallback received Ctrl+V but kept its partial
    bridge character because the key callback recognized only navigation.
    Pasting in the same control could then join a later low surrogate to a
    high from the previous edit context.

    Reuse the existing AltGr-aware modifier reader to retire incomplete
    contexts before non-modifier shortcuts. Preserve complete-word policy,
    ordinary packets, modifier keys and retired-hook admission. Seven real
    callback and bridge-worker regressions use the existing state ports.

  • Windows: Retire bridge fragments at idle boundaries
    With Hotstrings disabled, only the bridge retains a raw high surrogate.
    The empty engine and prefix shortcut skipped idle invalidation, allowing
    an unrelated later low unit to complete that retired fragment.

    Include bridge-owned incomplete text in the existing idle reset guard.
    Cover prediction and agent contexts through actual prefix callbacks,
    with controls for disabled idle reset and complete bridge words.

  • Windows: Preserve complete historical terminators
    Iterate characters in legacy delimiter sets so bulk toggles cannot remove
    only a shared surrogate and custom rows retain one complete target. Keep
    canonical records and the existing transaction owner unchanged.

    Exercise the real bulk transaction with captured stage images and the
    native menu provider with independent same-high custom records. Native
    execution remains pending on Windows.

  • Windows: Preserve the full preview end delimiter
    The collector selected one UTF-16 unit from the delimiter list, so an
    astral first delimiter became an incomplete high half. Select its full
    codepoint before asking the canonical engine decision and deduplicating
    against the magic key.

    Cover the real collector, preview decision and engine feed with six
    scalar, ordering, empty-list and magic-key controls. Native AHK unit,
    parse and E2E execution remains deferred to Windows.

  • Windows: Index complete scalar star continuations
    Use the existing character-width helper for continuation keys so a
    consumed astral delimiter can defer an END rule to a longer STAR trigger.
    Keep bounded prefix construction and candidate eligibility unchanged.

    Cover case-insensitive and case-sensitive continuations, plus a distinct
    scalar sharing the high surrogate. Native before and after remain unrun;
    this is an isolated local candidate pending Windows qualification.

  • Windows: Retarget terminator defaults after reading magic key
    Build the effective catalogue from the admitted configured key before
    custom records load. Restore fresh shipped definitions using the active
    magic slot, preserving independent historical and custom preferences.

    Local candidate only: native BEFORE remains blocked in its writer fixture
    and native AFTER has not run. Full scalar framing is a separate change.

  • Windows: Complete scalar framing before prefix decisions
    InputHook can deliver supplementary input as separate UTF-16 callbacks.
    Raw halves previously reached prefix boundaries, activation timestamps,
    prediction observers and agent pauses before the character was complete.

    Retain raw units once in each owned context and defer semantic consumers
    until that context completes its scalar. Retire derived work immediately,
    and revoke incomplete context on focus, navigation and lifecycle changes.
    Preserve complete chunks and ordinary word-reset settings.

  • Windows: Match only complete hotstring scalar tails
    Native Unicode packets deliver surrogate units in separate callbacks.
    Substring membership could expand on the high half and then discard the
    full delimiter, including when a maximum-length trigger filled the buffer.

    Retain pending framing without assigning delimiter membership, then match
    the complete scalar from the observed buffer. Orphan predecessors cannot
    create word boundaries or license repeat fallbacks. Keep complete chunks
    and preview state unchanged. Twelve public-API regressions cover these
    paths; native AHK execution remains deferred to Windows qualification.

  • Ahk: Split named input controls with separate delimiters
    AHK v2 treats a String delimiter as one substring. The scalar reader
    therefore kept each brace-wrapped allowlist intact and rejected native
    Escape, Enter, Backspace and Delete names as unknown input.

    Use separate brace delimiters while retaining case-insensitive exact
    name matching and joined-name refusal. Preserve the strict cancellation
    warning observer and add a single-name Escape regression with its own
    allowlist, alongside the existing Escape-outside-list negative case.

    A 38-case replay selects all 24 prior decoder vectors, the new Escape
    case and the existing passive/native control paths. Native-after
    qualification remains pending; 13 scoped Linux checks passed.

  • Ahk: Preserve Unicode scalars in the magic key editor
    The character editor also bounded capture by one UTF-16 unit, allowing
    an incomplete supplementary character to reach its persistence route.
    Use the canonical synchronous scalar capture and validate complete text
    after the existing exact-owner settlement acknowledgement.

    Prepare membership before creating the dialog and retain the genuine I1
    filter. Preserve physical SourceCapture and both editor lifecycle fixes.
    Keep the 18 ownership/startup controls and add complete-pair, malformed
    and six native I1 capture controls in a 30-case replay profile.

    Native-after capture and callback arguments still require Windows
    qualification; this does not claim configuration writes or application
    receiving. All 13 scoped Linux checks passed with unchanged sources.

  • Ahk: Capture complete Unicode scalars in text hooks
    Native L1 capture limits UTF-16 units and split supplementary input in
    both one-shot shift and dead-key composition. EndChars now stops on a
    complete BMP unit or a matching low surrogate before the next event.
    Keep named controls separate from text and refuse incomplete captures.

    Promote the measured prototype table and decoder into their canonical
    owner. Warm the static table before configured tap-holds can fire;
    per-hook native construction remains independent. Preserve the shared
    one-shot results and apply them to the complete configured magic key.

    Keep existing ownership regressions and add scalar/control/invalid-input
    coverage with a 91-case replay profile. Native-after qualification is
    pending Windows; Linux scoped checks and source projection are recorded.

  • Windows: Own magic key capture before showing its dialog

  • Windows: Reject revoked magic key capture results
    Completed InputHooks retain their result after Close or pause retires
    them. Both editors must retain the acknowledgement from the terminal
    path that still owns and retires that exact hook after native Stop.
    Keep idempotent lifecycle cleanup and refused Stop retry ownership.

    The dedicated Windows before receipt reached ten stale publications in
    each ordering while all four live and cancel controls passed. Preserve
    those fourteen oracles and isolate fixture globals from genuine locals.

  • Windows: Retain screenshot stage cleanup debt

  • Windows: Keep restore comparison inside the clipboard lease
    Hold the native lease from sequence validation through the ClipboardAll
    assignment. Defer busy or borrowed leases without sampling or restoring,
    and release before diagnostics or logical settlement.

    Cover copies at the comparison boundary and after completion, plus foreign
    content and borrowed native-lock controls.

  • Windows: Fence bitmap publication at native admission
    Revalidate the exact direct-capture generation under the clipboard lock
    before reserving a mutation or clearing data. A capture retired during
    its snapshot or image load must preserve the next clipboard owner.

    Cover eight admission cases and replay sixteen existing custody controls.

  • Windows: Fence failure publication by captured epoch
    The real logger can yield to dependency cancellation after the initial
    captured-epoch check. The old publisher then overwrites the newly pending
    state and calls the old failure callback. Recheck the epoch under a short
    critical section when publishing state and after the existing priority
    callout before delivering the business callback. Keep callouts outside
    Critical and preserve the legacy zero-epoch contract.

    Five controls use the real logger sink, real one-shot cancellation timer,
    real cancellation API and real priority setter through its existing port.
    Current delivery and initial stale refusal retain positive and negative controls.
    This does not add authority for same-epoch bootstrap cancellation.

    Validation: encoding, syntax, conventions, repository formatting, profile
    source projection and registration prechecks pass. Native AHK is unavailable
    on Linux. The full JS gate is explicitly deferred to the parent's combined
    qualification after dev70, avoiding another run on inherited b28 failures.

  • Windows: Reject revoked managed runtime exit callbacks
    A runtime owner captures its exit callback before leaving the terminal
    critical section. Cancellation can retire that owner before the bootstrap
    receives the captured callback, which previously published a stale failure.
    Validate the exact target and bootstrap admission before failure delivery,
    then recheck publication admission after bootstrap UI and log calls.

    Keep real current exits reportable after the runtime task becomes zero.
    Seven controls cover real terminal delivery, timer cancellation and source
    or epoch withdrawal, retained callbacks and foreign-target refusal. A replay
    profile retains two existing terminal ownership controls alongside them.

    The separate logger-before-state window inside the shared dependency
    failure publisher is outside this change. Native AHK qualification remains
    pending; local verification details are recorded in the audit receipt.

    Local selected verification: formatting and AHK encoding pass; JS444/449
    with the five inherited b28 failures (legacy clone imports, tray mutant,
    and the unchanged dev TOML pin mismatch). AHK execution/parse/E2E are not
    available on Linux. Root, config and independent review found no blocker.

  • Windows: Preserve admitted one-shot shift on rearm
    A second exclusive capture can be refused while the first Wait pumps
    messages. Clearing its shared latch turns the admitted result into raw
    input and silently cancels the user's pending Shift.

    Publish the latch only while its exact token and hook are still owned,
    atomically with the suspension check. Retire a revoked admission without
    changing a successor's state or extending the original timeout.

    Exercise genuine LAlt and RCtrl activation paths, explicit cancellation,
    and start-time revocation in eight isolated passive native fixtures.
    Include a dedicated replay profile. Native AHK execution remains deferred
    to Windows CI on this Linux host.

  • Windows: Retain each interrupted reload hold receipt
    A later tray selection can interrupt the first hold diagnostic and advance
    the global key before the first call returns. Capture the allocated key
    in one pure expression so both retries keep their own retained paths.

    Exercise real timer interruption through the logger boundary and normal
    dispatch, covering refused reloads and accepted successor handoffs.

  • Macos: Reject clipboard snapshots changed during acquisition
    A separate process could copy after the builtin snapshot was read but
    before its first ownership count. The expansion then adopted the newer
    generation and eventually restored its older snapshot over that copy.

    Require a known stable generation around snapshot capture and before the
    first payload write. Refuse unstable captures before acquiring clipboard
    debt, and retire unmutated debt when ownership changes later. Preserve the
    programmable publication lease and existing postmutation ownership rules.

    Add 13 public expansion regressions covering native empty and rich boards,
    foreign copies, count refusals, cancelled followers and healthy successors.

  • Macos: Refresh private browsing protection before resume
    Resume refreshes the foreground context while the pause transaction still
    holds its fence. The ordinary window callback therefore skipped privacy
    classification and left a previous nonprivate context allowed.

    Classify only coarse window privacy at this trusted boundary and revoke
    old title, document and fullscreen metadata before native reads. Preserve
    paused callback silence and incomplete physical observations. Keep native
    classification failures denied until a healthy foreground refresh.

    Add 14 public lifecycle regressions and strengthen the existing paused
    physical-observation case to require current privacy without admitting its
    incomplete receipt.

  • Macos: Revoke logging when foreground refresh fails
    A failed native foreground lookup during resume left the former context
    allowed even after synthetic admission reopened. Revoke persistence before
    refresh and retain that refusal until a complete foreground writer commits,
    independently of optional privacy preferences.

    Preserve valid modifier holds on healthy activations and capture. Accept
    absent bundle or path metadata only after successful native reads, while
    requiring a valid display name and process identifier.

    Cover public pause/resume admission, native failures, policy settings,
    recovery, physical observations and exact runtime cleanup in 25 regressions.

    Model successful context refresh acknowledgements in the existing modifier
    fixtures. Supply the native application identity readers in the boot world
    so its WPM scenarios exercise an accepted foreground context. Keep their
    input, modifier, recovery and WPM assertions unchanged.

  • Macos: Preserve newer copies after builtin text expansions
    Track the native clipboard generation for builtin pastes before snapshot
    reuse and restoration. Retire superseded snapshots without overwriting a
    newer user copy, including overlapping successors. Preserve exact native
    clear receipts for rollback and retain ambiguous failures without writing.

    Add 14 public expansion regressions and align legacy pasteboard ports with
    the pinned Hammerspoon clear and change-count contracts.

    Bound the existing restore-source guard by its unique function declaration
    and next local instead of a fixed character window. Retain its native
    false/nil retry assertion and verify detection against source mutants.

  • Macos: Preserve foreground intervals during rollover retries
    Retrying a refused day rollover could move a newly activated app back
    to midnight and credit time from before its activation. Treating the
    local hour label as elapsed time also shifted durations across DST.

    Convert the current calendar and midnight to actual instants with their
    own offsets, and never move an interval start backwards. Cover public
    maintenance retries, app switches and both DST transitions.

  • Macos: Refuse incomplete typing manifests before publication
    Manifest SQL refusals invalidated the cache but their completion receipt
    was lost before the dashboard saved and published partial data as fresh.
    Expose the receipt without changing either existing payload return, and
    require it for both the initial publication and live updates.

    Keep the last accepted snapshot and view during refusal, then recover on
    a later successful read. Certify healthy publication fixtures explicitly
    and cover persisted snapshot preservation, empty data and retry.

  • Macos: Preserve foreign copies before text clipboard writes
    A copy arriving between a clipboard snapshot and the first owned write
    could be overwritten and later replaced by the old snapshot. The plain
    paste, selection transform and selection wrapping paths shared this gap.

    Certify the native generation across capture and recheck it immediately
    before the first mutation. Refuse a changed or unavailable generation
    without writing, restoring or emitting a key, and retire the unused owner.

  • Macos: Preserve app intervals during foreground recovery
    The event-tap watchdog recaptures the foreground application after a
    restart. Recapturing the same app reset its open interval without logging
    it, losing elapsed app time and restarting its first-key measurement.

    Preserve both clocks for a continuing interval while refreshing native
    metadata, privacy state and accessibility observation. New intervals and
    real app switches still establish fresh clocks.

  • Macos: Retire typing history before cancellable merges
    A dashboard close can cancel the paced JSON encode after a historical
    merge has changed its cached data but before its watermark is advanced.
    Reopening then merges the same day twice.

    Retire only that entry after successful SQL reads and before mutation.
    Readmit it only after encoding and watermark updates complete, so an
    interrupted job rebuilds history without touching other accepted ranges.

    Integration onto dev: dev fixed the same defect by building the next entry
    aside without mutating or retiring the accepted one (ac1ad9b), so the
    projection keeps dev's code. The retirement tests are kept and assert that
    contract: during and after an interrupted or refused extension the
    accepted entry stays admitted, unmutated and never double counted. The
    fixture matches the reader frame with either path separator.

  • Macos: Reject incomplete current-day typing metrics
    A failed current-day query could still be published as fresh because the
    range receipt certified historical reads only. Carry the today receipt
    through the reader and projection before saving or publishing a range.

    Keep partial dictionaries available to callers while refusing incomplete
    dashboard publications. Cover all twelve sources, recovery, empty reads,
    public delivery correlation and exact fixture cleanup.

  • Macos: Preserve typing history after transient SQLite errors
    A failed SQLite query could certify an empty or partial historical range
    under a valid fingerprint, keeping incorrect counts through later retries.
    Propagate bounds and row completion before caching, advancing a watermark
    or publishing a fresh snapshot. Retain accepted history on refusal.

    Cover cold and incremental retries, every n-gram query family and public
    publication refusal with regression tests.

    Integration onto dev: dev already returned the read_ngrams completeness
    flag and built the next history entry aside (ac1ad9b), so the reader
    keeps dev's code. The projection keeps dev's build-aside structure and
    adds this change's intent: an incomplete extension leaves the accepted
    entry and watermark in place, and session.range returns the history
    receipt the dashboard uses to refuse saving or publishing a partial read.

  • Linux: Retain CapsWord output debt until acknowledged
    Forgetting a CapsWord owner before its unlock acknowledgement allowed a
    later call to report success while CapsLock remained unresolved. Retrying
    an unknown or partial emission could toggle the desktop a second time.

    Retain the original emitter and retry only a typed refusal proving that
    CapsLock output never started. Keep unknown outcomes blocked, preserve
    the emitter's outcome through the shortcuts manager, and refuse a modal
    before releasing input when the unlock has not been acknowledged.

    Emergency stop keeps its existing best-effort kernel ungrab contract.

  • Linux: Retain configuration ownership for profile actions
    An open editor or old profile menu could adopt a configuration folder
    selected after the menu was built, including a byte-identical copy. A
    selection or deletion could also cross that boundary during confirmation.

    Retain the original path, source, revision and ports through final writer
    admission for creation, editing, cloning, selection and deletion. Keep a
    refused draft open and require a fresh menu for a successor configuration.

  • Linux: Count prediction trigger deletions as characters
    The trigger suffix is sliced from the prompt in bytes, but accepting its
    prediction sends physical Backspaces and character metrics. A configured
    UTF-8 trigger could therefore erase text preceding the trigger itself.

    Keep prompt slicing byte-exact and count only the removed suffix through
    the existing UTF-8 owner. Preserve the established invalid-byte fallback
    and cover accepted edits with an independent document sink.

  • Macos: Keep backslash bytes in ui paths
    The tooltip configuration, onboarding wizard, path and keyboard layout
    menus, apps menu and hotstrings window treated a backslash as a path
    separator. They now go through the shared native path owner.

  • Macos: Keep backslash bytes in remap paths
    The remap onboarding, defaults, data directory and the launcher helper
    resolution treated a backslash as a path separator. They now go through
    the shared native path owner. platform/remap/init.lua sits at the
    200-local limit, so it requires the owner inline instead of adding a
    top-level local. The navigation layer and generator sites already landed
    with the ISO keyboard fix.

  • Macos: Keep backslash bytes in module paths
    The crash reporter, keyboard layout installer, input sources and the
    keylogger log manager and Karabiner bridge treated a backslash as a path
    separator. A layout name holding one was refused as if it named a folder.
    They now go through the shared native path owner.

  • Macos: Keep backslash bytes in infra and bootstrap paths
    Configuration, logs, caches, manifests, personal hotstrings, diagnostics
    and the Karabiner manifest split real macOS paths with [/] classes,
    cutting a file or folder name at a backslash although macOS stores it as
    an ordinary byte: a hotstring group named "back\slash" became "slash".
    They now go through the shared native path owner. The init.lua bootstrap
    block, which runs before the shared Lua root is on package.path and only
    inside Hammerspoon, splits at slashes directly.

  • Windows: Judge keyboard slots against the input owner's published domain
    Three Windows owners judged keyboard slots by three rules. The boot
    loader and the unused-key cleanup used a lowercase four-group grammar;
    the input owner (ReadKeyboardShortcutsConfig and the registration loop)
    used none, so a hand-written ctrl_f1, a case variant CTRL_K or a
    win_SC0FF scan the physical registry lacks became a live hotkey or a
    boot-time ValueError while the cleanup offered to delete it, and a live
    physical slot such as win_SC03A was offered for deletion. The parameter
    owner never judged a keyboard binding, so a parameter of a slot this
    build lacks stayed unread and unoffered forever.

    KeyboardSlotDeclaration now owns the slot groups and keys the tray
    picker offers, and KeyboardSlotIsCatalogued is the one rule the loader,
    the cleanup and the input owner ask: the manifest slots, the four chord
    groups over the picker keys, and any Win slot whose scan the physical
    registry has. KeyboardSlotCatalogueIds publishes the complete domain to
    the shared keyboard identity rule, so keyboard__ parameters are judged
    current, retired or (with no loaded manifest) unjudged: a retired one is
    warned once, reads neutral, refuses replacement and is offered for
    explicit cleanup. The picker reads its keys from the same declaration.

  • Macos: Deploy the ANSI virtual keyboard so the ISO pair passes through (#99)
    The generator set Karabiner's virtual_hid_keyboard.keyboard_type_v2 to the
    active layout's keycode convention, so an Ergopti (ISO) layout deployed the
    ISO product. An Apple ISO keyboard already reports the key left of 1 as
    non_us_backslash (0x64) and the key left of Z as grave_accent_and_tilde
    (0x35), as the physical-key registry's macos_iso names record; the ISO
    product swaps those usages once more, so every key Karabiner passed through
    typed the other key's character. The maintainer had to set Karabiner to
    ANSI to get ISO behaviour, and every deploy wrote ISO back.

    Ergopti now always deploys the ANSI product (Layout.VIRTUAL_KEYBOARD_PRODUCT),
    which keeps each keyboard's own keycodes, and replaces an ISO value an older
    build wrote. Logical-char actions and navigation-layer outputs are named for
    that product: the outputs use the same registry ISO projection as the inputs,
    and every output still reaches the keycode it did before. The layout's
    convention keeps deciding which keycode a key arrives as, for tap keys and
    the magic key (keyboard_geometry), unchanged.

    A table-driven test covers the four combinations (ISO or ANSI keyboard
    behind the ISO or ANSI product) from an independently declared product
    table and the registry; the ISO product fails it for both keyboards.
    Windows (SC029/SC056) and Linux (evdev 41/86) name these keys by position
    and have no virtual keyboard, so neither driver changes.

  • Launcher: Say what the lease worker was doing when its input closed
    Hammerspoon closes the worker's input without STOP when it fails the
    lease on an acknowledgement deadline. The outer then fenced silently:
    EOF is a revocation, not a loss, so it wrote no cause. Issue #99 had six
    PONG and five READY deadlines and no worker-side fact about any of them,
    so a PING that never reached the worker could not be told from an inner
    still waiting on karabiner_cli.

    On the first EOF that no STOP preceded, the outer now writes the last
    private command, how long ago it was sent and whether it was answered.
    Activation preparation (durable record and guardian ARM, then the
    suspended inner spawn) is reported with its parts when it takes a
    second or more, since Hammerspoon's READY deadline covers it too.

  • Macos: Report the evidence behind every Karabiner lease timeout
    Issue #99 logged six "timeout waiting for PONG 1" and five "timeout
    waiting for READY" failures with nothing else: not how long the driver
    had waited, not whether its own run loop had held the deadline, not how
    the worker ended once the driver had failed it (the completion line was
    dropped because the generation had already failed), and READY took 3.4
    to 6.0 s in the leases that survived without any line saying so.

    An ACK timeout now writes, beside the unchanged failure reason that the
    diagnostics report parses, how long it waited and how late its timer
    fired; a READY slower than a second reports its latency against the
    deadline; the exit of a worker the driver already failed is logged with
    its last stderr.

    The user also reported swallowed keys while the log held no drop: every
    loss happened upstream of the event taps, which the keystroke ring
    cannot see. A fence of a live lease and a revived keyDown tap now report,
    when keys were typed in the preceding two seconds, how many crossed that
    transition.

  • Config: Offer dotted and inline Windows cleanup rows by their real path
    Boot reads config.toml through the typed document, so an unknown or
    outdated leaf it warns about may be spelled as a root dotted key
    (metrics.old = true), an inline table member (layout = { old = 1 }), a
    child of an inline feature record or a dotted key inside a section. The
    unused-key cleanup still scanned the flat [section] key = value view:
    it never offered the first three spellings, so the startup offer did
    not even appear, and it offered the last one under a literal dotted key
    name whose deletion changed nothing while the cleanup reported
    "removed", wrote a backup and offered the same row again next time.

    ConfigUnusedKeysFind now classifies the semantic rows of the snapshot
    the loader applies, with the loader's unknown-key and outdated-value
    rules and its feature-record child policy, so each row carries the
    section/key pair the writer resolves to the same semantic path. The
    retired [ahk] handling keeps its flat and private whole-root paths, and
    a bare root key keeps its earlier contract of staying out of the
    preview. A removal whose rows name no value of the current source is
    "changed" before any backup, never a reported success.

  • Updater: Refuse a feed-only Versions install with a warning
    With the GitHub API rate limited, the changelog window lists the Atom feed,
    which names no release files or digests, so an install click is refused
    with the page's "try again in a few minutes" reason. That expected refusal
    logged "it is not in the release list the window loaded" as an ERROR,
    which opened the error window over the page (#99, twice) and named the
    wrong cause.

    The shared Lua install session and its Windows twin now log that case as
    a warning naming the feed. A tag missing from an API list (a stale page)
    stays an error.

  • Macos: Read the releases Atom feed when the update check gets no API list
    Every manual update check of #99 answered "HTTP 403": the anonymous GitHub
    API limit (60 calls an hour, shared with the changelog window and the
    diagnostics) was spent, and the check had no other source although the
    changelog window already falls back to the public releases feed on
    github.com.

    The manual and the background checks now read that feed when the API gives
    no list, as a list of tag names and dates (the shared pure reader
    updater.release_sources.feed_release_list, same repository rule as the
    page's atom_feed.js). On macOS this is all a check needs: Sparkle installs
    the offered release from the channel's appcast, never from the API. The
    feed never replaces the stored API copy or its ETag; a list the API
    answered but that does not parse is still a parse failure. Windows and
    Linux keep the API only: their installs need the API's asset digests.

  • Llm: Probe local AI servers at the IPv4 loopback literal
    The local server sweep of #99 and #95 published "none answers" every time
    while oMLX ran, and an oMLX address typed by hand failed too. oMLX, LM
    Studio, llama.cpp / LocalAI and Jan all bind 127.0.0.1 by default, but the
    catalogue named them at localhost, which a client may resolve to ::1 first
    (where they do not listen), send through the system proxy (this Mac has an
    automatic one), or answer slowly. Ollama and MLX, which answer on the same
    Mac, were always probed at 127.0.0.1.

    The catalogue now holds the IPv4 literal; endpoint affinity ignores the
    host, so an entry configured with localhost still matches its server. The
    macOS sweep also logs each server's answer (HTTP status or the transport
    error) at DEBUG, since the total alone named no cause. All three drivers
    read the same catalogue.

  • Macos: Check the AI runtime config.toml selects at boot
    The boot bootstrap read the backend from an hs.settings key that nothing
    writes, so on Apple silicon it always took the MLX default: an Ollama or
    API user got the MLX dependency check and the "MLX is not installed, the
    AI stays off until it is" notice at every start (#99, five times), while
    the menu applied the backend config.toml selects.

    effective_backend() now takes that selection first, and init passes the
    one Preferences loaded; the hs.settings key and the platform default only
    answer when config.toml selects none.

  • Macos: Name the refused system proxy mode in the download admission log
    Both model downloads of #99 stopped with "The system proxy configuration
    cannot be used by this client": the Mac has an automatic proxy setting
    that the Ollama client cannot follow. The line did not say which one, and
    the log is all a maintainer gets. It now names it: an automatic proxy
    configuration file (PAC), automatic proxy discovery (WPAD), or a SOCKS
    proxy without an HTTPS proxy. The admission marker and the notification
    are unchanged.

  • Macos: Log a gesture settings read cancelled by shutdown at INFO
    A quit or a reload while the boot reads the system trackpad settings
    cancels that read; nothing is lost, the next start reads them again. It
    was a WARNING, three times in the log of #99.

  • Macos: Turn the AI off with a warning when the model download is declined
    On a start with Ollama selected and its model not pulled, the requirement
    check asks to download the model. Closing that prompt logged "Disabling
    LLM (requirements check failed)" and "Startup-cycle settlement remains
    unsettled" as errors, and every ERROR opens the error window, so it
    appeared over the dialog the user had just answered (#99, twice).

    The system check now tells why it cancelled (download declined, or a disk
    too small for the model, which its dialog already said), and the startup
    controller turns the AI off with a warning naming that cause. A terminal
    delivered from inside its own requirement task waits for the armed
    observer to join that task; that expected deferral is logged at INFO, and
    only a settlement with no observer stays an error. An unexplained failure
    still logs an error, now with the manager's reason.

  • Macos: Read the diagnostics configuration where macOS keeps it
    The configuration probe of the diagnostics called config_paths.config(),
    which only the Linux driver has, so on every Mac it answered "error" and
    the report listed no configuration file or setting (#99). Every test
    injected path_of, so the production resolver never ran.

    config.toml now resolves through ConfigTomlPath (the hammerspoon/ folder)
    and the shared files (layers.toml) at the root of the configuration
    folder. The regression case drops the injection and keeps the real
    config_paths surface, so a call to a function it lacks fails again.

  • Macos: Let the lease READY deadline cover the worker's launches
    The worker's ARM and command budgets start once it runs, but READY also
    waits for Hammerspoon to launch the outer worker and for the outer to
    launch its suspended inner: two starts of the launcher executable. On
    the loaded Mac of issue #99 a start took about 2 s (detached revokers
    and guardian probes ran 2.0 to 2.2 s, and the probe's own 2.0 s deadline
    expired twice), leases that did activate took 3.4 to 6.0 s, and every
    slower one failed at the 7.5 s deadline. Each failure redeployed the
    743 KB karabiner.json and started one more lease, five times in a row.

    READY now adds a 4 s launch allowance (11.5 s in all). Until READY the
    generation's rules are inert, so waiting longer costs only the delay of
    a recovery that would otherwise have failed. The budget parity guard
    requires the allowance.

  • Macos: Name the timer callback that held the main thread
    A main-thread hold raises no error, yet it is what disabled the event
    taps and cost the Karabiner lease in issue #99: one ingest flush held
    Hammerspoon for 13.4 s and no line said which callback it was. The log
    only showed its consequences (tap restarts, a native logger stall, a
    lease fence) and a console hint printed just before it.

    The runtime capture already wraps every hs.timer callback in xpcall; it
    now also times each one on the monotonic clock and, past one second,
    writes one WARNING naming the callback's source line and how long it
    held the thread. A callback behind the timer scheduler's own closure is
    resolved to the owner's function. One site is reported at most once a
    minute, so a callback that stalls on every tick cannot flood the log.

  • Macos: Log an application that does not answer Accessibility at INFO
    hs.axuielement raises "Messaging failed" (kAXErrorCannotComplete) when
    the application does not answer, as one still launching does. Issue #99
    logged four of them as WARNINGs, each followed by a fifteen-line
    traceback that named only the driver line asking. The failures came back
    within 3 ms for four different short-lived PIDs: the state is the
    application's, and its next activation attaches again.

    That one cause is now an INFO line naming the PID and what stays
    unobserved; every other observer setup failure remains a WARNING with
    its detail, and the rejected candidate is rolled back as before.

  • Macos: Resolve running applications without scanning every window
    hs.application.get(name) is hs.application.find(name, false, true). When
    no running application matches, find() falls back to hs.window.find(),
    which reads every window of every running application through
    Accessibility on the main thread, macOS allowing each application six
    seconds to answer. The keylogger's ingest flush resolves the category of
    each batched application that way, including applications that have
    quit. On the maintainer's Mac (issue #99) Hammerspoon printed the
    Spotlight hint that find() emits just before that fallback and then froze
    for 13.4 s: the keylogger and mouse taps were disabled, the logger
    stalled 13443 ms, and the Karabiner lease worker fenced itself after its
    17.5 s inner silence budget ("SET 1 sent 18.08 s earlier, answered").

    adapters/running_application.lua resolves an application by exact name,
    bundle identifier or PID from the process list only. The keylogger
    category lookup, AppLauncher.isRunning, WindowManager's app-name
    specifications, the notification click, the dialog focus step, the
    previous-application focus fallback and the metrics icon lookups (up to
    24 per window open) now go through it. WindowManager.exists keeps its
    explicit window-title search, which its port contract names.

    A ratchet admits no new direct hs.application.get or find call; the AI
    model manager keeps its two lookups of the running Hammerspoon process,
    which always match by name, until the AI lane moves them.

  • I18n: Say the bug form only prefills version, system and driver
    Debug > Report a bug opens GitHub's bug form with only the version,
    system and driver prefilled; the diagnostic report is copied to the
    clipboard and saved as a file to attach. Reword notify.report_bug_body
    in the 21 locales accordingly and drop the unreferenced
    healthcheck.status.report_mode key.

  • Macos: Let a remap setter replace the obsolete leaf it sets
    An obsolete config_karabiner.toml leaf (a timing spelled as text, a
    switch that is not a Boolean, a binding that is not a table) is kept by
    every ordinary full-state save, and a non-neutral candidate at that leaf
    is refused because a carried value never proves a repair. No caller
    could say which leaf the user had just set, so the menu setter of that
    very setting was refused on every retry: the key-combinations switch,
    the Tap-Holds switch, the timeouts, the symmetry switch and a key's or
    pair's binding could never be changed again from the menu, and setting
    a timing to its default reported success while the file kept the
    obsolete value.

    save_user_config now takes an explicit leaf intent, validated by
    Config.explicit_leaves (owned leaves only, never with a reset). Each
    remap setter names exactly its own leaf through commit_state_mutation;
    the obsolete value at that leaf is replaced (an explicit neutral value
    removes it), while every other obsolete leaf, the inverse and recovery
    saves and the startup saves keep the ordinary preservation and refusal.
    The transaction fixture records and forwards the intent so the setter
    contract is tested.

  • Timers: Refuse a delay above 2^53 ms on every Lua timer boundary
    A finite delay beyond the native integer range armed a timer that never
    fired under LuaJIT, whose double-to-uint64 conversion is undefined, while
    Lua 5.4 refused only above 2^63 ms. Lua 5.4 also multiplied an integer
    second count in wrapping arithmetic, so a huge delay turned negative and
    fired at once.

    The finite-number policy now owns one ceiling, MAX_TIMER_DELAY_MS = 2^53,
    and converts seconds in floating point. Every boundary refuses above it
    with an explicit error: the Linux native timer owner that every libuv
    timer arms through, the TimerScheduler port on Linux and macOS, the event
    loop's deferred queue, periodic timer and sleep, the nanosleep binding,
    the shell runner deadline, the desktop confirmation deadline and the
    macOS blocking sleep.

    A shared corpus of boundary vectors is replayed by both suites against the
    policy and each boundary through recording native doubles. The Linux
    native fixture no longer expects a 1e308 ms deferred delay to be queued.

Performance

  • Windows: Reuse publication proofs within each hotif scan
    Directive mutations share the same immutable entry and driver image, but
    each previously normalized the whole driver again. Reuse their local
    publication proof while recomputing every changed publication image.
    Restore the actual whole-driver graph after all adversarial probes and
    keep the scanner, existing controls and native budgets unchanged.

  • Updater: Index the release list from one decode in the shared Lua twin
    The shared manual-check classifier decoded the 6 MB release list once to
    split it, then every release span twice more for its tag and its date, on
    the macOS and Linux event loops (the shared JSON module is pure Lua).

    release_parser.index_releases returns the spans and every release's own
    tag and publication time from the one decode split_releases_array already
    makes, and check_result.classify uses it, as the Windows updater's list
    index does. The result is the same by construction; the contract replays
    every split vector, nested and non-string members, and counts one decode
    for a 30-release list.

  • Windows: Decode only the release list members the updater reads
    The update check received GitHub's release list (about 6 MB: 100 releases
    of up to 39 assets, each asset with its uploader, plus every author and
    release notes) and decoded all of it into maps on the AutoHotkey thread to
    read one tag and one date per release: about 4 s with the keyboard hook
    frozen, twice that for a manual check (the other-channel notices decoded
    the list a second time), and again on every 304 from the cached copy.

    The JSON owner gains JsonArrayObjectMembers and JsonObjectMembers: the
    whole document is still validated with the parser's own grammar, depth,
    escape, NUL-key and number rules, but only the named top-level members are
    built. Unread values go through an iterative validating skip whose fast
    path matches runs of scalar members, whole asset arrays and notes with one
    anchored PCRE call each; it accepts only what the exact walk accepts, never
    a code point above U+FFFF unless one strict WideCharToMultiByte pass proved
    the text has no lone surrogate, and falls back to the exact walk on
    anything it does not model or on the engine's match limit. Whitespace and
    number runs are scanned natively.

    The updater decodes the list once per response into an index shared by
    channel selection and the other-channel notices, keeps that index on the
    conditional-GET entry (an entry's list never changes, so a 304 decodes
    nothing), reads the selected release's tag, notes and date one member at a
    time, and the Versions window decodes a release's notes only when shown.
    The request, the 100-release window, the ETag cache and its stored copy
    are unchanged.

    Measured on the real list (5.96 MB, fetched 2026-10-21) inside the AHK
    suite on this loaded laptop: a 200 took 6.1 to 7.6 s (background) and 12
    to 14 s (manual) before, 0.36 to 0.48 s after; a 304 took 6 to 13.6 s
    before, 0.04 to 0.11 s after; the Versions list 6.2 to 7.3 s before, 0.23
    to 0.41 s after. Deterministic budget: 100 containers and 5 917 decoded
    code units for the list instead of 3 008 containers and 5 034 639 units.

    The new budget test counts containers with JsonDecodeTally on a
    GitHub-shaped 40-release list (40 instead of 6 560 with the old updater
    core) and checks the skip accepts and refuses exactly what JsonParse does.

Refactoring

  • Ci: Name every lane job in two levels
    GitHub renders a job of a called workflow under every caller on the way:
    a lane stage held in a workflow of its own showed as three levels,
    "Linux / Unit tests / core", and a matrix stage named by its row lost
    its graph group to the raw job id, "Matrix: Linux / Unit tests /
    test-linux". The maintainer reads both as redundant.

    Each lane now holds its stages as jobs again, the stage workflows
    folded back byte for byte through the loader's own inliner, so a job
    renders as " / ()". A matrix stage keeps the
    static stage name and GitHub appends its row, "Linux / Unit tests
    (core)", under the graph group "Matrix: Linux / Unit tests"; a single
    job names its subject in parentheses, "Windows / E2E tests (Engine E2E
    corpus)", "macOS / Build (ErgoptiPlus.app, macOS 26)".

    The readers and tests the stage split had moved onto the stage files
    return to the lane files, and the loader drops its stage inliner. The
    naming guard now derives every lane job name from one stage table: a
    matrix job carries exactly its stage name, a single job its stage and
    subject, and no lane job name may hold an expression or a third level.

  • Windows: Keep the screenshot GDI calls in a screen capture adapter
    6d9eae1 wrote the in-process GDI and GDI+ capture inside
    modules/gestures/screenshots.ahk, raising the direct OS calls of
    modules + lib from 252 to 279 against a ratchet that forbids raising the
    baseline. The seven native functions and their constants move unchanged to
    adapters/screen_capture.ahk, included by the owner; the wiring test reads
    the adapter and the architecture diagram is regenerated.

  • Paths: Give POSIX path splitting one shared owner
    A backslash is ordinary filename data on macOS and Linux and separates
    components only when the host itself is Windows, where the headless test
    suites run. The macOS bootstrap walk and the two shared hotstring file
    helpers each re-derived that rule from package.config.

    native_path.lua in the shared Lua root now owns it: rooted and absolute
    tests, trailing separators, dirname, directory prefix, basename,
    components and the one-component check, each reading the host per call so
    a test can model either host. The bootstrap walk and the hotstring file
    helpers use it. A shared contract replayed by both suites pins the
    POSIX and Windows-host vectors.

Documentation

  • Todo: Record the green full run and the CI red fixes

  • Todo: List the open-target and Backspace word-start changes

  • Todo: Hand over the copy_selected_path open-anything change

  • Todo: Hand over the Unit, Integration and E2E CI columns

  • Ci: Describe the Integration tests stage of every lane

  • Todo: List the key-combination action picker in the checklist

  • Todo: Hand over the key-combination action picker change

  • Todo: Hand over the Backspace word-start fix
    Records what the typing ledger proved, the four fixed rules and their
    owners on each driver, the regression tests, the known limits (browser
    inline autocompletion, Shift+Enter on Linux) and the physical checks for
    Windows Terminal and a browser address bar, with the hosted runs.

  • Memory: Record how Backspace restores the word start
    The word-start flag describes the buffer's left edge, so the Backspace
    that empties a buffer keeping its terminators is back at that edge; an
    empty line is the one place a Backspace past it deletes nothing. Also
    notes that the typing ledger batches keys about every 200 ms, so an
    incident sequence must be rebuilt across rows.

  • Todo: Hand over the tap-hold action picker change

  • Todo: Record the clean end-of-day state and the 1.0.5 release as next

  • Todo: Record the maintainer answers of October 21 as decisions to implement

  • Todo-agents: Record the dev red fixes and what is left
    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Todo-agents: Record the exe proxy fixes done and the steps left
    The handoff lists the size gate revert, the in-process screenshot capture
    and the exe version resource with their commits, the other replaceable
    hidden PowerShell launchers, and what is left: the reproducible PAC
    helper, signing, and the new hash for VirusTotal after the next build.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Todo: Add the after-release checklist with the AppImage retest

  • Todo: Record the AppImage catalog screenshot fix
    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Todo: Aim for a self-contained AppImage with update information

  • Todo: Record the Codex integration and the reds left on dev
    The previous commit of this title was empty by mistake; this one carries the text.

  • Todo: Record the integration of the Codex fix branches and the reds left on dev

  • Todo: List the dev reds left on the rebased integration tip
    Records run 39627729075 on the branch rebased onto the newer dev, whose
    remaining reds belong to dev or to a runner timeout.

  • Todo: Record the Linux archive cell root cause in the handoff
    The intermittent archive cell failure is resolved by private group
    homes; the remaining reds belong to dev and are attributed by a run of
    dev alone.

  • Todo: Record the hosted CI evidence of the fix branch integration
    The integrated commits are green on the earlier dev base; after the
    rebase onto the newer dev the reds sit in jobs the integration does not
    touch, and a run of dev alone is recorded to attribute them.

  • Todo: Hand over the integration of the Codex fix branches
    Records which Codex commits of fix/linux, fix/macos and fix/windows are
    integrated, how the two macOS conflicts with dev were resolved, the
    follow-up fixes hosted CI required, what remains and the open risks.

  • Todo-agents: Add the Linux tray menu agent to the relaunch list

  • Todo: Record the passed AppImage catalog test and its polish items

  • Todo-agents: Store the coordinator memory and relaunch prompts

  • Todo: List the fixture ownership commits in the timer and path handoff
    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Todo-agents: Hand off the v1.0.4 exe proxy antivirus verdict analysis
    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Todo: Track the Windows exe flagged by a corporate proxy

  • Todo: Hand over the Lua timer ceiling and separator owner work

  • Todo-agents: Hand off the config policy items 33 and 42
    Records per sub-item what was reproduced, the local commits, the
    remaining work with its next steps and what only a real Mac or Linux
    host can confirm.

  • Todo: Hand over the issue #99 AI startup, local server and update work

  • Todo: Hand over the release list freeze and the menu census rows
    State of the Windows release list freeze (measurements, commits, the
    pending seal) and the design and next steps for the last three native
    menu rows.

  • Todo: Hand over the issue #99 Karabiner and input work
    What landed, what remains for the AI and startup lanes, the two checks
    only the Mac can make, and the open risks of the ANSI deployment.

  • Todo: Hand over the work in flight with one handoff file per agent

  • Memory: Record the window scan behind hs.application.get
    hs.application.get falls back to reading every window of every
    application when no running one matches; the console's Spotlight hint
    is its tell in a log. Route lookups through the running-application
    adapter, and read the slow-callback report and the lease timeout
    evidence first when taps restart or a lease fences.

  • Memory: Integrate fix branches linearly and preflight before pushing dev
    The fix-branch entry still prescribed --no-ff merges, which the
    maintainer ruled out on October 21.

  • Todo: Bring the checklist up to the October 21 midday state
    v1.0.4 shipped with nothing deferred, the closed issues keep only their
    Mac checks, the CI layout and the Windows and macOS parity backlogs are
    done, and the NTLM-only proxy gap leads the Windows items. The answered
    maintainer questions move to the answered list.

Tests

  • Windows: Let the curl literal-path children start on a slow runner
    curl-literal-paths waited 5 s for each wrapped cmd.exe and curl.exe
    child. The test is the first in the llm-servers shard to start curl and
    usually takes 0.7 s in total, but in run 39713353269 one child was still
    running at 5.5 s while the next test, starting the same programs warm,
    took 1.2 s. That is a cold first launch on a loaded hosted runner, not a
    lost exit: retirement then succeeded at once.

    The four waits on real children now share a 30 s bound. Each wait ends
    as soon as its condition holds, so the bound costs nothing on success.

  • Linux: Count dry-run dispatch calls with the JIT suspended
    dynamic dry-run user posture false failed once on the Arch installed
    archive lane of run 39709106231: the injector call was counted but the
    user-code request or source read was not. The test counts calls with a
    debug.sethook call hook, and LuaJIT calls no hook from an already
    compiled trace: a loop warmed before the hook is armed reports 2 of
    100000 calls, and 100000 once jit.off() and jit.flush() have run. Which
    caller was compiled depended on the earlier tests of the group.

    The hooked window now runs on the interpreter only, asserts that the JIT
    is off while counting, and turns it back on afterwards, also when the
    fixture raises.

  • Linux: Keep the non-/tmp watcher fixture out of the driver tree
    make_non_tmp_dir created ./ergopti_fw_nontmp_/init.lua relative to
    the working directory, which the runner sets to the driver root. On the
    Alpine installed-archive lane a parallel group's production caption walk
    (linux-native-titles) listed that init.lua and then read it after the
    watcher test had removed it, failing with "No such file or directory".

    The fixture now asks mktemp -d for a private directory under TMPDIR or
    TEMP, or /var/tmp when that root is spelled /tmp, so the watcher's /tmp/
    filter still does not hide it, and it refuses any path under /tmp/ or
    inside the driver tree. fw-nontmp-outside-source-tree pins both.

  • Windows: Let the native listener fixture start on a slow runner
    groups247-native-identity waited 5 s for a cold Windows PowerShell 5.1
    child to publish its listener receipt. It takes about 1 s on a hosted
    runner, but the llm-servers shard of run 39700749511 ran about twice as
    slow overall and the receipt was still missing at 5.13 s, with nothing
    in the failure about the child.

    The fixture now uses only .NET calls: New-Object, ConvertTo-Json,
    Join-Path, Move-Item and Test-Path each made the cold shell autoload a
    module before the receipt existed (about 100-200 ms locally, far more
    under load). The waits are bounded at 30 s, which costs nothing on
    success because each wait also ends when the child exits, and a missing
    receipt now reports the child's exit code and stderr.

  • Macos: Scope the open-target fixture's module stubs
    test_open_target_vectors installed recording adapters.shell_runner,
    file_system, notifications, window_info and logger stubs straight into
    package.loaded and never restored them, so the next test file the runner
    loaded could capture them at require time. The shell_runner stub-restore
    meta gate caught it on the macOS runner. Each case now runs inside
    helpers.with_fresh_modules, which restores every owned cache entry even
    when an assertion raises.

  • Shortcuts: Pin the two open-target ports to one corpus
    The classifier exists in Lua and in AHK. The gate checks the corpus,
    keeps the IP-address regression vectors in it, compares the two ports'
    vocabularies, ties the macOS and Linux search fallback to the
    shortcuts.search default and checks that every suite replays the
    corpus and every locale carries the four notices.

  • Windows: Read the Ollama file component step from integration-ahk
    The component shell probe parsed its workflow body from the test-ahk job,
    which no longer owns that step, so the native integration lot of run
    39688508159 refused before receiving the component.

  • Ci: Admit whole-file integration entries in the module group checks
    The integration group of each Lua driver's module_groups.lua names whole
    test files, but the meta check that every prefix holds a test compared
    them as folders, so it failed on the Linux and macOS meta rows of run
    39688508159. A ".lua" entry now has to name exactly one discovered file.
    The Swift launcher CI check still expected package-macos to need the
    Unit job and package-linux to read ci_unit_result; both now follow the
    Integration stage.

  • Windows: Ignore foreign clipboard readers in lock assertions
    Two clipboard tests asserted that no window at all held the clipboard
    after a release, and three fixtures opened it once and required
    success. A clipboard history reader that opens the clipboard right
    after each change made them fail at random (holder HWND 11208000 seen
    locally).

    The release assertions now require that no window of this process holds
    the lock, which is the leak they guard against, and the fixture opens
    wait for transient readers through CB_OpenClipboardWithin.

  • Macos: Pick live tap-hold choices through the shared action picker
    The live lease-transition cases chose a key's tap and hold from the action
    submenus that 61ab0fb replaced by the shared action picker, so every
    case failed on CI before reaching the transition it guards. They now open
    the slot row's picker and confirm the action there; the transition
    assertions are unchanged.

  • Macos: Stub the keymap's new dependencies in the fail-fast loader
    d01c21e made the keymap read the focused application through
    adapters.text_sender and modules.llm.app_filter. The fail-fast loader
    test replaces every keymap dependency with a stub; without those two it
    loaded the real modules against its stubbed logger and failed before
    the registry refusal it asserts (hosted run 39672299252).

  • Windows: Keep the component shell probe off the compat session
    The managed Ollama component shell control timed out once on a hosted
    runner (spawnSync pwsh.exe ETIMEDOUT after 15 s). Bare pwsh starts in
    0.65 s here; the probe took 5-8 s because it called Write-Output while
    the Windows PowerShell 5.1 module path led PSModulePath. pwsh then
    autoloaded Microsoft.PowerShell.Utility from System32 through the
    Windows PowerShell compatibility layer, which launches powershell.exe
    and opens a remoting session, contradicting the native_calls=0 claim.

    The probe now only returns a value; output happens after the original
    path is restored, and the control refuses any compatibility session.
    The child drops to about 1.2 s locally, the 15 s bound is unchanged,
    and reintroducing a cmdlet inside the probe fails the new check.

  • Lua: Flush LuaJIT traces before proving the ledger releases a cycle
    A trace recorded while Authority.current called the still-monomorphic
    keyboard getter keeps that closure as an IR constant, and trace constants
    are GC roots. Its upvalue then pins the withdrawn owner, so the weak-ledger
    assertion failed in whichever lane's shared hot counters fired on that call
    (Debian installed-archive, run 39658930106). Embedded LuaJIT 2.1 with
    hotloop=1 reproduced it every time; with the flush 4500 runs pass, and a
    strong-valued or strongly referenced ledger still fails every run.

  • Windows: Bind parameter tests to catalogued keyboard slots
    ef238c2 judges keyboard__ parameters against the published slot
    catalogue: an unknown slot is retired, reads neutral and refuses writes.
    The LLM vision, translate, prompt prediction and live tests used invented
    slots (vision_test, translate_test...) and the gesture persistence test a
    macOS cmd_k, so 46 cases saw empty parameters. They now use real Windows
    slots (ctrl_shift_*, ctrl_k); the production rule is unchanged.

  • Macos: Follow the native path join of the hotstrings override bootstrap
    2f52d00 joins the config dir and hotstrings_config.toml through
    native_path; the secondary-destination gate still matched the old plain
    concatenation. The redirect-to-config.toml mutation control is unchanged.

  • Macos: Classify a backslash group path under POSIX rules on any host
    2f52d00 made macOS paths POSIX: a backslash is a file-name byte. The
    classification case still expected a Windows split of C:\hotstrings\mygroup.toml.
    The case now models a POSIX host and pins that the backslash survives.

  • Windows: Replay the JSON whitespace scan in the number-row fixture
    8a43bd8 makes the JSON owner skip whitespace and number runs with the
    native text adapter's msvcrt wcsspn. The passive number-row ownership
    fixture replays only wcscspn and refuses every other native call, so its
    first JsonParse of the layout registry threw and all 16 cases failed
    before reaching the number-row callbacks.

    The fixture now replays wcsspn as well, unit by unit and from memory only;
    every other native call, layout and profile probes included, stays
    refused.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Windows: Pin the updater notes to the single member decoder
    8a43bd8 reads a release's notes through _Updater_ReleaseMember and
    JsonObjectMembers, which validates the whole object with the complete
    parser and decodes only the named member through its own object grammar.
    The single-decoder meta test still required Updater_ParseBody to call
    _Updater_DecodeRelease and failed on the new, still single, decode path.

    It now follows that chain down to _JsonParseObject, the object grammar
    JsonParse itself uses, and refuses any replacement or pattern decode in
    Updater_ParseBody and _Updater_ReleaseMember. The behavioural round trip
    of every escape through Updater_ParseBody is unchanged.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Macos: Own the running-application adapter in module-scope fixtures
    0292ec4 routes the notification click and the typing dashboard's icon
    lookups through adapters.running_application. The gesture actions, the
    selection translation and the typing metrics fixtures construct those real
    owners but did not save and restore the new adapter, so their scope-residue
    checks found it left in package.loaded after every scenario.

    Claude-Session: https://claude.ai/code/session_01FZ7MWJ1ujpDyWosp4vTXY3

  • Windows: Pair each WebView2 Close with its own subscriptions
    The teardown-order guard compared every subscription release in a host
    file with every controller Close in that file. The personal info editor
    now closes a half-built local Controller in its open path, after moving
    its local MsgSub and NavSub away, and well above the reset that releases
    _PiEdWeb_MsgSub and _PiEdWeb_NavSub before _PiEdWeb_Controller.Close().
    The guard read the correct reset as a release after a Close and failed
    the shared ui suite.

    A release now counts only for the controller with the same name prefix,
    and a controller whose own subscriptions are stored but never released
    fails. Moving the editor's releases after its Close, or deleting them,
    still fails the guard.

  • Windows: Expose invalid managed JSON response decoding
    Exercise the actual managed completion and remote JSON consumer with raw
    response bytes before they can be replaced during text decoding. Keep
    independent controls for valid Unicode, replacement characters, generic
    text, empty responses and caller-owned binary output.

    Native Windows execution is deferred to the dedicated BEFORE replay.

  • Windows: Retain warm debt through terminal retirement
    Exercise a new committed signal while the actual exhausted terminal still
    reserves its job slot. An early retry must keep the exact new owner until
    retirement, then publish the full and live projections without duplication.

  • Ci: Replay pending metrics and personal editor recovery
    Select four metric budget and six personal editor cases in both orders.
    The exact normal-index candidate passed FORMAT and 448 of 449 JS checks;
    Check 122 matches the measured dev baseline. Native replay is pending.

  • Windows: Initialize deferred Ollama menu dependencies
    The real LLM menu initializes indentation choices while its owner loads.
    Include the canonical manifest reader and shared display policy before
    that initializer so the private deferred-delivery child can reach main.

    Keep the real generated catalogue and all seven business oracles intact.

  • Windows: Initialize Ollama readiness menu dependencies

  • Windows: Recover cold metrics after exhausted input
    Establish legitimate same-revision exhaustion before notifying a fresh
    committed batch. Deliver the real idle-warm chain and resume drain before
    requiring renewed first-paint ownership, so deferred recovery is observed.

    Keep the previous three cases unchanged and require actual publication,
    paint and live delivery after the fresh retry. Native validation remains
    unexecuted; process, COM and timer delivery use isolated passive ports.

  • Windows: Expose deferred Ollama child startup failures
    Keep durable owner and main checkpoints when a private child does not
    settle. Deliver its native capture before strict retirement deletes the
    private files, while preserving the original deadline failure.

    Reuse the shared startup observer without changing the seven business
    oracles, owner seeds, or primary deadline.

  • Windows: Preserve personal info editor source consent
    Exercise real native and web save handlers after a newer identity file
    replaces the cached form source. Require byte-preserving refusal for
    unrelated fields and aliases, with unchanged-source save controls.

    Native execution is deferred to the Windows BEFORE replay.

  • Windows: Preserve fresh warm terminal retry budgets
    Admit the last retry through real failed-worker callbacks, then notify
    new committed input before the old projection settles. Require a fresh
    revision budget and actual publication, paint and live-ingest drain.

    Keep same-revision exhaustion bounded and old-request success productive.
    Process, COM and timer delivery remain passive; native before validation
    has not run for these three cases.

  • Windows: Retain Ollama child startup diagnostics

  • Ci: Replay deferred Ollama delivery across pause
    Select seven deferred Ollama delivery cases in both orders.
    The exact normal-index candidate passed FORMAT and 448 of 449 JS checks;
    Check 122 matches the measured dev baseline. Native replay is pending.

  • Windows: Include real tray retry classes in Ollama child

  • Windows: Include deferred Ollama child exception ownership
    The real LLM menu catches TrayRootRetryPendingError at parse time. Its
    isolated delivery child also needs the whole authored tray owner that
    defines that class, before it loads the menu index.

    Resolve that owner through its exported classifier rather than seeding a
    fake class. Preserve all seven delivery scenarios and business oracles.

  • Windows: Reproduce deferred Ollama scripts lost during pause
    Exercise native one-shot delivery across Suspend and the real resume
    reactor after readiness has detached its queue. Preserve FIFO and exact
    submission counts for both queued and already-ready public producers.

    Keep current, early-resume, successor and settled submissions as controls.
    A refused observer after accepted submission must never replay an append.
    The private ports do not qualify native COM or browser DOM execution.

  • Ci: Replay session recovery across pause and reload
    Select eleven warm-recovery and Ollama readiness cases in both orders.

(Changelog truncated — too many commits to display in full on this release page.)



Generated by CI

Windows signature qualification

WINDOWS AUTHENTICODE UNSIGNED / qualified:false (source 17d4a3f): ErgoptiPlus.exe has no Windows code-signing certificate for this v1.0.5 release. All tests, build, package, source and asset integrity, and installation checks remain mandatory. Windows may show an unknown-publisher warning. Signature evidence. Future signing requires SignPath Foundation enrollment and qualified GitHub integration.