Repository navigation
Ergopti v1.0.5
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:
- Download
ErgoptiPlus.exe. - Double-click to run. On first launch, resources are extracted to
%LOCALAPPDATA%\Ergopti. - 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 ergoptiplusergoptiplus 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:
- Unzip
ErgoptiPlus.app.zipand moveErgoptiPlus.appto/Applications. - Remove the quarantine flag (the app is not yet Apple-notarised):
xattr -dr com.apple.quarantine /Applications/ErgoptiPlus.app- Launch the app. On first run, Karabiner-Elements will request a System Extension approval — required for key remapping.
- 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/PKGBUILDin the repository; no Arch package is attached to this release - Flatpak →
ErgoptiPlus-linux-x86_64.flatpak, installed withflatpak install --bundle. It requests--device=all, which is the only permission that reaches/dev/inputand/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.AppImageorergopti-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:
- Open the downloaded package with your distribution's software installer and install it.
- 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.
- Follow the first-use window. If group membership changed, sign out and back in when prompted.
- 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. - engine:backspace() sets the word start when it erases the last
-
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. - the user's Backspace that erases the last character typed since the
-
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 gentherefore 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.