# CHANGELOG

## Unreleased

### Added
- **Secure Input now recovers itself, and can stop interrupting you.** Two settings under *Privacy → Secure Input*, plus the one path that still works during a hold made reachable.
  - **"Auto-restart a browser that gets stuck"** (default on). Chromium browsers leak their `EnableSecureEventInput` assertion and then hold it indefinitely — no prompt exists to dismiss, and only ending the process releases it. Measured on this machine: Claude.app held it **128 minutes** (2026-08-10), Brave **30+ minutes** (2026-08-11) while the user repeatedly pressed Cmd+Tab into a dead tap. The manual "Restart \<app\>…" button existed throughout and went unused, because the overlay carrying it had been dismissed — so recovery now runs independently of the panel. After `SecureInputAutoRecovery.autoRestartDelay` (3 min) WOS shows a 10-second countdown and restarts the holder with `--restore-last-session`, proceeding on its own if nobody is at the machine. Deliberately narrow: **Chromium only** (they restore losslessly; an Electron app or editor can hold unsaved work, so those keep asking first), never a system holder, never a dead holder, and **once per episode** — the assertion can survive the restart, and repeating it would quit the freshly restored browser again. Policy is pure and unit-tested.
  - **"Show the on-screen warning"** (default on). Turning it off stops the corner panel appearing without disabling detection: the menubar item still shows the lock, names the holder, and carries the same remedy. Worth stating plainly, since the request behind it was "can we disable the Secure Input feature?" — the overlay does not cause the blocking, the kernel does, and switching detection off would only restore the silent-mystery behaviour the overlay was built to end.
  - **The menubar now offers "Open Switcher — your keyboard still works here" while a hold is active.** Secure Input blocks *interception* — event taps and global hotkeys, i.e. keys aimed at another app — but not key delivery to the focused foreground app. Quick Search is a `canBecomeKey` panel, so opening it from the menu gives back typing, arrows and Enter: one mouse click replaces the dead Cmd+Tab and the rest of the switch stays on the keyboard. It was always reachable via "Search Targets…", but nothing said so at the moment it mattered.
  - The menubar's recovery hint no longer says *"Dismiss the password prompt or finish typing — Cmd+Tab resumes automatically"* unconditionally. That is false for a leaked hold, which has no prompt and never resumes on its own; it now uses the same `SecureInputRemedy.stillHeldAdvice` the overlay and the diagnostics file use, so the three cannot drift.
  - Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift) (`SecureInputAutoRecovery`), [`GeneralSettingsStore.swift`](Sources/WOS/GeneralSettingsStore.swift), [`StatusItemController.swift`](Sources/WOS/StatusItemController.swift), [`SettingsPanelController.swift`](Sources/WOS/SettingsPanelController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`SecureInputAutoRecoveryTests.swift`](Tests/SwitchCoreTests/SecureInputAutoRecoveryTests.swift).

### Fixed
- **A Secure Input hold whose holder quits no longer strands you.** When Telegram asserted Secure Input, leaked the assertion and quit (2026-08-10), the overlay went on offering **"Show Telegram"** for 61 s — the holder-liveness check only ran on the 1/min heartbeat — and the user spent all three of their dismissals waving away advice for a process that no longer existed. `SecureInputAttentionPolicy` then gave up for the rest of the episode, so Cmd+Tab stayed dead with nothing on screen: "I quit Telegram and WOS still does not come back." Three changes: holder liveness is now probed every tick (`kill(pid, 0)`, so the remedy corrects in ~1 s instead of ~61 s); mid-episode holder changes are pushed straight to the overlay instead of waiting for the next heartbeat; and the dismissal budget is now scoped to **what was offered** rather than to the episode — when the remedy changes (`focus:<app>` → `restart:<app>` → `lock` → `logout`), the overlay comes back immediately with the advice that can actually work, once per distinct offer so a flapping holder can't nag. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift) (`SecureInputRemedy.remedyKey`, `SecureInputRemedyReset`), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`SecureInputRemedyResetTests.swift`](Tests/SwitchCoreTests/SecureInputRemedyResetTests.swift).

- **Quick Search results no longer wait for tab enumeration before appearing.** Typing `show video source` into a freshly-opened panel showed the "Searching 78 tabs… / Auto-continues in 2s" card and nothing else — even though the answer needs no tabs at all. The rows were in fact already computed on that keystroke (`applyFilter` runs per keystroke and had built them); the results table simply sat at `alphaValue = 0` behind the central loading card, which stayed up for the whole of `isInitialBootstrap`. The panel was hiding an answer it already had. The card now yields the moment a typed query produces rows, and returns if the query stops matching; enumeration keeps running behind the lighter footer indicator and merges its tabs in when they land. Three guards keep everything else intact: an empty field still gets the full loading experience (bootstrap entries would otherwise dismiss the card the instant the panel opened), developer preview mode still holds the card open for inspection, and `setLoading` is re-run only on a visibility *transition* so the tip rotators don't restart on every keystroke. This also un-hides the bootstrap apps and LRS targets, so typing `sla` for Slack no longer waits on 78 tabs it never needed either. File: [`QuickSearchPanelController.swift`](Sources/WOS/QuickSearchPanelController.swift).

### Added
- **Cmd+Tab+S now takes commands, not just names — starting with "show audio source".** Quick Search only ever indexed *things* (apps, tabs, windows, files), so typing a verb returned an empty list: "show audio source" found nothing, because nothing was named that. A new Commands section sits above the results with a catalogue of actions ([`WOSCommand.swift`](Sources/SwitchCore/WOSCommand.swift)) — `show audio source`, `show video source`, `show media source`, `silence all audio`, plus the panels that already existed behind chords (clipboard history, recent files, bookmarks, memory bank, menu-bar apps, settings, shortcuts). The media commands *jump*: one detected source is activated immediately with no picker, several open the existing audio HUD in jump-primary mode (Enter goes to the highlighted row instead of silencing everything), none shows a toast. Commands are held to a stricter matcher than ordinary entries and stay hidden below three characters, so short queries can never hijack Enter from the app you were switching to. Files: [`WOSCommand.swift`](Sources/SwitchCore/WOSCommand.swift), [`QuickSearchPanelController.swift`](Sources/WOS/QuickSearchPanelController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`WOSCommandTests.swift`](Tests/SwitchCoreTests/WOSCommandTests.swift).
- **"Where's that sound?" now answers "where's that video?" too.** The audio-silence detector became kind-aware ([`MediaKind.swift`](Sources/SwitchCore/MediaKind.swift)). For video the per-tab JS predicate looks for a `<video>` that is actually rendering frames — `readyState > 2` and a non-zero `videoWidth`, and *muted does not disqualify it*, because a silently autoplaying video is exactly what you are hunting for — while the process-level signal switches from CoreAudio output to IOKit display-sleep assertions ([`ProcessVideoProbe.swift`](Sources/SwitchCore/ProcessVideoProbe.swift)): holding one is what every video player does so the screen doesn't dim mid-film, it is the same data `pmset -g assertions` prints, and it is read through a public C API, so it works in the sandboxed App Store build with no shell-out. Both passes reuse `AudioSourceMerger`, so a browser's renderer-helper PIDs still fold into one row and that row still disappears when the exact tab is found. A source detected by both passes appears once, labelled `AUDIO + VIDEO`. Files: [`MediaKind.swift`](Sources/SwitchCore/MediaKind.swift), [`ProcessVideoProbe.swift`](Sources/SwitchCore/ProcessVideoProbe.swift), [`BrowserScriptBridge.swift`](Sources/SwitchCore/BrowserScriptBridge.swift), [`AudioSilenceService.swift`](Sources/WOS/AudioSilenceService.swift), [`AudioSourcesHUDController.swift`](Sources/WOS/AudioSourcesHUDController.swift).
- **Free text in Cmd+Tab+S: type what you want in your own words.** A trailing "Ask WOS" row (or ⌘↩ from anywhere in the panel) accepts any sentence and resolves it on this Mac, in two tiers that degrade instead of failing ([`LocalIntelligenceService.swift`](Sources/WOS/LocalIntelligenceService.swift)). Tier 1 is a rule parser ([`NLCommandParser.swift`](Sources/SwitchCore/NLCommandParser.swift)): pure Swift, no model, no network, nothing to install, and it already handles "where is that sound coming from", "which tab is playing video", "mute everything", "switch to spotify" — so the feature works out of the box on every supported Mac. Tier 2 is **Apple's on-device foundation model** (macOS 26 + Apple Intelligence), which ships with the OS rather than being something the user downloads, and is used only for text tier 1 could not parse; it is asked to *classify* against the command catalogue, not to generate, and any answer that isn't a known identifier is rejected rather than executed. Whatever the rules understood is previewed under the row as you type, and a resolution that names a thing rather than an action ("switch to figma") is handed back to Quick Search's own index, which matches app and tab names better than any model. No prompt leaves the machine, and `FoundationModels` is weak-linked so WOS still launches on macOS 13–25, where the feature simply falls back to the rule tier. Tests in [`NLCommandParserTests.swift`](Tests/SwitchCoreTests/NLCommandParserTests.swift).
  - Deliberately **no shell-out tier**: an earlier draft called the Homebrew `apfel` CLI as a third tier, but `apfel` links the same `FoundationModels.framework` tier 2 calls in-process, so it requires macOS 26 + Apple Intelligence too and can never answer where tier 2 could not — while costing a process spawn, a PATH lookup and a hard incompatibility with the sandboxed App Store build. The existing `ApfelChatPanelController` chat feature is unaffected.

## 0.1.1029 (2026-08-05)

### Added
- **The "Cmd+Tab is blocked" overlay now comes back the moment you press a shortcut into it.** WOS's event tap is bypassed while Secure Input is held, so it cannot see Cmd+Tab — but the session-level event counters keep running (they must: idle detection has to work while a password is typed). While a hold persists, WOS samples `CGEventSource.counterForEventType`/`flagsState` at 5 Hz and, when a key goes down with Command/Control/Option held, force-shows the overlay out of schedule — the exact instant the user presses Cmd+Tab and gets nothing. Plain typing never triggers it (the commonest legitimate cause of Secure Input *is* the user typing a password), and Shift alone doesn't count. Throttled to one interruption per 15 s. Closes the gap where a dismissed warning stayed silent for 15 minutes while Cmd+Tab was dead — observed 2026-08-05: dismissed 11:15:07, next scheduled reminder 11:30:07, and in between the user concluded WOS had crashed. Policy is pure (`SecureInputAttentionPolicy`) and unit-tested. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift).
- **Wake and unlock re-surface a held Secure-Input warning.** The classic report is "the notebook slept overnight, I unlocked it, started working, and Cmd+Tab was dead". WOS now observes `NSWorkspace.didWakeNotification` and loginwindow's `com.apple.screenIsUnlocked`, and force-shows the overlay if the hold is still active — the user meets the state fresh at unlock, whatever the re-show schedule had planned before the machine slept.

### Fixed
- **After WOS restarts a stuck holder, it re-checks within seconds instead of waiting a minute.** The mid-episode re-identification ran only on the 1/min heartbeat, so when the flag survived the holder's exit (2026-08-05: Chrome quit, `kCGSSessionSecureInputPID` kept naming the dead pid 16734, Secure Input stayed asserted) the overlay went on offering "Restart Google Chrome…" for a process that no longer existed — for 54 seconds, after which the heartbeat corrected it to the lock remedy that cleared the hold in 4 s. A restart WOS performed now schedules re-checks at 3/8/15 s, so the remedy switches as soon as the old process is actually gone. Files: [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift).

## 0.1.1028 (2026-08-04)

### Fixed
- **`scripts/build_release.sh` no longer SIGKILLs unrelated processes that merely mention "WOS".** Its survivor sweep was `pgrep -f "WOS" | xargs kill -9` — a match against the whole command line, so it killed an editor with a WOS file open, a `log stream` filtered on our subsystem, or a shell whose arguments contained the word. It killed the shell running the 0.1.1028 release build itself (the output filter's pattern contained "WOS"), aborting the build mid-flight and — because the shell died with it — skipping the very `EXIT` trap meant to restore the app. The sweep now matches the app binary path (`/WOS.app/Contents/MacOS/WOS`), which still catches the `--watchdog` sibling and nothing else. File: [`build_release.sh`](scripts/build_release.sh).
- **`scripts/build_release.sh` no longer leaves the machine with no WOS running.** The script kills every running instance before building — including the crash-relaunch watchdog, which its `kill -9` sweep also takes out — and, unlike `dev_loop.sh`, never restarted anything, because it exists to produce a DMG rather than to install one. So every release build silently took the user's WOS down until they noticed Cmd+Tab doing nothing (2026-08-04: ~21 hours). It now records the bundle path of the running instance (skipping the `--watchdog` sibling, relaunching which would not give the app back) and restores it from an `EXIT` trap, so the app returns even when the build fails or is interrupted, and warns instead of failing silently if the relaunch doesn't work. Opt out with `RELAUNCH_WOS_AFTER_BUILD=false` for headless build machines. File: [`build_release.sh`](scripts/build_release.sh).

## 0.1.1027 (2026-08-03)

### Added
- **The Secure-Input holder is now re-identified mid-episode when it stops making sense.** Identification ran once, on the OFF→ON edge, so a long episode kept naming whoever was blamed at the start — verified wrong twice on 2026-08-03: after Brave was quit, `kCGSSessionSecureInputPID` kept naming the dead pid 3808 while Secure Input stayed asserted (the assertion outlived its owner — a session-level leak), and the overlay went on offering "Show Brave Browser" for an app that no longer existed; only a whole new episode or a WOS relaunch corrected the label. The observer now re-checks at heartbeat cadence (one IORegistry read + a `kill(pid, 0)` liveness probe per minute) and re-identifies when the recorded holder has exited or the holder pid changed, preserving `activatedAt` so episode duration keeps measuring from the original assertion. The corrected holder is published immediately, so the overlay's copy and button follow it — a dead app holder becomes a system holder and the remedy switches from "Restart \<app\>…" to the lock → logout ladder. Guarded against re-probing forever (a dead holder re-identifies to the same dead pid) and silent when nothing actually changed. Decision is pure (`SecureInputCulpritRefresh`) and unit-tested. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift); tests in [`SecureInputTabJumpTests.swift`](Tests/SwitchCoreTests/SecureInputTabJumpTests.swift).
- **The Secure-Input overlay can now restart a stuck app holder — and stops claiming there is a prompt to dismiss when there isn't.** A browser can *leak* its `EnableSecureEventInput` assertion: on 2026-08-03 Brave held Secure Input for 20+ minutes with a single window, no password field anywhere, surviving repeated front/background cycles — Cmd+Tab dead system-wide, and the overlay's only offer was "Show Brave Browser", clicked 15 times in 13 seconds because nothing else was there. App holders now get the escalation ladder system holders already had: **"Show \<app\>" / "Show the Tab" → "Restart \<app\>…"**, tripped either 25 s after a failed front attempt or 2 minutes into the episode (`SecureInputRemedy.shouldOfferRestart`, unit-tested), re-evaluated on the grace boundary rather than only on the 1/min heartbeat. The restart confirms first (unsubmitted form input is at stake), quits gracefully, waits up to 12 s for the process to exit — never force-kills, and says so when a "Leave site?" dialog blocks the quit — then relaunches, passing `--restore-last-session` to Chromium browsers so every tab comes back regardless of the user's "On startup" preference. Copy fixed in both places it was wrong: the overlay no longer says "dismiss the password prompt or finish entering your password" for a leaked hold, and the diagnostics file no longer prescribes "lock/unlock the screen" for an *app* holder — lock/unlock cannot touch an app's own assertion, only ending the process does (`SecureInputRemedy.stillHeldAdvice`, shared by both surfaces so they can't drift). Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`SecureInputTabJumpTests.swift`](Tests/SwitchCoreTests/SecureInputTabJumpTests.swift).
- **The Secure-Input overlay now jumps to the browser *tab* holding the keyboard, not just the browser.** "Show Google Chrome" fronted the app and left the user hunting: the password field is one tab among hundreds, routinely in a background window of a *different profile* (12 Chrome windows is a normal session here), and `NSRunningApplication.activate` raises whichever window AppKit considers key. WOS now captures the holder's front tab at the OFF→ON edge — a focused password field lives in the front window's active tab, so that read *is* the culprit — and the button becomes **"Show the Tab"**, selecting the exact tab (window raise → tab select, same order as Tab Therapy) so the user can close it. The overlay names the tab in its subtitle and the hint becomes "close it (Cmd+W)". The capture is budgeted (0.35 s, `fetchFrontTabWithinBudget`) because it runs on the observer's main-thread poll — a contended AppleScript engine yields no tab and the button silently degrades to the old "Show \<app\>". Only real browsers are probed (`BrowserKind.isWebBrowser` — a terminal holding Secure Input via Secure Keyboard Entry has no tab to close), and Incognito tab titles are redacted from the overlay and from `~/Library/Logs/WOS/WOS.log` while the jump still works. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`SecureInputTabJumpTests.swift`](Tests/SwitchCoreTests/SecureInputTabJumpTests.swift).
- **The Secure-Input overlay now offers a one-click remedy.** New contextual action button next to an explicit Dismiss: for stuck system holders (loginwindow, SecurityAgent, screensaver/Touch-ID brokers — including `com.apple.LocalAuthentication.UIAgent`, which the old system-holder matcher missed) it shows **"Lock Screen to Fix"** and locks the screen via `SACLockScreenImmediate` (login.framework, resolved dynamically, `#if !WOS_MAS`; degrades to a logged no-op naming the Control-Command-Q chord); for regular app holders it shows **"Show \<app\>"** and fronts the holder via `NSRunningApplication.activate` (unaffected by Secure Input) so the user can finish or dismiss the secure prompt. `SecureInputCulprit` now carries the holder `pid` for exact activation; remedy selection is pure (`SecureInputRemedy` in SwitchCore) and unit-tested. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift).

### Fixed
- **The floating status toast can now be clicked away.** The toast that surfaces persistent diagnostics next to the Cmd+Tab HUD / Quick Search panel ("⚠️ Multiple Google Chrome instances running…", missing Automation permission, …) accepted clicks but did nothing with them — its click handler was armed only for opt-in toasts (audio panic), so the warning could neither be moved nor dismissed and could linger on screen indefinitely. Now a click anywhere on the toast body hides it, and the exact dismissed message is suppressed for 30 minutes (`StatusToastDismissPolicy` in SwitchCore) so the persistent condition re-emitting the same note on the next HUD open doesn't undo the dismissal seconds later. Error toasts hide on click too but are never suppressed. The Details/Ignore buttons and the audio-toast click action are unchanged. Files: [`StatusToastDismissPolicy.swift`](Sources/SwitchCore/StatusToastDismissPolicy.swift), [`StatusHUDController.swift`](Sources/WOS/StatusHUDController.swift); tests in [`StatusToastDismissPolicyTests.swift`](Tests/SwitchCoreTests/StatusToastDismissPolicyTests.swift).
- **A dismissed Secure-Input warning now comes back while the hold persists, and Secure-Input episodes are recorded in a persistent logfile.** The "Cmd+Tab is blocked" overlay could be waved away with one click for the *entire* episode — for a stuck system hold (loginwindow leaked Secure Input for two days, 2026-07-19) that meant a single reflexive click bought days of unexplained dead Cmd+Tab, and since WOS logged only to unified logging (unretrievable historically on many machines), there was nothing on disk to diagnose with afterwards. Now: a dismissed overlay re-shows 2 minutes after the first dismissal and every 15 minutes thereafter for as long as Secure Input stays held (`SecureInputReshowPolicy`, driven by a 1/min observer heartbeat), and a new append-only diagnostics file at `~/Library/Logs/WOS/WOS.log` (2 MB rotation, low-frequency events only) records app launches plus every Secure-Input episode: activation with holder/confidence/evidence, a "still held" line every 10 minutes, overlay shown/dismissed/re-shown, and release with duration. Files: [`DiagnosticsFileLog.swift`](Sources/SwitchCore/DiagnosticsFileLog.swift), [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`DiagnosticsFileLogTests.swift`](Tests/SwitchCoreTests/DiagnosticsFileLogTests.swift).
- **Link-click browser activations no longer promote the browser's previous tab above the app you came from.** `freshenBrowserRecencyOnActivation` optimistically bumps the browser's last-known front tab at activation, betting the user is returning to it. When Chrome is activated *by a link click that opened a brand-new tab* (e.g. from the Gmail PWA), that bet is wrong — yet the wrongly-bumped old tab kept its fresh stamp and sat at Cmd+Tab position 2, displacing the app the user switched from ("Villa Brissago above Gmail"). The bump is now recorded as a `PendingArrivalBump` and settled by its paired deliberate capture: front tab matches the bumped entry (or its window) → bump stands; different front tab within 5 s → `LRSHistory.revertRecencyBump` restores the pre-bump timestamp (guarded so a legitimate write that landed in between is never demoted). Dropped/late captures leave the bump in place — preserving arrival order during rapid switching is what the bump exists for. Files: [`FrontTabFeed.swift`](Sources/SwitchCore/FrontTabFeed.swift), [`LRSHistory.swift`](Sources/SwitchCore/LRSHistory.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift).
- **Email-digest link clicks no longer leave a phantom duplicate tab row in Cmd+Tab.** Clicking a tracking-wrapped link (e.g. a Quora digest opened from the Gmail PWA) makes Chrome open a *lander tab* — `google.com/url` → tracker → landing page — that `window.open`s the real destination tab and closes itself within ~0.5 s. Chrome mints a fresh tab GUID for each, so WOS's deliberate front-tab captures raced the hand-off and recorded both: the dead lander then sat at Cmd+Tab position 2 with the same title as the real tab (one press landed on a dead tab instead of the app you came from) until the next enumeration + prune (~30 s) removed it. The reconciler couldn't heal it — the real tab's entry consumes the live GUID via its Tier-1 exact match, and the lander's URL differs from the live tab's, so `tabMatchKeys` (URL-only when a URL is present) never linked them. New `TransientTabDetector` merges the pair at capture time instead: when a deliberate capture lands, a *provisional* (capture-inserted, never enumeration-confirmed) same-window predecessor tracked within the last 6 s whose normalized title or URL host matches is retired into the incoming tab via `renameStableID`, so the phantom never surfaces. Enumeration snapshots confirm provisional GUIDs as real (never merged) and expire registrations after 60 s. Diagnosed via Chrome History forensics (`visits.opener_visit` + 0.5 s `visit_duration` on the lander visit). Files: [`FrontTabFeed.swift`](Sources/SwitchCore/FrontTabFeed.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); tests in [`FrontTabFeedTests.swift`](Tests/SwitchCoreTests/FrontTabFeedTests.swift).

### Added
- **Browser Extension (Turbo) automation engine is now real — a WOS Chrome extension replaces the never-shipped "patched browsers" backend.** AppleScript stays the default; power users opt in via Settings → Performance. Three new pieces: an MV3 extension ([`Sources/WOS/Resources/ChromeExtension/`](Sources/WOS/Resources/ChromeExtension/README.md) — `chrome.tabs`/`chrome.windows` only, no `chrome.debugger`, so no "started debugging this browser" banner) speaking the existing `WOS.ListTabs`/`WOS.ActivateTab` JSON-RPC vocabulary; [`WOSNativeHost`](Sources/WOSNativeHost/main.swift), a native-messaging relay the browser launches, which serves the per-browser `NativeIPC.sock` that [`NativeIPCBridge`](Sources/SwitchCore/NativeIPCBridge.swift) already polls; and a shared [`WOSBridgeKit`](Sources/WOSBridgeKit/WOSBridge.swift) target owning the contract (framing codec, socket paths, pinned extension ID). New Settings card "Browser Extension Bridge" (Performance section) does one-click setup: writes native-messaging host manifests for every installed Chromium browser (Chrome, Chrome Beta/Canary, Brave, Arc) and reveals the unpacked extension at `~/Library/Application Support/WOS/ChromeExtension` for chrome://extensions → Load unpacked. Falls back to AppleScript automatically whenever the bridge is down. Direct-distribution only (`#if !WOS_MAS`). Files: [`ChromeExtensionInstaller.swift`](Sources/WOS/ChromeExtensionInstaller.swift), [`SettingsPanelController.swift`](Sources/WOS/SettingsPanelController.swift), [`build_release.sh`](scripts/build_release.sh), [`dev_loop.sh`](scripts/dev_loop.sh); tests in [`WOSBridgeKitTests.swift`](Tests/SwitchCoreTests/WOSBridgeKitTests.swift).

### Changed
- Automation Engine option renamed: "Native IPC (Turbo Mode - Patched Browsers Only)" → "Browser Extension (Turbo)" (same stored raw value — existing settings keep working).
- When the Turbo engine is selected but the bridge socket is absent (extension off, browser closed), tab scans and activations now skip the bridge instantly instead of stalling on the 800 ms / 450 ms timeouts before falling back to AppleScript.
- Successful bridge activations now also bring the browser app frontmost (the extension can only focus the window *within* the browser; AppleScript's `activate` used to do the app-level raise).
- Chrome Canary/Beta get distinct bridge socket paths (previously all three Chrome variants collided on `Google/Chrome/NativeIPC.sock`).

## 0.1.1021 (2026-07-03)

Two independent bugs with the same face — "my most-used Safari tab keeps vanishing from Cmd+Tab" — found and fixed one day apart. Plus a new audio-silence feature.

### Fixed
- **Subprocess AppleScript output was never decoded — the first tab of every enumeration was silently dropped.** Both `osascript` subprocess runners launch with `-s s` (source-form output), which wraps string results in quotes and backslash-escapes `"`/`\`. Nobody decoded it (since 2026-03): the leading quote glued onto the first data row, its index failed `Int()` parsing, and **tab 1 of window 1 vanished from every scan, for every browser** — the history pruner then kept evicting that tab's Cmd+Tab entry as "closed". Masked for months by the pre-0.1.1014 sampler firehose; the (correct) anti-firehose change exposed it. Output is now decoded centrally in both runners (`AppleScriptSubprocess.decodeSourceForm`), so subprocess and in-process paths return identical text — this also stops literal `\"` escapes leaking into stored tab titles and match keys. `parseTabRows` now warning-logs any malformed row it drops, so a parse regression can never be silent again. Files: [`AppleScriptSubprocess.swift`](Sources/SwitchCore/AppleScriptSubprocess.swift), [`BrowserScriptBridge.swift`](Sources/SwitchCore/BrowserScriptBridge.swift); see LESSONSLEARNED → "osascript `-s s` Quote-Wrapped Every String Result".
- **Post-commit verification pruned the tab the user had just switched to.** Verification compared the *stored* title/URL against the live front tab — for an SPA that rewrites both between visits (per-route titles, badge counters, overnight session-expiry → login redirect), every *successful* Cmd+Tab activation evaluated as `.differs`. The recovery chain's content search ran against the dead metadata, the `activateWindow` fallback was frequently aborted by an `activationToken` bump (the same SPA's title churn fires the manual-switch classifier every few seconds), and the `.differs + failed fallback` path **removed the just-activated entry from history**. Static-title tabs never triggered it — which is why exactly one heavily-used web app kept dying. Two fixes: (1) `BrowserTabMatchEvaluator` gained an **identity tier** ahead of metadata — index-based browsers (Safari/Firefox/terminals) match on window + tab index (precisely what the activation targeted), Chromium on the full composite tab-GUID identifier; (2) the eviction arm now refuses to prune a stableID still present in the live open-tab cache (a token-aborted fallback is "couldn't act", not "target gone"). Files: [`BrowserTabMatch.swift`](Sources/SwitchCore/BrowserTabMatch.swift), [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift); see LESSONSLEARNED → "Post-Commit Verification Compared Dead Metadata".

### Added
- **Silence noisy tabs without leaving your keyboard (Cmd+Ctrl+M).** Tap the chord for the new audio-sources HUD — every tab/app currently playing audio across all running browsers, resolved via CoreAudio process detection merged with browser tab matching; silence one, jump to it, or silence all. Hold the chord for panic silence-all. Toggleable in Settings. Files: [`AudioSilenceService.swift`](Sources/WOS/AudioSilenceService.swift), [`AudioSourcesHUDController.swift`](Sources/WOS/AudioSourcesHUDController.swift), [`AudioSource.swift`](Sources/SwitchCore/AudioSource.swift), [`AudioSourceMerger.swift`](Sources/SwitchCore/AudioSourceMerger.swift), [`ProcessAudioProbe.swift`](Sources/SwitchCore/ProcessAudioProbe.swift).

### Changed
- Clipboard-history and Memory-Bank stores moved from the app target into `SwitchCore` ([`ClipboardHistory.swift`](Sources/SwitchCore/ClipboardHistory.swift), [`MemoryBank.swift`](Sources/SwitchCore/MemoryBank.swift)) with dedicated unit-test suites — behaviour-preserving, testability refactor.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1021, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1016 (2026-06-22)

Follow-up to 0.1.1015 — review-driven correctness fixes to the changes that shipped in it.

### Fixed
- **Browser-window QA regression gate restored.** A refactor in 0.1.1015 reduced the gate's pass condition to a tautology — `seeded && uriPass && (initialPass || uriPass)` collapses to `seeded && uriPass`, so `initialPass` became a dead no-op and the *initial-window activation* leg was silently dropped from the release self-test. A real regression in initial activation could have passed CI undetected. Restored to require all three legs (`seeded && initialPass && uriPass`), and rewrote the unit test so the `initialPass:false` row now expects failure and actually catches it. **QA-harness only — no end-user impact.**
- **Secure Input terminal fallback no longer over-claims `confirmed`.** When a terminal (Terminal/iTerm2) is frontmost and the *Secure Keyboard Entry* probe is unavailable or negative, WOS labelled it `confidence=confirmed`. Since the probe returns nil for both "engine busy" and "SKE genuinely off", that could confidently mislabel a terminal as the holder when another process held Secure Input. Downgraded to `likely` — the authoritative IOKit Tier-0 holder check (added in 0.1.1015) already had its chance to confirm the real holder.

### Changed
- QA activation AppleScript embeds the app name via the escaped `AppleScriptLiteral.quoted()` helper (house style) instead of raw string interpolation, and hoists loop-invariants. No behavioural change — defensive hardening.

## 0.1.1015 (2026-06-22)

### Fixed
- **Secure Input holder is now identified authoritatively, not guessed.** Previously WOS labelled the culprit from `NSWorkspace.frontmostApplication` at the moment the flag flipped — so it could misattribute the hold to whatever app happened to be in front (e.g. *"likely held by Claude"* when macOS's `loginwindow` actually held a stale Secure-Input assertion). WOS now reads the real holder PID from IOKit `kCGSSessionSecureInputPID` (`IOConsoleUsers` on `IOResources`) as a Tier-0 check, so the overlay names the true holder with `confidence=confirmed`. This also catches **system / authentication holders that are never frontmost** — `loginwindow`, `SecurityAgent`, the screensaver auth prompt — and shows the correct remediation for them: *"held by loginwindow (a system process) — lock & unlock the screen to clear it."* The IOKit read needs no entitlement and is sandbox-safe (MAS-ready). Files: [`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift), [`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift).
- **The "Cmd+Tab is blocked" overlay could get stuck on screen after Secure Input cleared.** Dismissal was edge-triggered off a main-thread-mutated latch, and the 1 Hz poll timer ran in `.default` run-loop mode only — so a clear-edge that landed during menu tracking, a modal panel, or main-thread AppleScript work could be missed and orphan the panel until the next real transition. Dismissal is now **level-triggered** (re-asserted every tick while the flag is off) and the timer runs in `.common` modes.

### Changed
- Secure Input culprit detection short-circuits on the IOKit holder before the AppleScript Terminal *Secure Keyboard Entry* probe, cutting OSA calls (and TASLexer-crash exposure) on the 1 Hz path.
- **QA regression logic extracted from `AppDelegate` into pure, unit-tested `SwitchCore` policy types** — `AppSwitchRegressionPolicy`, `FrontTabSamplingDrainPolicy`, `RegressionActivationRetryPolicy`, `RegressionFixtureFilter` (with new test suites). Behaviour-preserving; hardens browser-window / app-switch activation retries and `WOS_*` regression-fixture purging. Settings-UI margin QA now skips degenerate off-screen group frames. Terminal SKE probe gained a frontmost-terminal fallback.

## 0.1.1014 (2026-06-20)

### Added
- **`scripts/release_qa_suite.sh`** — single post-build gate for unit tests, perf smoke, eight in-app LRS regressions, and Settings UI layout QA. Profiles: default, `--quick`, `--full`. `build_release.sh` runs it when `RUN_RELEASE_QA_SUITE=true`.
- **`FrontTabFeed.swift` (SwitchCore)** — pure, unit-tested helpers for front-tab coalescing, promote/passive write mode, stale-capture guard, browser arrival bumps (`BrowserFrontTabHint`), and index-tab drift detection. Keeps `AppDelegate` orchestration thin and testable.
- **`ChromiumTabContentMatch.swift`** — strict-then-loose URL/title scan scripts for Chromium content activation (same-URL Gmail disambiguation).
- **Live ordering smokes** — `scripts/qa_safari_chrome_lrs_ordering.sh` and `scripts/qa_new_window_focus_theft.md` document cross-browser LRS ordering and new-window focus-theft QA.

### Fixed
- **Front-tab sampler no longer promotes glanced tabs.** Passive ~300 ms samples use `refreshExistingOnly`; deliberate navigation still uses `markSeen`. See [ARCHITECTURE.md → Deliberate vs passive promotion](ARCHITECTURE.md).
- **Chromium same-URL tabs activate the correct account** via strict URL+title matching before loose URL-or-title fallback.
- **Index-based tab drag no longer duplicates HUD rows** — eager `renameStableID` on deliberate front-tab capture when content keys match a stale index slot (`IndexTabDriftDetector`).
- **QA harness hardening** — browser window regressions seed LRS before first activation; iTerm targets tab index 3 explicitly; Terminal app-switch regression retries activation and purges `WOS_*` fixture pollution from prior suite steps; Settings QA skips hidden provider groups and no longer fails on screenshot capture errors; `perf_smoke.sh` parses `Cycle HUD shown` / `ms=` log lines.
- **AI Rewrite preview** — provider visibility sync when selecting the AI Rewrite QA section in Settings.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1014, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1009 (2026-06-16)

### Added
- **WOS now relaunches itself within seconds after a crash — Cmd+Tab can no longer silently die for hours.** The 2026-06-12 segfault (build 0.1.1008, 07:59) killed WOS and with it Cmd+Tab until manually relaunched. At startup WOS now arms a tiny supervisor: it re-execs its own binary as `WOS --watchdog <pid> <bundle>` (no extra helper, no packaging change), which sleeps in a kqueue `NOTE_EXIT` wait until the app exits. A user-initiated Quit — menu, AppleEvent, and now also `kill`/Ctrl+C, which previously died without saving tab history — writes a `clean` marker down a close-on-exec pipe and the watchdog stands down; a crash writes nothing, and the watchdog relaunches the bundle via `open -g` (no focus theft) after an exponential backoff (1 s for the first crash, doubling to 16 s), giving up after 5 relaunches inside 10 minutes so a crash-looping build doesn't thrash forever — intermittent crashes keep being rescued as the window slides. The "Launch at login" setting is untouched: the watchdog never touches launchd or SMAppService — a deliberate choice over a `KeepAlive` LaunchAgent, which only supervises launchd-spawned instances, fights the login-item toggle via implied `RunAtLoad`, and leaves orphaned plists after uninstall. Excluded from the MAS build; `WOS_DISABLE_WATCHDOG=1` disarms it for scripts; dev loops need no changes because `pkill -x WOS` kills the same-named watchdog alongside the app. Forensics: `log stream --predicate 'subsystem == "com.agentic.wos" AND category == "watchdog"'`; crash timestamps persist in `~/Library/Application Support/WOS/crash_relaunch_history.txt`. See [CrashRelaunchWatchdog.swift](Sources/WOS/CrashRelaunchWatchdog.swift), [CrashRelaunchPolicy.swift](Sources/SwitchCore/CrashRelaunchPolicy.swift), [ARCHITECTURE.md → §9.2](ARCHITECTURE.md), and [LESSONSLEARNED.md](LESSONSLEARNED.md) → "In-Process NSAppleScript Outside the Engine Queue Crashes the App".

### Fixed
- **Fixed the crash that silently killed WOS (and with it Cmd+Tab) — all AppleScript now runs in exactly one safe context.** Build 0.1.1008 segfaulted in Apple's AppleScript compiler (`EXC_BAD_ACCESS` in `TASLexer::UseEvent`, crash report 2026-06-12 07:59) because several call sites still created `NSAppleScript` directly on the main thread or on global-pool threads — racing the serialized engine queue's compile against AppleScript's process-global parser state. This is the same crash family that motivated the single-queue engine in 0.1.839; the rule existed but wasn't enforced. All seven bypassers are fixed: permission-flow and System Settings scripts now run in isolated `osascript` subprocesses (new `AppleScriptSubprocess`, [AppleScriptSubprocess.swift](Sources/SwitchCore/AppleScriptSubprocess.swift)) that share no state with the app and can't wedge the switcher behind a TCC prompt; the Secure-Input probe runs on the engine with a 250 ms budget; the engine warms up OSA deterministically at launch; and a new lint test fails the build if `NSAppleScript` ever appears outside the engine again. See [LESSONSLEARNED.md](LESSONSLEARNED.md) → "In-Process NSAppleScript Outside the Engine Queue Crashes the App".
- **A delayed Cmd+Tab tab-switch can no longer "land" seconds late and pull you off the tab you're using.** All AppleScript work shares one serialized queue, so under contention a commit's `select tab` script could start seconds after the keypress — by which time you may have opened a new tab and started typing; the late script then yanked you back (the second focus-theft vector, distinct from the verification fallback fixed in 0.1.1008). The script is now re-validated at the moment it would run: it is refused if you manually changed tab or app since the commit, or if the commit is older than 2.5 s — and every refusal is logged. Post-commit verification freshness is likewise now measured from the commit itself, not from when the check happened to be scheduled. See [BrowserTabMatch.swift](Sources/SwitchCore/BrowserTabMatch.swift) (`StaleCommitActivationGuard`), [AppDelegate.swift](Sources/WOS/AppDelegate.swift), and [LESSONSLEARNED.md](LESSONSLEARNED.md) → "A Committed select-tab Executing Late Is Focus Theft".

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1009, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1008 (2026-06-10)

### Added
- **AI Rewrite — rewrite any selected text in place.** Select text in any app and press **⌃⌘R** (configurable in Settings → Shortcuts) to rewrite it with AI. Three providers: **Apfel (Apple Intelligence)** on-device via the local `apfel` CLI — the **default**, nothing leaves your Mac; any **OpenAI-compatible** `/chat/completions` endpoint (OpenAI, OpenRouter, Groq, or local Ollama / LM Studio via a configurable base URL); and **Anthropic (Claude)**'s native API. A floating preview panel shows the rewrite **before** it touches your document — **Replace**, **Copy**, or **Discard** — and the original is never modified until you choose. Provider, model, and the rewrite instruction are set in Settings → AI Rewrite; cloud API keys are stored in the macOS **Keychain** (never in UserDefaults). See [RewriteService.swift](Sources/WOS/RewriteService.swift), [RewritePreviewPanelController.swift](Sources/WOS/RewritePreviewPanelController.swift), [RewriteSettingsStore.swift](Sources/WOS/RewriteSettingsStore.swift), [RewriteKeychain.swift](Sources/WOS/RewriteKeychain.swift).
  - **Privacy:** with the default Apfel provider the text is processed entirely on-device. If you choose a cloud provider, the selected text is sent to that provider's API under your own key.
- **Memory Bank entries can be renamed (⌘E).** In the ⇧⌘M search HUD, press **⌘E** to rename the selected entry inline — the field becomes a title editor (**⏎** commits, **esc** cancels). The custom title overrides the derived first-line title in the list; clearing it reverts to the first line. See [MemoryBankStore.swift](Sources/WOS/MemoryBankStore.swift) and [MemoryBankHUDController.swift](Sources/WOS/MemoryBankHUDController.swift).

### Fixed
- **iTerm tabs no longer collapse into one — or send you to the wrong tab — when several share a title.** Terminal sessions are identified by *position* (window + tab index), but two paths — drift reconciliation and commit-time dedup — were matching them by *title*. Because iTerm tabs routinely share a title (`-zsh`, `bash`, a folder name), distinct sessions were treated as one: the Cmd+Tab recents collapsed several tabs into a single row and "go back" landed on the wrong session. Terminals now match by position only in both paths. Covered by a regression that provably failed on the old code. See [HistoryReconciler.swift](Sources/SwitchCore/HistoryReconciler.swift), [TargetFiltering.swift](Sources/SwitchCore/TargetFiltering.swift), [LESSONSLEARNED.md](LESSONSLEARNED.md).
- **Cmd+Tab no longer steals focus back to a previous Chrome tab while you are typing.** After a browser-tab switch, WOS verifies it landed and re-activates the target if a different tab is in front. Under AppleScript-engine contention that check could land 1–2 s late — after you had opened a new tab and started typing — and re-activated the old tab on top of you. The recovery is now **freshness-gated**: it fires only within 0.5 s of the switch (and the `.unknown` path only within 0.8 s, never on a timed-out read); past that, your current tab is assumed intentional. See [BrowserTabMatch.swift](Sources/SwitchCore/BrowserTabMatch.swift), [AppDelegate.swift](Sources/WOS/AppDelegate.swift), [ARCHITECTURE.md → "Load-Aware Graceful Degradation"](ARCHITECTURE.md).
- **One iTerm session is recorded as one entry, not two.** Front-tab sampling and full enumeration now stamp the process id identically for non-PID-targetable browsers (iTerm / Terminal), so a single session no longer surfaces as two identical Cmd+Tab rows. See [BrowserScriptBridge.swift](Sources/SwitchCore/BrowserScriptBridge.swift) and [ITermIdentityTests.swift](Tests/SwitchCoreTests/ITermIdentityTests.swift).

### Changed
- **Duplicate terminal tabs show their position in the switcher.** When two iTerm/Terminal tabs share a title, each row now carries a subtle position suffix — `-zsh · 3`, `-zsh · 7` — matching the ⌘N number iTerm prints on each tab, so you can tell them apart. See [LRSTarget.swift](Sources/SwitchCore/LRSTarget.swift).
- **Architecture documented: load-aware graceful degradation.** WOS uses *one* adaptive algorithm — refresh backoff, budgeted fetches, freshness-gated recovery — that degrades along a continuum under load, rather than switching between discrete "normal" and "heavy-load" modes. Now a first-class section in [ARCHITECTURE.md](ARCHITECTURE.md).

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1008, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1007 (2026-06-01)

### Changed
- **The "Search menu bar items" panel (⌘⇧B) accessory badge is redesigned.** The trailing chip that names a menu item's specific control ("Battery", "Dark Mode", "Bluetooth") was a heavy **monospaced**, full-contrast slab in a 36 pt box with a forced 64 pt minimum width — so even a short word rendered as a chunky typewriter block. It is now a subtle, content-hugging pill: a padded container (`AccessoryBadgeView`) with an inner label pinned by Auto Layout — proportional system font (12 pt medium), `secondaryLabelColor` text, a 6 % `labelColor` fill, even 11×4 pt internal padding, vertically centred, hugging its text (capped at 200 pt with tail truncation), corner radius 6, 18 pt right margin. See [Sources/WOS/MenuBarExtrasPanelController.swift](Sources/WOS/MenuBarExtrasPanelController.swift).
- **Cleaner ⌘⇧B header.** The redundant **"Visible"** section header and the horizontal divider rule beneath the search field are removed — the first results group is implicitly the visible items, so the search field now flows directly into the list. The **"Hidden"** header is kept so hidden menu-bar items remain grouped. See [Sources/WOS/MenuBarExtrasPanelController.swift](Sources/WOS/MenuBarExtrasPanelController.swift).

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1007, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1006 (2026-06-01)

### Added
- **Memory Bank "wrong-shortcut rescue."** If you have text selected when you open the Memory Bank **search** HUD (**⇧⌘M**) — easy to hit when you meant the **save** shortcut (**⌥⌘M**) — a banner now offers to save that selection as a new entry. **⏎** (or click **Save**) commits it; **esc** (or **Discard**) dismisses the banner and keeps you searching, and a second **esc** closes the HUD. The entry is a *draft* — nothing is written to disk until you confirm. The selection is read via the Accessibility API **only** (never the synthetic-Cmd+C "copy trick" that the save path falls back to), so a missing selection costs nothing, no keystroke is injected into the frontmost app, and the clipboard is never touched. Text that's already saved verbatim is not re-offered. See [Sources/WOS/MemoryBankHUDController.swift](Sources/WOS/MemoryBankHUDController.swift) and [Sources/WOS/SelectionUtilities.swift](Sources/WOS/SelectionUtilities.swift).

### Changed
- **The "Search menu bar items" panel (⌘⇧B) now matches Quick Search (Cmd+Tab+S) exactly.** It reuses Quick Search's Spotlight-style search field — fixing the **clipped placeholder** (the old plain `NSSearchField` had no vertical-centering cell) and adding the separate magnifier glyph with matching fonts and metrics. The window is now a **chromeless borderless overlay** (no more close / minimize / zoom traffic-light buttons; **esc** dismisses). And while the Accessibility scan runs it shows the same rotating **"did you know?" tips card** as Quick Search instead of a bare spinner. See [Sources/WOS/MenuBarExtrasPanelController.swift](Sources/WOS/MenuBarExtrasPanelController.swift).

### Fixed
- **⌘⇧B now opens on the first press — no need to tap Cmd+Tab first.** The hotkey-inspection fast-path gate matched the menu-bar shortcut against a **Shift-stripped** modifier set, but the default **⌘⇧B requires Shift** — so a cold idle press never passed the gate and only worked once a Cmd+Tab cycle was already active (which short-circuits the gate). The gate now matches the full modifiers; Shift-stripping remains correct only for the Cmd+Tab primary/fallback shortcuts, where Shift is the reverse-direction modifier. See [Sources/WOS/HotkeyTap.swift](Sources/WOS/HotkeyTap.swift).

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1006, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1005 (2026-06-01)

### Added
- **Memory Bank — a searchable Markdown wiki for everything you copy.** Select text anywhere on macOS (browser, editor, PDF, chat, anywhere `kAXSelectedTextAttribute` or the copy-trick reaches) and press **⌥⌘M** to append it to a persistent knowledge base. Press **⇧⌘M** to open a glassmorphic, Spotlight-styled search HUD with live multi-term filtering; `↩` pastes the chosen snippet into the app you came from, `⌘C` copies, `⌘⌫` deletes, `⌘O` reveals the file in Finder, `Esc` closes. Storage is plain Markdown at `~/Library/Application Support/WOS/memory_bank.md` — editable by hand in any Markdown editor and round-trips losslessly thanks to invisible HTML-comment markers per entry. Both shortcuts are user-configurable in Settings → Shortcuts. **Zero new sandbox primitives** were added: the file lives inside the directory rule **2b** already authorized for `recent_files.json`. See [Sources/WOS/MemoryBankStore.swift](Sources/WOS/MemoryBankStore.swift), [Sources/WOS/MemoryBankHUDController.swift](Sources/WOS/MemoryBankHUDController.swift), [ARCHITECTURE.md §14](ARCHITECTURE.md), and [SECURITY.md → "Filesystem Writes — Memory Bank"](SECURITY.md).

### Fixed
- **Cmd+Tab no longer drops a tab that happens to share a title with another.** On every browser-tab commit WOS prunes stale duplicate history entries, but the title-based fallback fired even when two tabs had *different* URLs — so committing to one of two same-titled tabs (two Gmail accounts both showing "Inbox", several "Untitled" editors, duplicate page titles across windows) silently evicted the other from the Cmd+Tab list until it was seen again. Title is now used as a dedup key only when neither side has a URL. The matching is a pure, unit-tested predicate. See [Sources/SwitchCore/TargetFiltering.swift](Sources/SwitchCore/TargetFiltering.swift) and [Sources/WOS/AppDelegate.swift](Sources/WOS/AppDelegate.swift).
- **Duplicate iTerm / Terminal rows in the switcher are gone.** The same terminal session could appear twice because the front-tab sampler stamped the live process id into the tab's identity while full enumeration did not — and terminals fold the pid into their stable identity, so one tab produced two different IDs (`tab::iterm::<win>::<idx>::pid<PID>` vs `tab::iterm::<win>::<idx>`). Both recording paths now run their identity pid through a single `stableIdentityPID(...)` policy that mirrors enumeration, so one tab is one row. Chromium (GUID identity) and Safari (no pid in identity) are unaffected. See [Sources/SwitchCore/BrowserScriptBridge.swift](Sources/SwitchCore/BrowserScriptBridge.swift).
- **Recent switches survive a crash far better.** The LRS-history on-disk save debounce dropped from 60 s to 10 s. A graceful quit already flushes immediately in `applicationWillTerminate`; this bounds how much recency a crash or force-quit (which skips that flush) can lose. See [Sources/WOS/AppDelegate.swift](Sources/WOS/AppDelegate.swift).
- **History restore no longer races engine startup.** Saved history is now loaded from disk *before* the app-activation observer and front-tab sampler start, so a switch recorded during launch can't be clobbered by the disk restore rebuilding the history dictionary. See [Sources/WOS/AppDelegate.swift](Sources/WOS/AppDelegate.swift).

### Security
- **Filesystem-writes audit refreshed.** [SECURITY.md](SECURITY.md) gains a "Filesystem Writes" section enumerating every on-disk path WOS is permitted to write to, the Seatbelt rule authorizing each, and what each file contains — including the new `memory_bank.md`. The website security page is corrected to match: it previously claimed WOS writes to "exactly one directory" (`~/Documents/WOS-Exports/`), which has been inaccurate since v0.1.853 when `recent_files.json` was added. The embedded SBPL profile on the website now reflects the real, current rules.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1005, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.1004 (2026-05-29)

### Fixed
- **Clipboard HUD now scrolls to follow the keyboard selection.** Arrow-key navigation moved the selected row correctly, but the list never scrolled to keep it in view: `scrollToSelected()` sent `scrollToVisible(_:)` to the scroll view's *clip view*, which is a no-op (a clip view has no enclosing clip view to scroll). Once the selection passed the visible rows it slid below the fold and every on-screen row looked unselected, so navigation felt broken. The call now targets the document view (the stack) with the row's rect plus 8 pt of padding, so the highlighted item stays on screen as you arrow up/down. See [Sources/WOS/ClipboardHistoryHUDController.swift](Sources/WOS/ClipboardHistoryHUDController.swift).

---

## 0.1.1003 (2026-05-28)

### Fixed
- **iTerm tabs now appear in search at all — and after a WOS restart.** Two distinct bugs were keeping iTerm sessions out of the Cmd+Tab+S panel:
  1. **iTerm tabs never enumerated, even when iTerm was healthy.** The enumeration AppleScript read each tab's position via `index of t`, but iTerm2 tabs don't support that property — it raises `-1728`, which threw inside the per-row `try` and silently skipped *every* tab. WOS therefore always saw zero iTerm tabs. The script now uses a per-window positional counter (matching the `select tab N of window` activation path) and reads the title from `name of current session of t`, the property iTerm actually exposes. See [Sources/SwitchCore/BrowserScriptBridge.swift](Sources/SwitchCore/BrowserScriptBridge.swift).
  2. **Reactive-only discovery after restart.** Even with enumeration working, iTerm was previously learned only when the user focused a session (`TabFocusObserver`), so a fresh WOS/login-item start showed nothing until a manual focus. A **cold-start retry** (~2.5s after launch) plus a **Quick-search lazy fallback** now run a targeted iTerm-only re-enumeration when the cache is empty but iTerm is running, merging results without clobbering other browsers. Both in [Sources/WOS/AppDelegate.swift](Sources/WOS/AppDelegate.swift).
- **A wedged iTerm can no longer stall tab enumeration.** When iTerm's AppleScript handler hangs (e.g. a stuck session), even `count of windows` times out — previously `osascript` blocked ~2 minutes until the system `-1712`, freezing the snapshot and any Quick Search load waiting on it. Terminal scans now get a hard 3-second watchdog that SIGKILLs the stuck `osascript`, routes to the existing per-app backoff, and surfaces a HUD note: *"iTerm isn't responding to automation — quit & relaunch iTerm to restore tab search."* Terminals run subprocess-only so a hang can never block the shared in-process AppleScript engine (which would also stall the Cmd+Tab cycle).
- **Clipboard HUD no longer overflows off-screen.** The 0.1.1003 row redesign (below) wrapped the preview/meta labels in a vertical `NSStackView` whose `.leading` alignment leaves child widths at their intrinsic size. A long URL (~925pt) then dragged the fixed 520pt panel to ~1054pt wide, spilling off the right edge of the screen. The text fields are now pinned to the stack width with `.defaultLow` horizontal compression resistance so long content tail-truncates instead of growing the window. See [Sources/WOS/ClipboardHistoryHUDController.swift](Sources/WOS/ClipboardHistoryHUDController.swift).

### Changed
- **Clipboard HUD rows redesigned for a content-aware, lighter look.** The Clipboard HUD inherited the 40×40 icon plate + 14.5pt semibold title from SwitcherHUD/RecentFiles, but those rows show real app/file icons whereas clipboard rows all rendered the same generic `doc.on.clipboard` symbol — the plate added weight without signal and the heavy title read as aggressive for arbitrary body text. Rows are now 52pt tall (was 64), cornerRadius 10 (was 14), with a type-aware leading SF Symbol (link/blue for URLs, `text.alignleft` for multi-line, `text.quote` for single-line), 13.5pt regular preview text, a meta sub-line (`URL · 47 chars` / `Multi-line · 12 lines · 320 chars`), and compact monospaced relative times (`2m`, `1h`, `3d`). Chrome, section label, footer, and selection tokens are unchanged so the HUD still reads as part of the WOS HUD family. See [Sources/WOS/ClipboardHistoryHUDController.swift](Sources/WOS/ClipboardHistoryHUDController.swift).

### Known issues
- **Clipboard HUD centers on the mouse's screen.** On a multi-monitor setup the HUD appears on whichever display holds the pointer (consistent with Quick Search, but the Switcher HUD uses the active-window screen instead). If it shows on the "wrong" monitor, this is why.
- **Identically-titled iTerm tabs may collapse.** Several sessions sharing the exact same title (e.g. multiple `-zsh`) can de-duplicate down to fewer rows in the list.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.1003, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.997 (2026-05-22)

### Added
- **App-target activation latency warning.** A new `AppActivationLatencyMonitor` watches the existing app-target activation measurement pipeline in `AppDelegate`; when a single activation crosses **5 seconds** (hard threshold, not a rolling ratio — app activations span "already-running, instant" → "cold launch with disk thrash" → "wedged, never comes forward", and only the high tail is user-actionable), it surfaces the existing `BrowserSlowdownPillController` with a per-app message like *"Safari took 8.3s to activate · restart recommended"*. A 30-second per-bundle cooldown prevents a single wedged app from spamming the pill across rapid retries. The pill controller gains an optional `dwell:` override so the longer remediation message gets 6s on screen instead of the browser path's default 2.6s.

### Changed
- **What's New panel redesigned in native Apple style.** [Sources/WOS/WhatsNew/WhatsNewView.swift](Sources/WOS/WhatsNew/WhatsNewView.swift) was rebuilt to match the macOS Welcome / iWork *What's New* idiom instead of a custom dark card: adaptive light/dark background via `NSVisualEffectView` with the `.windowBackground` material (no more hardcoded near-black + forced dark mode), app icon hero above the title, SF Symbols rendered with `.symbolRenderingMode(.hierarchical)` and per-row accent tints (blue / indigo / orange) replacing the monochrome-icon-in-chip treatment, keyboard shortcuts rendered as actual key caps (⌘ ⇥ L) instead of a dash-separated text pill, and the CTA now uses native `.borderedProminent` at `.controlSize(.large)` with `.keyboardShortcut(.defaultAction)` so Return dismisses. Body text moved to `.foregroundStyle(.secondary)` and system font sizes so contrast is correct in both color schemes.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.997, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.995 (2026-05-22)

### Fixed
- **Cross-window Cmd+Tab no longer wedges on the "two windows have the same active-tab label" case.** When two VS Code windows both showed e.g. `Welcome`, or both had the same file open as their active tab, the title-fallback raise path in `VSCodeIPCTabSource.raiseVSCodeWindow(matchingActiveTabLabel:)` saw two AX windows whose titles contained the label, refused as ambiguous (the 0.1.989 P1-4 guard), and the user saw "nothing happens" on the second-or-later cross-window switch. **This is the most likely concrete root cause of the previously-reported "the 2nd time doesn't work" symptom** — the prior 0.1.992 / 0.1.993 fixes addressed adjacent routing paths but didn't disambiguate this case.
- **The fix** adds two layered disambiguators inside `raiseVSCodeWindow`, both kicking in only when title-prefix matching finds 2+ candidates:
  1. **Workspace-suffix match.** VS Code window titles end with ` — <workspace-folder-name>` (e.g. `tab-4.txt — wos-test-w1`). The IPC bridge derives the target entry's workspace name from the longest common path prefix of its `file://` tab URIs (excluding the filename component), then picks the candidate whose AX title ends with ` — <workspace>`. Works without VS Code's `editor.accessibilitySupport: on` and survives every common multi-window setup.
  2. **AX tab-strip overlap.** Falls back to reading each candidate AX window's tab strip via the existing `AccessibilityTabBridge` BFS, and picks the window whose visible tabs have the largest set-overlap with the target entry's IPC tab list. Only useful when accessibilitySupport is on (the AX tabs are otherwise empty), but a free safety net for users who happen to have it enabled.
- Both disambiguators only commit when the winner has a strictly-better signal than every other candidate — a tie still triggers the original "refusing as ambiguous" error rather than guessing.

### Added (test infrastructure)
- **`scripts/qa_daily_runner.sh`** + **`scripts/install_qa_launchd.sh`**: a local LaunchAgent that runs the WOS regression suites every morning at 08:00 local time, logs to `~/Library/Logs/wos-qa-daily/`, and posts a macOS notification on failure. Install with `./scripts/install_qa_launchd.sh install`; uninstall with `./scripts/install_qa_launchd.sh uninstall`; status with `./scripts/install_qa_launchd.sh status`. The runner skips gracefully (exit 0, no notification) if WOS isn't running in debug-control mode — no false alarms on machines that don't opt into daily QA.
- The runner found exactly this title-fallback ambiguity bug on its first execution.
- `scripts/qa_vscode_lrs_ordering.sh`'s test corpus updated to use window-prefixed filenames (`w1-tab-1.txt` instead of `tab-1.txt`) so it doesn't accidentally exercise the same-label ambiguity case the 0.1.995 fix is designed to handle gracefully.

### Test results on this build
- `swift test --filter VSCodeIPCEntryRoutingTests` → 10/10
- `scripts/qa_vscode_multi_window.sh` → **28/28** (including the previously-failing tab-4.txt ambiguity cases — now disambiguated by workspace suffix)
- `scripts/qa_vscode_lrs_ordering.sh` → **28/28**
- Total: **66/66**

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.995, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.994 (2026-05-21)

### Added (test infrastructure)
- **`scripts/qa_vscode_lrs_ordering.sh`** — multi-window LRS-ordering regression suite. Spawns two **brand-new** VS Code windows via the bundled `code` CLI (independent of any pre-existing user state), opens five test files in each (10 test tabs total), then drives 20 randomized `(window, tab)` activations through the debug control socket and asserts the LRS history's recency ordering behaves under concurrent `markSeen` pressure. **28/28 assertions pass on the binary that ships in this release.**
- **Three new debug-control-socket commands** powering the suite (off in normal use; gated on `WOS_DEBUG_CONTROL=1` like the rest of the debug socket):
  - `list-entries-full` — returns each connected entry's full `tabs[]` array (existing `list-entries` only exposes the active tab).
  - `mark-seen-stableid` — simulates the `LRSHistory.markSeen` call the cycle pipeline fires on every commit, scoped to a specific LRS slot.
  - `activate-and-mark` — composed primitive: invoke `EditorTabSourceSelector.activate` then `LRSHistory.markSeen` for the target identified by `stableID`. One iteration of this mirrors a real Cmd+Tab commit.

### Why this is a separate release from 0.1.993
PR #45 (0.1.993) shipped the `.browserWindow` routing fix and the first multi-window regression suite. PR #46 (merged after 0.1.993 shipped) added the LRS-ordering suite + the three new debug commands above. 0.1.994 bundles those source-only changes into a notarized binary so the suite can run against the DMG that's actually on the website — without that, the production binary would be missing the `list-entries-full` / `activate-and-mark` commands the suite depends on.

### What this means for users
**No user-visible behavior change.** Everything in this release is dev-time tooling, gated on an env var that's off by default. The fix that closed the cross-window "second-time-doesn't-work" symptom is still 0.1.993's contribution — this release just makes future regressions of that symptom catchable by a one-line shell command.

### How to run the new suite
```bash
pkill -x WOS
WOS_DEBUG_CONTROL=1 open -a /Applications/WOS.app
sleep 3
./scripts/qa_vscode_lrs_ordering.sh
```

The test invariants are realistic for an LRS engine that receives concurrent `markSeen` from at least 4 subsystems (cycle commit, ~300ms front-tab sampler, window-focus observer, app-activation observer): every activated target lands in the LRS top-5 within 1.5s of the activation, and all unique activated targets remain in the LRS top-20 after the 20-iteration loop. The suite tolerates the sampler reinforcement that's intentional in production — what it catches is a target falling out of recency entirely (the bug shape that would surface as "the tab I just cycled to isn't in my next Cmd+Tab MRU").

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.994, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.993 (2026-05-21)

### Fixed
- **Cross-window tab switching follow-up: `.browserWindow` VS Code targets now route through the IPC bridge instead of falling through to the AX bridge.** 0.1.992 fixed the colliding-tab-id case for `.browserTab` targets, but the activation path still had two more rough edges that could surface as "the 2nd cross-window jump doesn't work":
  - **`.browserWindow` targets always fell through to `AccessibilityTabBridge.activate`.** The AX-bridge's `kAXWindows[0]` heuristic raises whichever window is at AX index 0 when the stored title doesn't match any current window title — which is almost always the case after a focus change because the title bar repaints ~50-100 ms behind the IPC frame. Result: clicking a window-level LRS slot raised whichever window macOS happened to put at index 0, not the one the user picked.
  - **Failed IPC activations silently fell through to AX.** `EditorTabSourceSelector.activate` was wired to retry via the AX bridge if the IPC call returned false. For VS Code that's worse than failing visibly — the AX bridge would raise an arbitrary window instead of doing nothing. Combined with the bullet above, the user saw an unexplained window flip every time IPC routing came up short.
- **Fix**:
  - `VSCodeIPCTabSource.activate(target:)` now accepts both `.browserTab` and `.browserWindow` targets. For `.browserWindow`, it looks up the entry by `windowIdentifier` ("ipc:<n>") alone and raises the entry's window without sending a per-tab focus IPC. Falls back to `lookupFrontmostEntry` only for legacy non-`ipc:` window identifiers.
  - `EditorTabSourceSelector.activate` dispatches by browser kind. For VS Code with IPC ready, it routes through `VSCodeIPCTabSource.activate` and refuses to fall through to the AX bridge — a failed IPC routing is now a visible no-op instead of a wrong-window raise.
  - The capture-on-raise update in `VSCodeIPCTabSource.activate` was extended to scope writes correctly for `.browserWindow` targets too (single matched entry, never broadcast).
- **Test infrastructure**: this release ships an actual end-to-end multi-window regression suite that previous releases relied on hand-driven CUA tests for.
  - `scripts/qa_vscode_multi_window.sh` exercises six sections (pre-flight, cold-cache window activation per entry, alternating cross-window switches A→B→A→B→A, tab-level activation, repeated activations of the same entry as a state-leak guard, and full LRS-history coverage). Each section asserts both the `activate(target:)` return value AND that the AX-frontmost window's title contains the entry's expected active-tab label.
  - To make the suite runnable without keystroke synthesis (synthesized `CGEvent`s require Accessibility on the calling process — fundamentally untestable from a stock shell), 0.1.993 adds a hidden test control socket. Off in normal use; enable with `WOS_DEBUG_CONTROL=1 open -a /Applications/WOS.app`. It listens at `~/Library/Caches/com.agentic.wos/sockets/debug-control.sock` and accepts JSON commands: `list-entries`, `activate-tab`, `activate-window`, `query-frontmost`, `query-frontmost-app`, `reset-ax-cache`, `list-lrs-history`, `activate-by-stableid`.
  - The suite passed 20/20 against the build that ships in this release, including the exact alternating-cross-window sequence the user-reported "second time doesn't work" symptom would hit.
- **Diagnostic log added in `VSCodeIPCTabSource.activate(target:)`**: every activation now logs `windowIdentifier`, `parsedWindowID`, and `tabID` so future regressions are reproducible from a fresh log capture without needing to reproduce the user's exact state.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.993, macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS.

---

## 0.1.992 (2026-05-21)

### Fixed
- **Cmd+Tab activated the wrong tab — or raised the wrong window — when two VS Code windows shared any tab with the same id (Welcome, Untitled-1, or the same source file open in both).** Discovered via a live two-window VS Code session with WOS's IPC log monitor running. The smoking gun: two log lines fired in a single user cycle, at the **same** microsecond timestamp, for two **different** entries with the **same** raised `AXUIElement`:
  ```
  2026-05-20 20:27:33.365 VSCodeIPC: captured AX window via activate-raise for entry windowID=20527571 appPID=80134
  2026-05-20 20:27:33.365 VSCodeIPC: captured AX window via activate-raise for entry windowID=20722269 appPID=80134
  ```
  Two compounding defects in `Sources/SwitchCore/VSCodeIPCTabSource.swift`:
  - **`activate(target:)` ignored `windowIdentifier`.** `LRSTarget.browserTab` carries a 3rd field of the form `"ipc:<entry.windowID>"` that uniquely pins each tab to a specific VS Code window — but `activate(target:)` only used the `url` field (the extension's synthesized stable tab id) to find the owning `ClientEntry`. The entry-resolution loop returned the **first** entry whose tabs contained the target id, so when two VS Code windows both contained a tab with the same extension-generated id, first-match-wins routed the activation to the wrong window. This was bound to bite the moment a user had two VS Code windows open — both have a Welcome tab, both have an Untitled-1, anyone who keeps the same project open in two windows shares every file id.
  - **Capture-on-raise broadcast `axWindow` to every matching entry.** After `raiseAXWindow` succeeded, the post-raise update block iterated **every** `ClientEntry` whose tab list contained the target id and wrote `entry.axWindow = raised` into all of them. So a single user-initiated focus action corrupted the AX-window cache for every other VS Code window that happened to share even one tab id with the picked entry. Subsequent activations of those entries then used the wrong cached AX reference and raised the wrong window — even if you'd never have noticed the collision yourself, because two `Welcome` tabs are a perfectly ordinary state.
- **Fix**: extract the `windowIdentifier` alongside `tabID` in `activate(target:)`, parse the integer after the `"ipc:"` prefix, and prefer the entry whose `windowID` matches over the legacy first-match-wins behaviour. The capture-on-raise block now resolves the same way and writes `axWindow` into **one** entry only — never broadcasting. Fallback to id-only resolution preserved for any caller that omits a windowIdentifier (defensive against future AX cycle paths). Decision logic isolated into `parseIPCWindowID(from:)` and `routeEntryIndex(candidates:tabID:windowIdentifier:)` static helpers, exposed via `@testable import` so the routing is exercisable without the AppKit/AX stack.
- **Test coverage**: new `Tests/SwitchCoreTests/VSCodeIPCEntryRoutingTests.swift` with **10 tests** covering: the colliding-tab-id scenario (two windows share a `welcome` tab, target is window 2 — exactly the production evidence above); all `parseIPCWindowID` happy/sad paths (positive ints, empty string, `ipc:` with no number, non-numeric tail, `IPC:` wrong case, non-`ipc:` scheme); the capture-on-raise invariant that exactly one entry is selected even when two share the tabID; three fallback cases (empty windowIdentifier, windowID absent from candidates, legacy non-`ipc:` scheme); and nil-result edge cases (tab not found, empty candidate list).
- **Audit**: `audit/vscode-ipc-id-routing.md` classifies all five entry-resolution sites in `VSCodeIPCTabSource.swift`. Two were unsafe (now fixed); the other three are safe — `tabs(forBrowser:pid:maxTabs:)` aggregates from all entries by design (read-only, multi-window-tab-list), `frontTab(forBrowser:pid:)` only reads the active-tab label, and `handleTabs` scopes by connection identity (`===`) not tabID, so an extension-host's frame never updates another extension-host's entry.

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.992 (or 0.1.991, 0.1.990, 0.1.989), macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS. Verify in logs that `[com.agentic.wos:hotkey] Global event tap started` appears at startup.

---

## 0.1.991 (2026-05-20)

### Fixed
- **Cmd+Tab silently fell through to the native macOS app switcher on a fresh-launch / TCC-reset path because the hotkey event tap was never registered.** Discovered via real-world testing after `tccutil reset` of WOS's Accessibility + Input Monitoring permissions. Symptom: the native macOS Cmd+Tab UI appears instead of WOS's HUD. Logs showed `[hotkey] Accessibility trust changed: trusted=true` and `[app] Permissions verified. Starting WOS engines...` — but `Global event tap started` never followed. More damningly, `Permissions verified...` fired **twice** ~12 seconds apart in the same process. Root cause was a **reentrancy bug in `AppDelegate.startEngines()`**:
  - `startEngines()` set up notification observers, started tab sampling / workspace observation / VS Code IPC / clipboard / recent-files engine, *then* created the `hotkeyTap` and called `start()`.
  - On fresh launches after a TCC reset, the proactive automation probe + Spotlight init + AppleScript browser tab enumeration combined to take **~10 seconds** of synchronous work that pumped the main runloop (each AppleScript call internally yields).
  - During those 10 seconds, the 1 Hz accessibility-watchdog timer fired on the main thread. `reconcileHotkeyState` saw `hotkeyTap == nil` (the assignment hadn't been reached yet) and called `startEngines()` again. The `guard hotkeyTap == nil else { return }` at the top was ineffective because the SAME thread re-entered before the assignment.
  - Both reentrant calls eventually reached the `hotkeyTap = HotkeyTap(...)` line; the second call's `HotkeyTap` instance overwrote the first's, but BOTH had already had `start()` called on them. The orphaned first instance's CFRunLoop thread was likely never re-tickled with the runloop add-source from the second instance — the event tap mach port was registered but the corresponding `HotkeyTap` object held by `self` had no live tap.
  - Net effect: no `Global event tap started` log, no Cmd+Tab cycle, native switcher takes over.
- **Fix**: extracted the hotkey-tap creation + `start()` into a new `installHotkeyTap()` method, called at the **top** of `startEngines()` immediately after the guard. The event tap is now registered in <100 ms before any blocking engine setup runs. Any subsequent reentrant `startEngines()` call hits the guard with `hotkeyTap != nil` and bails immediately. `installHotkeyTap()` itself is non-reentrant because the guard catches its second entry before any work.

---

## 0.1.990 (2026-05-20)

### Fixed
- **0.1.989's capture path was still rejecting valid captures due to title-bar repaint lag.** Real-world CUA testing of 0.1.989 showed `kAXFocusedWindow` returning the correct window for active-tab changes (single candidate, AX sources agreed) — but the title sanity check kept rejecting because the macOS title bar repaints ~50-100 ms *after* `onDidChangeTabs` fires, so the AX `kAXTitle` still reads the *previous* active-tab label at the millisecond WOS reads it. The check `title.contains(newLabel)` returned false for every cross-window switch, leaving entries operating on stale-or-nil references and falling back to title-prefix matching. Two changes:
  - **Single-candidate trust.** When `kAXFocusedWindow` and `kAXFocusedUIElement` agree on one window (or only one source returns), there is no ambiguity — the title check was protecting against multi-window AX-state lag that doesn't apply here. Capture the candidate immediately when the title matches the *new* active-tab label, OR when it still contains the *previous* active-tab label (diagnostic of title-bar lag — the window IS ours, just one repaint frame behind). Only reject when the title matches neither (real wrong-window or AX-state-lag case).
  - **`windowFocused=true` fast path.** With extension 0.2.0+, when the extension self-reports its window is OS-focused, we skip the title check entirely and capture the first candidate — the extension knows its own focus state authoritatively via `vscode.window.state.focused`, which is a stronger signal than any title comparison.
- **Logging privacy bug: `focused=<private>` in tabs-update logs.** The `windowFocused` interpolation in `os_log` defaulted to `.private` redaction, hiding whether the new 0.2.0 extension's self-report was actually arriving. Added explicit `, privacy: .public` modifier in two places (`tabs update` and `active tab changed` logs).

### Known issue — re-grant Input Monitoring after upgrading
After installing 0.1.990 (or 0.1.989), macOS may revoke WOS's Input Monitoring permission because the binary signature differs from the previously-installed version. Symptom: WOS Cmd+Tab cycle does nothing (or falls through to the native macOS app switcher). Fix: **System Settings → Privacy & Security → Input Monitoring → toggle WOS off and back on (or re-add it)**, then restart WOS. Verify in logs that `[com.agentic.wos:hotkey] Global event tap started` appears at startup.

---

## 0.1.989 (2026-05-20)

### Fixed
- **VS Code cross-window tab switching: ~75% of AX window captures were silently rejected, leaving entries operating on stale-or-nil references.** Diagnostic logs showed `kAXFocusedWindow` returning the *previously*-focused VS Code window for ~75 % of active-tab-change events, because the IPC frame from the extension's `onDidChangeTabs` arrives microseconds before macOS commits the focus change to AX. The "skipped AX window capture; preserving previous reference" log fired for every cross-app Cmd+Tab into VS Code — and when "previous reference" was nil (initial capture also failed), `activate(target:)` quietly fell back to the title-prefix path that 0.1.988 was supposed to make obsolete. Fixed via six coordinated changes:
  - **P0-1: success log on capture.** `captureFocusedAXWindow` now logs `.notice` with the captured title on success — the 0.1.988 logs only had the rejection side, so we couldn't tell whether captures were ever landing.
  - **P0-2: capture-on-raise in `activate()`.** When `raiseAXWindow` or `raiseVSCodeWindow` successfully raises an AXUIElement, we *know* it's the right window for the target tab's entry (we just told the OS to focus it). Storing it back on the entry breaks the dependency on `captureFocusedAXWindow` ever landing.
  - **P1-3: `kAXFocusedUIElement` cross-check.** Capture now reads both `kAXFocusedWindow` and `kAXFocusedUIElement` (walking up via `kAXWindow`); these can disagree by one AX frame during focus transitions. Picks whichever candidate's title matches the expected active-tab label.
  - **P1-4: refuse ambiguous title-fallback.** `raiseVSCodeWindow` now counts title matches; when two or more windows match (the "two windows both show Untitled-1" case), it logs `.error` and refuses rather than silently picking the first. Silent-wrong is worse than fail-loud.
  - **P2-5: extension reports `windowFocused`.** Bundled extension bumped to 0.2.0; tabs frames now ship `windowFocused: vscode.window.state.focused`. The extension knows directly whether its window is OS-focused — no AX race. The Swift side gates `captureFocusedAXWindow` on this flag (skip entirely when `false`), and `lookupFrontmostEntry` prefers `windowFocused=true` entries over `lastTabsFrameAt` (which ticks for programmatic / autosave changes too). New `lastUserInteractionAt` advances only when `windowFocused=true`.
  - **P2-6: 3-strikes `.error` log.** A `consecutiveCaptureRejections` counter on each entry triggers an `.error` log after 3 consecutive rejections — making "this entry has never captured" visible in normal triage instead of buried in `.notice` noise.
- **After upgrading to 0.1.989, in each open VS Code window: `⌘⇧P` → "Developer: Reload Window" → Enter** to pick up the 0.2.0 extension. Old extension (0.1.0) keeps working with degraded behaviour — Swift falls back to the pre-0.1.989 heuristics for entries that don't report `windowFocused`.

---

## 0.1.988 (2026-05-19)

### Fixed
- **Multi-window VS Code window-raise picked the wrong window when both windows shared an active tab title.** 0.1.987 raised the target window via `kAXTitle` *contains* the entry's active-tab label. When two VS Code windows have similar or identical active-tab labels (e.g. both showing "Welcome", both "Untitled-1", or one tab's label is a substring of the other window's label like "Untitled-2" being inside "new22222 • Untitled-2"), title-matching is ambiguous and falls on whichever AX window comes first — usually the wrong one. The IPC focus then routed to the correct extension host, but the OS-foreground window was the wrong one, so the user saw nothing change. Fixed by capturing the VS Code app's `kAXFocusedWindow` reference into the owning `ClientEntry` at every active-tab change for that entry: that event implies the user just clicked *inside* that window, so at that instant the OS-focused AX window IS unambiguously this entry's window. `activate(target:)` now raises via that direct AX reference, with title-prefix matching kept only as a fallback for entries that haven't seen an active-tab change yet (just connected). A title-sanity check on capture rejects bogus captures when an unrelated programmatic tab change fires in a non-focused window.

---

## 0.1.987 (2026-05-19)

### Fixed
- **Picking a VS Code tab from a non-frontmost window did not raise that window.** The IPC activate path sent only the `focus` frame to the extension; the extension's `vscode.window.showTextDocument(...)` correctly revealed the tab inside its window, but didn't bring that window to the OS foreground. If you had two VS Code windows open and Cmd+Tab'd to a tab in the *background* window, the active tab in that window changed silently while you continued staring at the still-frontmost other window. `NSRunningApplication.activate` couldn't fix it either — VS Code was already the frontmost app (via the other window), so app-level activation was a no-op. Fixed by having `VSCodeIPCTabSource.activate` AX-raise the target window before issuing IPC focus. The bridge knows which entry owns the picked tab, and each VS Code window's title bar prefix is its currently-active tab's label, so AX `kAXTitleAttribute` title-matching against `entry.tabs.first(active).label` finds the right `AXUIElement` even when two windows belong to the same main process. `kAXRaiseAction` brings it forward; the subsequent IPC focus then swaps the active tab.

---

## 0.1.986 (2026-05-19)

### Added
- **Settings → VS Code status line now flags un-reloaded windows.** When the WOS Tab Bridge IPC count is lower than the number of VS Code windows AX sees for the same app pid, the status line reads e.g. *"Status: WOS Tab Bridge connected to 1 of 2 VS Code windows. 1 window is not connected — extensions installed after a window was opened only activate after that window reloads. In each missing window: ⌘⇧P → 'Developer: Reload Window' → Enter."* This makes the "I installed the extension but my second window's tabs don't show" failure mode self-diagnosing instead of silent. Added `AccessibilityTabBridge.windowCount(forPID:)` as the AX-side counter consumed by `vsCodeStatusText()`.

---

## 0.1.985 (2026-05-19)

### Fixed
- **Multi-window VS Code only ever surfaced one window's tabs in Cmd+Tab.** Each VS Code window runs its own extension host process — so two open VS Code windows produce two IPC clients connecting to WOS, both with the same main app pid (the parent of both extension hosts). The reverse index `clientsByAppPID` was a one-to-one `[pid_t: ClientEntry]`, so the second `hello` frame silently *overwrote* the first window's entry. When `BrowserScriptBridge` enumerated VS Code tabs via the main app pid, it could only see one window's tabs. Fixed by making `clientsByAppPID` a one-to-many `[pid_t: [ClientEntry]]`: the new `lookupAllEntries` aggregates tabs across every connected window, `tabs(forBrowser:pid:maxTabs:)` concatenates per-window tab lists into one cycle, `activate(target:)` routes the focus command to the specific window whose tab list contains the target tabID, and the front-tab sampler uses `lookupFrontmostEntry` (the window whose extension host most recently pushed a tabs frame) so the "currently visible" tab written into LRS corresponds to the foreground VS Code window. Disconnect / hello-replace paths surgically remove only the affected entry from the per-appPID list so the other window's connection stays live.

---

## 0.1.984 (2026-05-19)

### Fixed
- **VS Code workspace entry in Cmd+Tab always showed the leftmost tab's title (typically "Welcome") instead of the currently-active tab's title.** The IPC active-tab notification path correctly writes the browserWindow entry with the active tab's title (e.g., "Code: index.html"), but the next `HistoryReconciler` pass overwrites it. Root cause: `HistoryReconciler.matchWindow` derives its window candidate from `windowsByBrowserAndNormalizedID`, which is built by taking `Self.browserWindowTarget(from: firstTab)` for the first tab encountered in `openTabs`. For VS Code that's the leftmost tab. When the reconciler then `refresh`es the entry toward that candidate on every stableID-match tick, the active-tab title is silently replaced with the leftmost-tab title. Fixed by skipping the in-place refresh for VS Code browserWindow entries on stableID match — the active-tab `markSeen` becomes authoritative for the window's title. Rename behavior (when the stableID would change) is preserved.

---

## 0.1.983 (2026-05-19)

### Fixed
- **First-visited VS Code tab (idx=1) silently disappeared and idx=1 slot kept getting overwritten with whatever was active.** 0.1.982 fixed the IPC active-tab-change notification path, but missed a second instance of the same anti-pattern in `VSCodeIPCTabSource.frontTab(forBrowser:pid:)` — the method that powers `BrowserScriptBridge.fetchFrontTab(...)`. The front-tab sampler runs every ~300 ms while a "browser" app is frontmost (VS Code counts) and `markSeen`s whatever that method returns. Because `frontTab` still built its target via `makeTargets(tabs: [active], max: 1)`, the returned LRSTarget always had `tabIndex = 1` regardless of where the active tab really sat in the strip. Every sampler tick therefore overwrote the `tab::vscode::ipc:<window>::1::pid<X>` history row with the current active tab's title — destroying whatever browserTab the user had previously visited at index 1 (typically the leftmost / "Welcome" tab). Fixed by building the LRSTarget in `frontTab` with the active tab's **real index in `entry.tabs`**, identical to the active-tab-change path. Both writers now agree on stableID, so the sampler reinforces history instead of clobbering it.

---

## 0.1.982 (2026-05-19)

### Fixed
- **Every VS Code tab collapsed to one history entry whose label kept rewriting.** 0.1.981 hooked the IPC active-tab-change notification into `LRSHistory.markSeen`, but built the target via `makeTargets(tabs: [active], max: 1)` — which numbered the single sliced element as `tabIndex = 1` every time. VS Code does not use `prefersNativeTabIDStableIdentity`, so its tab stableID is `tab::vscode::ipc:<window>::<tabIndex>::pid<X>`. Result: every active-tab notification wrote to the same row, so the user saw at most one VS Code tab in Cmd+Tab and its title changed to whatever they just clicked ("A also shows as B"). Fixed by building the target in `handleTabs` directly with the active tab's **real index in the full tabs list**, so each tab gets a distinct stableID — matching what the cycle-enumeration path (`tabs(forBrowser:pid:maxTabs:)`) already emits. After this fix, every tab you click in VS Code accumulates as its own entry in LRS history and appears in Cmd+Tab.

---

## 0.1.981 (2026-05-19)

### Fixed
- **Only the most-recent VS Code tab appeared in Cmd+Tab even after visiting many tabs.** The cycle is history-driven: it shows recently-visited tabs plus fallback windows. For Chrome, the front-tab sampler runs every ~300 ms and `markSeen`s whatever tab is active in the browser, so every tab you read gets into history. For VS Code, the sampler doesn't help — clicking tabs inside VS Code keeps VS Code as the frontmost app, so the sampler never fires for the new tab. Result: only tabs you explicitly Cmd+Tab'd to ended up in history (typically one), and that's all you saw in the cycle. Fixed by having `VSCodeIPCTabSource` post a `activeTabDidChangeNotification` whenever the extension reports a new active tab. AppDelegate observes the notification and `markSeen`s the new tab — so every tab you click *inside VS Code* now accumulates in LRS history exactly the way Chrome's sampler-driven flow does. After a few minutes of normal use, Cmd+Tab will list every tab you've worked in.

---

## 0.1.980 (2026-05-19)

### Fixed
- **IPC tabs not reaching Cmd+Tab unless user reloaded VS Code.** 0.1.979 fixed the pid-mismatch problem by having the new extension send `process.ppid` (the main VS Code app pid) in its hello frame, but VS Code's extension host caches its loaded extension code for the lifetime of the window — meaning users had to `Cmd+Shift+P → Developer: Reload Window` for the new code to run. WOS now also *derives* the main app pid server-side via `sysctl(KERN_PROC_PID)` whenever a hello frame arrives with `ppid=0` (older extension or future extension that doesn't ship the field). The IPC bridge therefore works without requiring a VS Code reload after a WOS upgrade.

---

## 0.1.979 (2026-05-19)

### Fixed
- **IPC bridge connected but tabs never appeared in Cmd+Tab.** 0.1.978 successfully accepted the client and logged `VSCodeIPC: client hello pid=96113 window=...`, but Cmd+Tab still only surfaced VS Code's WINDOWS via the AX fallback. Root cause: the extension's `process.pid` is the *extension host* process (a child of VS Code's main process — `Code Helper (Plugin)`, ppid=96078). AX-based dispatch in `BrowserScriptBridge` looks up VS Code via `NSRunningApplication`, which returns the *main* process pid. So `EditorTabSourceSelector.resolveSource(pid: 96078)` checked `isReady(forPID: 96078)`, but the IPC source only had a client registered under pid 96113 — fell back to AX, which returned 2 windows and 0 tabs. Fixed by having the extension send `process.ppid` (the main VS Code app pid) in the hello frame and maintaining a reverse lookup `clientsByAppPID` on the Swift side. Every IPC query now tries extension-host pid first, then main-app pid. Also bumped `VSCodeIPC: tabs update` from debug to notice level so the data flow is verifiable in `log show` without enabling debug capture.

---

## 0.1.978 (2026-05-19)

### Fixed
- **WOS Tab Bridge IPC: accepted client connections were silently deallocated.** In 0.1.976 the listener accepted client connections (verifiable in `lsof -U`), but no `VSCodeIPC: client hello` log ever appeared and the extension's data was discarded. Root cause: `allConnections` was a `Set<ObjectIdentifier>`, which does not retain its members — the only strong reference to each `ClientConnection` lived in the `q.async` block that registered it, and the moment that block ran, the connection deallocated. Its `DispatchSourceRead` died with it, so accepted fds remained open but the read handler never fired. Fixed by changing the storage to `[ObjectIdentifier: ClientConnection]` so accepted connections live until `handleDisconnect` explicitly removes them. Added a `VSCodeIPC: accepted client connection fd=N` log line at notice level so future regressions show up at accept time, before the hello.
- **"VS Code tabs hidden" HUD banner persisted even after the extension was installed and connected.** `vsCodeTabsHiddenFromAccessibility()` only checked the Accessibility-based tab snapshot; it didn't know about the IPC bridge. Now it returns false early when `VSCodeIPCTabSource.shared.connectedWindowCount() > 0` — either setup path silences the banner.

---

## 0.1.976 (2026-05-19)

### Fixed
- **WOS Tab Bridge IPC socket failed to bind under the WOS Seatbelt sandbox.** 0.1.975 placed the listener socket at `~/.wos/sockets/vscode.sock`, which is outside every `file-write*` rule in the SBPL profile — `mkdir` was silently denied and `bind()` failed with `errno=2 (ENOENT)`. Moved the default socket path to `~/Library/Caches/com.agentic.wos/sockets/vscode.sock` (already inside an allowed `file-write*` subpath), and added `(allow network* (local ipc))` before the existing `(deny network*)` so AF_UNIX bind/accept work. The network blackout for AF_INET sockets is unchanged — local IPC is kernel-only and never touches the network stack. Companion VS Code extension default updated to match.

---

## 0.1.975 (2026-05-19)

### Added
- **VS Code tab switching, Phase 2 — `wos-tab-bridge` extension + IPC bridge.** Drop-in alternative to the Accessibility-based path shipped in 0.1.974, with cleaner UX: no `editor.accessibilitySupport` requirement, exposes file URIs, dirty/pinned/preview state, and updates instantly on every tab change. The trade-off in the AX path (some font ligatures degrade in screen-reader mode) is gone.
  - [`extension/wos-tab-bridge/`](extension/wos-tab-bridge) — small VS Code extension (TypeScript, ~250 LOC + `.vsix`). Subscribes to `vscode.window.tabGroups.onDidChangeTabs` and streams JSON frames over a Unix socket. Handles `focus` commands back from WOS. Reconnects with exponential backoff (500 ms → 8 s) so it's not order-dependent with WOS startup.
  - [`Sources/SwitchCore/VSCodeIPCTabSource.swift`](Sources/SwitchCore/VSCodeIPCTabSource.swift) — `~/.wos/sockets/vscode.sock` listener. Accepts multiple clients (one per VS Code window), maintains a `pid → tabs` map, mirrors `AccessibilityTabBridge`'s public API (`enumerateTabs`, `fetchFrontTab`, `activate`).
  - [`Sources/SwitchCore/EditorTabSourceSelector.swift`](Sources/SwitchCore/EditorTabSourceSelector.swift) — policy layer that picks IPC when a client is connected for the queried pid, falls back to AX otherwise. `axOnly` and `ipcOnly` modes available for advanced users. `BrowserScriptBridge` now dispatches every VS Code call through the selector — no other code site touches either bridge directly.
  - **Settings → VS Code** now leads with the WOS Tab Bridge extension as *Option 1 (recommended)*, with **Install Extension** and **Show .vsix in Finder** buttons. The Install button locates `code` in `/usr/local/bin`, `/opt/homebrew/bin`, `~/.local/bin`, and the VS Code bundle itself, then runs `code --install-extension <bundled.vsix> --force` via `NSTask`. Falls back to clear instructions if `code` isn't installed yet. Legacy Accessibility path is preserved as *Option 2*.
  - Bundled `wos-tab-bridge.vsix` ships as a WOS.app Resource so the Install button works offline.
  - Live status line: *"Status: WOS Tab Bridge extension connected to N VS Code windows — IPC path active."* (or the AX-based equivalent when only that path is on).
- JSON protocol (newline-delimited): ext → WOS `{"type":"hello",pid,window,version}` + `{"type":"tabs",window,tabs:[{id,label,uri,group,active,dirty,pinned,preview}]}`; WOS → ext `{"type":"focus",tabId}`.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline.

---

## 0.1.974 (2026-05-19)

### Added
- **VS Code tab switching, Phase 1 — Accessibility tab bridge.** VS Code ships no AppleScript dictionary, so the existing browser-tab enumeration path couldn't see its tabs. New [`AccessibilityTabBridge`](Sources/SwitchCore/AccessibilityTabBridge.swift) reads the tab strip from VS Code's AX tree (Chromium publishes it as HTML buttons with `role=tab`) and feeds the entries into the standard cycle alongside browser tabs.
  - New `BrowserKind.vscode` case (`com.microsoft.VSCode`). `usesAccessibilityEnumeration` toggles the dispatch path. Window enumeration always works; tab enumeration requires `editor.accessibilitySupport: "on"` in VS Code (default is `auto`, which only publishes the tab strip when a screen reader is detected).
  - Stable per-tab IDs are synthesised from `(window enumeration index, tab enumeration index)` — VS Code doesn't expose stable identifiers, and the title-based reconciliation in `HistoryReconciler` already handles drift.
  - Activation via `AXUIElementPerformAction(_, kAXPressAction)` on the resolved tab.
  - **Settings → VS Code** tab: explains the one-time `editor.accessibilitySupport` change, shows live tab-strip visibility status, and includes an *"Open VS Code Settings (Accessibility Support)"* deep-link via the `vscode://settings/<id>` URI scheme.
  - Power-user tip surfaced in the same tab: any `vscode://` URI on the clipboard opens via Jump-to-File (default `⌘⇧G`) — works for `vscode://settings`, `vscode://file/...`, `vscode://extension/...`, and is symmetric to `cursor://`, `raycast://`, `slack://`, etc.
  - README has a *"VS Code tabs — one-time setup"* section covering both the Settings UI and `settings.json` paths.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline.

---

## 0.1.968 (2026-05-19)

### Added
- **macOS Secure Input is no longer a silent "Cmd+Tab doesn't work" mystery.** When the kernel turns on Secure Input — because a password field is focused (1Password's master-password prompt, Touch ID, sudo, FileVault, browser login), or Terminal/iTerm has *"Secure Keyboard Entry"* enabled — every CGEvent tap on the system is bypassed and Cmd+Tab falls through to the native macOS switcher. WOS could not surface this beyond a buried menubar line. Now three surfaces light up simultaneously:
  - **Persistent corner overlay** ([`SecureInputOverlayController.swift`](Sources/WOS/SecureInputOverlayController.swift)) at the top-right of the active screen — *"Cmd+Tab is blocked. Secure Input is active in 1Password."* Stays visible the entire time Secure Input is on (no auto-dismiss timer) and includes a clickable mini-switcher of recent apps. Cmd+Tab is dead during Secure Input, but `NSRunningApplication.activate` is not — the mini-switcher gives the user a working app-switch path while the password prompt is up.
  - **Menubar warning** rewritten with the culprit name: *"SECURE INPUT ACTIVE — Cmd+Tab is blocked"* + *"Held by 1Password"* / *"Likely held by Safari"* + a recovery hint. The status item icon swaps to `lock.circle.fill` while active.
  - **Unified-log breadcrumb** at notice level: `Secure Input ACTIVATED — likely held by <App> (confidence=<level>, evidence=<reason>)` and a matching `RELEASED after Xs` line. Capture with `log show --predicate 'subsystem == "app.wos"' --info --last 1h | grep "Secure Input"`.
  - **Culprit identification** ([`SecureInputObserver.swift`](Sources/WOS/SecureInputObserver.swift)) uses a three-tier heuristic ladder: (1) AX focused-element scan for `AXSecureTextField` role → *confirmed*; (2) AppleScript probe of Terminal/iTerm `secure keyboard entry of front window` with a 250 ms budget → *confirmed*; (3) frontmost-app-at-moment-of-activation → *likely*. The overlay labels its identification with the qualifier so we never assert when we are guessing.
  - **Centralized observer**: replaces `StatusItemController`'s former 2 s self-poll with a single 1 Hz observer that feeds the menubar, the overlay, and the log together. One timer, one source of truth.
  - Documentation: [KNOWNISSUES.md](KNOWNISSUES.md) → *"macOS Secure Input Blocks Cmd+Tab"*, [README.md](README.md) → *"Troubleshooting"*, [ARCHITECTURE.md §9.1](ARCHITECTURE.md) → *"Secure Input Subsystem"*.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline.

---

## 0.1.966 (2026-05-18)

### Added
- **"Browser tab switch slow" non-blocking warning pill.** When a Cmd+Tab commit into a browser tab takes more than ~2× the rolling per-browser median (and above an absolute 350 ms floor), a small glassmorphic HUD pill — `tortoise.fill` + amber tint, anchored to the just-shown Cmd+Tab HUD — fades in with the latency reading: *"Google Chrome tab switch slow · 740 ms · ≈3.2× usual"*. Lives 2.6 s, fades out, never blocks input. The threshold uses a multiplier rather than a fixed ms ceiling so the warning auto-calms once today's slow browser becomes the new median — no point nagging when nothing the user can do will help. Per-browser 8 s cooldown prevents burst-spamming during sustained slowdowns. Cycle-only (preview hovers are excluded so the median isn't poisoned). New [`BrowserActivationLatencyMonitor.swift`](Sources/WOS/BrowserActivationLatencyMonitor.swift) does the threshold math, [`BrowserSlowdownPillController.swift`](Sources/WOS/BrowserSlowdownPillController.swift) renders the pill. Defaults to on; toggle lives in Settings → Debug → *"Warn when browser switching is slow"*. The pill distinguishes "WOS got slower" from "the host browser's AppleScript engine got slower today" — the latter being the most common complaint we previously had no way to communicate.

### Fixed
- **Menubar status item could fail to appear when LaunchServices is degraded.** `StatusItemController.rebuildMenu()` called `historyProvider()` synchronously on the main thread, which fan-outs to `pruneUnavailableHistoryTargets` → `NSRunningApplication.bundleIdentifier` → `_LSCopyApplicationInformation` XPC for every history entry. With a 38-entry history and a system-wide LaunchServices slowdown (the same degradation that motivated the slowdown-pill feature above), each rebuild took >1 s. The 1 Hz accessibility-watchdog called `setHotkeyStatus`, which called `rebuildMenu`, which re-fired *before the previous rebuild finished* — locking the main runloop in a self-reinforcing wedge so the `NSStatusItem` was allocated but never painted to the menubar. Three changes break the loop:
  - `setHotkeyStatus(isEnabled:statusText:)` now mutates the existing menu's title and toggle items in place (O(1) string assignment) instead of rebuilding the whole menu — kills the per-tick storm.
  - `rebuildMenu()` builds the chrome synchronously with a *"Loading recent…"* placeholder, then schedules the `historyProvider()` call as `DispatchQueue.main.async`. The status item gets a runloop tick to paint before the heavy LaunchServices work runs — even fully-frozen LaunchServices no longer prevents the menubar icon from appearing.
  - `menuWillOpen(_:)` switched from a blocking rebuild to an async refresh, so opening the menu is instant; rows refresh in-place within a tick. A `historyRefreshGeneration` counter discards stale async refreshes that land after a full rebuild.
  - See [LESSONSLEARNED.md](LESSONSLEARNED.md) → *"Watchdog timers + heavy main-thread work = self-wedging UI"* for the full forensic write-up.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline.

---

## 0.1.965 (2026-05-16)

### Changed
- **Jump-to-File (Cmd+Shift+G) now also opens web URLs.** Previously, the hotkey only recognized `/...` and `~/...` file paths and the UI-path catalog (System Settings panes, app menus). When the selection or clipboard held a URL — common when reading docs, chat transcripts, or terminal output — the resolver fell through to the "treat raw clipboard string as a file path" branch and surfaced confusing toasts like *"Jump failed: not found — /git push"* (the clipboard string being whatever the user had copied last, not the URL on screen). New [`SelectionUtilities.detectURL(in:)`](Sources/WOS/SelectionUtilities.swift) uses `NSDataDetector(.link)` so it catches both schemed URLs (`https://github.com/owner/repo`) and bare domains (`foo.workers.dev`, `apple.com/mac`); `file://` URLs are skipped so the existing file-path branch still wins for local files. Detection runs against both the AX/copy selection and the pasteboard fallback. The hotkey's precedence is now: UI path → file path → web URL → raw-string path fallthrough. URLs open via `NSWorkspace.shared.open`, which goes through LaunchServices (sandbox-safe under the `(allow lsopen)` rule added in 0.1.964) and hands the URL to the user's default browser; an *"Opened &lt;host&gt;"* status HUD with the `link` SF Symbol confirms the action. See [ARCHITECTURE.md §12.4](ARCHITECTURE.md) for the precedence-tier rationale and [MANUALTESTING.md](MANUALTESTING.md) for the URL-recognition spot-checks table.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline. Notary submission `0aa2f224-5ee8-4bf2-87b4-95dd9d9a7cfa` — *Accepted*. `xcrun stapler staple` and `spctl --assess --type install` both pass (`source=Notarized Developer ID`). First-launch UX remains the standard one-click *"Open"* dialog.

---

## 0.1.964 (2026-05-13)

### Fixed
- **Quick Search "App Launcher" can now cold-launch installed apps.** Pressing Enter on a non-running app (e.g. *Front*, *Google Chrome Canary*) silently failed with the on-screen error *"Couldn't launch X: The application could not be launched because a miscellaneous error occurred."* Root cause: the runtime Seatbelt profile in [`main.swift`](Sources/WOS/main.swift) starts with `(deny default)` and never allowed `lsopen` — the LaunchServices IPC that `NSWorkspace.openApplication` and `NSWorkspace.open(URL)` both call. macOS's NSError message named the exact missing primitive ("missing `(allow lsopen)`"). Activating *already-running* apps via `NSRunningApplication.activate()` uses different IPC, which is why "Front works once it's running but Cmd+Tab+S can't cold-launch it" was the precise symptom. The new `(allow lsopen)` rule is the narrowest primitive Apple's sandbox profile language offers for this capability — `(deny network*)`, Gatekeeper, notarization, and sandbox non-inheritance to children are all unchanged. See [SECURITY.md](SECURITY.md) → *"Sandbox Rule Audit — `(allow lsopen)`"* for the full threat-model delta and [ARCHITECTURE.md §10](ARCHITECTURE.md) for the standing policy on future sandbox additions.
- **App-launch hardening (the other half of the cold-launch fix).** Three independent latent bugs uncovered while diagnosing the sandbox denial, all fixed in [`AppDelegate.swift`](Sources/WOS/AppDelegate.swift) → `launchAppFromSearch`:
  - `runningApplications(withBundleIdentifier:).first` could pick a window-less helper process over the regular activation-policy process; `activate()` then succeeded silently. Switched to the existing `pickActivationTarget()` helper that prefers `.regular`.
  - `activate()`'s return value was discarded; `false` now falls through to `NSWorkspace.openApplication`, which delivers a reopen Apple Event that typically unhides or recreates the main window.
  - When `openApplication(at:configuration:)` errors, we now fall back to the older `NSWorkspace.open(URL)` — the same entry point Finder uses on double-click. It sometimes succeeds where the newer API returns the generic "miscellaneous error" wrapper.
- **Crash on Jump-to-File hotkey when two running apps normalize to the same name.** `Dictionary(uniqueKeysWithValues:)` in [`UIPathResolver.swift:53`](Sources/SwitchCore/UIPathResolver.swift) traps on duplicate keys — and sibling app variants, identical display names from different bundles, and helper processes can collide. Reproduced in a real `EXC_BREAKPOINT` crash report. Switched to `Dictionary(_:uniquingKeysWith:)` (first-occurrence wins). Both keys map to "an app with this name" and the resolver only uses the value as a display string, so either choice is semantically equivalent.
- **Launch failures are no longer log-only.** `launchAppFromSearch` now surfaces both error paths (`urlForApplication` returning nil; `openApplication` returning an NSError) via a beep + status HUD message with auto-hide after 5s. The structured error log now captures the NSError `domain`, `code`, and `userInfo` so future sandbox-denied primitives can be identified from a single line in `log show --predicate 'process == "WOS"'`.

### Changed
- **Recent Files engine idle CPU dropped from 50–65% to ~0%.** `processSpotlightResults` previously re-scanned the entire NSMetadataQuery result set (500 items × 6 synchronous XPC roundtrips to `mds`) on every `NSMetadataQueryDidUpdate` notification — and Spotlight fires DidUpdate several times per second when browser caches, IDE artifacts, and `~/.cache` churn. With `mds` itself already heavily loaded, we were piling on. Three independent fixes stack:
  - A 2-second leading-edge throttle on `queryDidUpdate`. The result set is live, so dropped updates are caught by the next one — no trailing call needed.
  - Delta processing via `NSMetadataQueryUpdateAddedItemsKey` / `NSMetadataQueryUpdateChangedItemsKey`. Process only the items the notification flags as added/changed (typically 1–10) instead of the full 500.
  - Per-item XPC work extracted into `extractRecentFile(from:allowList:)` so the full-rescan path (`DidFinishGathering`, runs once on initial gather) and the delta path (`DidUpdate`) share the same per-item logic.

### Added
- **Settings → Recent Files → "Scan Documents, Downloads, Desktop Only" toggle.** Narrows both the Spotlight `searchScopes` and the FSEvents watch root from `NSMetadataQueryUserHomeScope` / `$HOME` to just `~/Documents`, `~/Downloads`, and `~/Desktop`. Skips per-second churn from `~/Library`, browser caches, and IDE build artifacts — the noise that drove the Spotlight DidUpdate storm. Defaults to `true` for fresh installs (the CPU-safe default after the bug above); existing saved settings decode the missing field as `false` so an upgrade does not silently narrow an existing user's index. Flipping the toggle rebuilds both the Spotlight query and the FSEvents stream cleanly.

### Maintenance
- This release ships through the established Developer ID + notarization pipeline. Notary submission `0f06edd9-17d3-4b48-9515-f65574c10074` — *Accepted*. `xcrun stapler validate` and `spctl --assess --type install` both pass (`source=Notarized Developer ID`). The new `(allow lsopen)` Seatbelt rule is documented in [SECURITY.md](SECURITY.md), [ARCHITECTURE.md](ARCHITECTURE.md), and [website/security.html](website/security.html) — the published kernel-sandbox profile listing now includes the new rule with an inline comment, and a "A note on `(allow lsopen)`" explainer card sits directly below the profile block.

---

## 0.1.955 (2026-05-12)

### Added
- **Easter egg: `somebody` → *"Somebody's gonna get hurt real bad. I'm not saying who. Somebody."*** A Russell Peters reference in the Cmd+Tab+S search panel. Joins the text-reveal catalogue with an `exclamationmark.triangle` SF Symbol and counts toward the `wos secrets` discovery tally. See [EASTEREGG.md](EASTEREGG.md).

### Changed
- Maintenance: this release is the first Easter-egg addition built and shipped through the new Developer ID + notarization pipeline introduced in 0.1.954. Notary submission `525c2c18-773f-4eea-8057-e4fc42907886` — *Accepted*. First-launch UX remains the standard one-click "Open" dialog.

---

## 0.1.954 (2026-05-12)

### Changed
- **Distribution is now signed with Apple Developer ID and notarized.** Previous releases shipped ad-hoc-signed (`source=Unnotarized Developer ID`), which forced users through *Privacy & Security → "Open Anyway"* on first launch. The DMG is now signed with `Developer ID Application: CYP Europe GmbH (579VUWVTXN)`, submitted to Apple's notary service, and the notarization ticket is stapled. `spctl --assess --type install` reports `source=Notarized Developer ID` and `xcrun stapler validate` passes. First-launch UX is now the standard one-click *"WOS is an app downloaded from the Internet — Open?"* dialog, with no System Settings detour. Notary submission `54526365-bebb-41f9-9d7f-d1270e49183b` — *Accepted*. See `LESSONSLEARNED.md` → "Developer ID Signing & Notarization Wiring".

---

## 0.1.953 (2026-05-12)

### Added
- **Easter egg: `vedio` → *"Did you mean: Video? ;)"*.** A gentle spelling-correction nudge for one of the internet's most reliable typos. Joins the existing text-reveal catalogue in the Cmd+Tab+S search panel; rendered in the floating HUD lightbox with a `questionmark.bubble` SF Symbol, counts toward the `wos secrets` discovery tally. See [EASTEREGG.md](EASTEREGG.md).

---

## 0.1.952 (2026-05-11)

### Fixed
- **Easter-egg lightbox no longer persists into a fresh Cmd+Tab+S invocation.** Previously, typing a trigger phrase like `who am i`, then dismissing the panel with Esc, then re-invoking the search panel left the floating HUD banner ("It's you. It's always you.") drawn over a freshly-empty search field. Cause: the lightbox was attached as a child window of the WOS panel via `addChildWindow(_:ordered:.above)`. `closePanel()` called `window?.orderOut(nil)` *before* `EasterEggLightboxController.shared.hide()`, which flipped the lightbox child's `isVisible` to `false` via AppKit's parent-child auto-hide. `hide()`'s `guard panel.isVisible else { return }` then returned early without ever calling `removeChildWindow(_:)` — the lightbox stayed in the WOS panel's child list, so the next `makeKeyAndOrderFront` revived it with alpha 1.0 alongside the empty results. Fix in [EasterEggLightboxController.swift:189](Sources/WOS/EasterEggLightboxController.swift:189): always sever the parent-child link at the top of `hide()`, before the visibility guard. The animated fade still runs when the lightbox is on screen (e.g. when the user changes their query mid-session); during panel teardown it just unparents and orders out. See `LESSONSLEARNED.md` → "Child Windows Survive Parent's orderOut".

---

## 0.1.951 (2026-05-11)

### Changed
- **Maintenance rebuild.** No source changes since 0.1.950 — clean from-scratch build with proper bundle structure: `Identifier=com.agentic.wos`, sealed `Info.plist`, hardened-runtime flag (`adhoc,runtime`). The 0.1.950 binary previously published from this worktree had a malformed signature (`Identifier=WOS`, `Info.plist=not bound`, `adhoc,linker-signed`) caused by a codesign step that fell back to ad-hoc when the signing keychain was locked mid-build. Users on 0.1.950 (this worktree's build) may need to re-grant Accessibility and per-browser Automation in System Settings → Privacy & Security after updating, because TCC keys grants to the client's signature requirement and the new bundle has a different one.

## 0.1.950 (2026-05-11)

### Added
- **Easter-egg system in the Cmd+Tab+S search panel.** Typing one of the recognized trigger phrases reveals a floating HUD-style lightbox above the panel and, for some triggers, plays a one-shot animation on the panel itself. The lightbox is attached as a child window of the WOS panel via `addChildWindow(_:ordered: .above)` — the deterministic AppKit API for "always draw above," independent of window-level / activation state. Icons are monochrome SF Symbols in `labelColor`. See [EasterEggLightboxController.swift](Sources/WOS/EasterEggLightboxController.swift) and `dispatchEasterEgg()` in [QuickSearchPanelController.swift](Sources/WOS/QuickSearchPanelController.swift) for the dispatcher; full catalogue + implementation notes in [EASTEREGG.md](EASTEREGG.md).
- **Text reveals:** `show wos version` (also pins a subtle version chip to the top-right of the search bar), `open the pod bay doors`, `42` / `meaning of life`, `hello there`, `how do i exit vim`, `i am groot`, `the cake is a lie` (rendered with real strikethrough), `i'm a teapot`, `rm -rf /`, `sudo make me a sandwich` vs. `make me a sandwich` (xkcd #149), `chmod 777`, `git blame`.
- **Self-referential:** `who am i` returns *"You are {NSFullUserName}, keeper of N open tabs."* — N is the live tab count from the result set. `show wos credits`. `wos secrets` returns the unique-discovered count, persisted in `UserDefaults[WOSEasterEggsDiscovered]`.
- **One-shot animations:** `do a barrel roll` (360° `CALayer` rotation around the panel's geometric centre — anchor temporarily re-centred and restored), `matrix` / `the matrix` (transient green tint overlay for ~2.5 s), `thanos` / `snap` (alternate visible result rows fade to alpha 0 then return), `konami` / `up up down down` (4-pt rainbow border pulses around the panel for ~2 s via a transient `CALayer`). Animations only fire when the normalized trigger phrase actually changes.

### Changed
- `scripts/build_release.sh` honours a new `SWIFT_BUILD_JOBS` env var (`-j N`) so the release build can run with reduced parallelism on memory-constrained machines.

---

## 0.1.938 (2026-05-11)

### Changed
- **Comprehensive source code documentation pass.** No behaviour changes; this release improves maintainability and onboarding:
  - `captureFrontmostBrowserTabIfNeeded` — new function-level doc comment covering coalescing design, stale-capture guard, window-mark dedup, and the tab-drag / index-drift detection mechanism.
  - `refreshOpenTabCacheAsync` — full doc comment covering coalescing flag, adaptive backoff, non-authoritative snapshot guard, and all standard `reason` labels.
  - `confirmBrowserTabActivation` — doc comment explaining retry rationale, synchronous queue constraint, and why URL staleness cannot be cured by retrying.
  - `scheduleBrowserTabActivationVerification` — doc comment covering the 40 ms timing guarantee, token-guard design, tristate outcome table, and the `.unknown` fallback invariant.
  - `HistoryReconciler.tabMatchKeys` — expanded doc comment explaining the key-prefix design, the empty-set contract, and why the function is `public` (cross-module drift detection in AppDelegate).
  - `HistoryReconciler.windowBucketKey` — doc comment explaining the key format, Chromium composite-identifier stripping, and the `public` visibility justification.
  - `BrowserTabMatchEvaluator` — expanded class doc comment with SPA URL drift explanation and cross-profile Chrome `.unknown` rationale.
  - `ARCHITECTURE.md` — new "captureFrontmostBrowserTabIfNeeded — design invariants" and "Tab-drag / index-drift detection" subsections under §3 (Foreground Observation), documenting the full call-site inventory, coalescing, stale-capture guard, window-mark dedup, the drift-detection predicate, and why eager single-slot eviction fails.

---

## 0.1.937 (2026-05-11)

### Fixed
- **Safari tab drag no longer corrupts Cmd+Tab history for other tabs in the same window.** The 0.1.936 fix used an eager single-slot eviction approach: before writing the dragged tab's new stableID, WOS removed any same-window, same-content entry at a stale index. This had collateral damage — when another tab (e.g. SabeeApp) had *also* shifted to a new index position, its slot was evicted and it disappeared from Cmd+Tab until the next background reconcile cycle. The corrected approach detects index drift (incoming tab's content matches an existing entry whose stableID differs, same window) and immediately fires `refreshOpenTabCacheAsync` to re-enumerate the entire window atomically via `HistoryReconciler`, which handles all shifted positions in a single pass rather than piecemeal at write time. No correctness difference when no drag has occurred; the accelerated reconcile fires only when drift is detected, and is throttled by the same 5-second `chromiumAXRefreshThrottleSeconds` guard.

---

## 0.1.936 (2026-05-11)

### Fixed
- **Dragging a Safari (or Firefox / iTerm / Terminal) tab no longer creates a duplicate entry in the Cmd+Tab HUD.** For index-based browsers, `stableID` encodes the tab's position (`tab::<browser>::<windowID>::<tabIndex>`), so moving a tab to a new slot changed its stableID and caused `markSeen` to create a second history entry while the old one persisted. Chromium-family browsers were unaffected because they use a persistent GUID as stableID. Fix: before `markSeen`, `captureFrontmostBrowserTabIfNeeded` now evicts any existing same-window, same-content entry whose stableID differs from the incoming tab's — the same matching logic `HistoryReconciler` uses, applied eagerly at write time. `HistoryReconciler.tabMatchKeys` and `windowBucketKey` promoted to `public` to support the call site. See `LESSONSLEARNED.md` → "Index-Based stableID Creates Duplicate HUD Entries on Tab Drag".

## 0.1.935 (2026-05-11)

### Fixed
- **Post-commit verification no longer fires a redundant `activateWindow` fallback for SPA browser tabs.** `BrowserTabMatchEvaluator` previously returned `.differs` on any URL mismatch without trying the title. For single-page apps (Gmail, Linear, SabeeApp, Notion, etc.) the LRS entry holds the URL from scan time; by commit time the tab has navigated freely, leaving the stored URL stale while the title stays constant. When URL comparison fails and the target has a non-empty title, the evaluator now checks whether the front tab's title also matches before returning `.differs`. The window-ID guard checked earlier in the same function already prevents a cross-window false-positive from this path. Every SPA tab in any supported browser benefits — not just Safari. See `LESSONSLEARNED.md` → "Post-Commit Verification Mismatch for SPA Tabs — Stale Stored URL".

### Added
- **Recent Files executable-launch security gate.** Previously, any LaunchServices-runnable file in Recent Files (`.sh`, `.command`, `.app`, files with the `+x` bit, etc.) was one Return key away from executing under the user's account — macOS's "OK to run …?" dialog was the only thing standing in the way, and it's a single-click bypass. Three new layers, all on by default:
  1. **Detection** — new [`RecentFilesExecutableGuard`](Sources/WOS/RecentFilesExecutableGuard.swift) flags files via an extension allowlist (shells, app bundles, scripting runtimes, Automator, Java, Windows runners macOS will still hand off — full list in source) plus a `stat()` probe of `S_IXUSR` so chmodded scripts with no extension are also caught. Restricted to `S_IFREG` to avoid false-positiving directories.
  2. **Open-time refusal** — when `RecentFilesSettings.allowExecutableLaunch` is `false` (default), the HUD's `onOpenFile` callback in [`AppDelegate`](Sources/WOS/AppDelegate.swift:780) replaces the Apple-Events `open` with `NSWorkspace.activateFileViewerSelecting([url])`. Finder reveals the file; LaunchServices is never asked. A `lock.shield` toast tells the user how to enable launching.
  3. **Optional index hiding** — new `hideExecutablesFromIndex` setting (default `false`) filters executables out of all three discovery paths in [`RecentFilesEngine`](Sources/WOS/RecentFilesEngine.swift) (Spotlight, FSEvents, AX window-title) so they never surface in the HUD at all.
- **Admin-auth gate on the `allowExecutableLaunch` toggle.** Flipping the setting in **either direction** requires a fresh admin password. Implementation in [`AdminAuthGate`](Sources/WOS/AdminAuthGate.swift) uses Authorization Services with a private right `com.agentic.wos.recent-files.allow-executable-launch`, registered on first use with rule `class=user, group=admin, timeout=0, shared=false`. The `timeout=0` is load-bearing — the built-in `system.privilege.admin` caches a successful auth for ~5 minutes system-wide, which would let an attacker piggy-back on a legitimate admin auth elsewhere; our private right re-prompts every time. Bootstrap on first call piggy-backs on the system `config.add` right (one dialog handles both registering the right and gating the action that triggered it). `AuthorizationFree(authRef, .destroyRights)` after each call ensures successful auth here doesn't pre-authorize anything else later in the process. Verified to build clean under both `swift build` and `WOS_MAS=1 swift build` — the gate works in the sandboxed MAS variant via XPC to `authd`.
- **Settings UI: new "Security" group in the Recent Files tab** ([SettingsPanelController.swift](Sources/WOS/SettingsPanelController.swift)) holding the two new toggles. The admin-gated toggle's handler captures intended vs. persisted state, runs `AdminAuthGate.authenticate(...)`, and on cancel/wrong-password reverts the visible toggle to the persisted value (the persisted value never changed, so no actual policy change occurred).
- **Threat-model coverage:** malware-as-user can't flip the toggle programmatically (synthetic UI events block on the auth dialog); can't piggy-back on a recent admin auth elsewhere (`timeout=0`); can't flip it on, run a script, and quietly flip it back (both directions are gated). Jump-to-File is deliberately not gated — the user types those paths themselves with explicit input.
- **Schema upgrade safety.** `RecentFilesSettings` now decodes the two new fields via `decodeIfPresent` so older v1 payloads upgrade to the secure defaults rather than failing to decode and resetting all settings.
- Documentation: full design rationale in [ARCHITECTURE.md §13.13](ARCHITECTURE.md), threat-model summary in [SECURITY.md §4](SECURITY.md).

## 0.1.934 (2026-05-11)

### Fixed
- **Snap-back bug killed at the source: stripped `activate` from every commit-time tab-activation AppleScript.** The recurring "Cmd+Tab → focus drifts back to Chrome ~250 ms later" symptom (documented in `LESSONSLEARNED.md` under "The Snap-Back Trap" and revisited many times) was caused by the trailing `activate` AppleScript verb in `chromiumActivateTabScripts` (and the equivalent Safari / iTerm / Terminal / Firefox / Arc / Orion tab paths). `AppleScriptEngine.run` checks `shouldContinue()` once before compile + execute; once the script starts, NSAppleScript has no cancellation API. When a Chrome tab commit's AppleScript drained late (queue backed up behind enumeration scans) AFTER the user had cycled away to e.g. Claude, the trailing `activate` ran to completion and pulled Chrome back to the foreground — overriding the user's actual commit. Foregrounding is now exclusively the responsibility of the synchronous `app.activate(...)` call at `AppDelegate.activate(_:)` plus the existing `scheduleBrowserTabActivationVerification` fallback at +40 ms (which has its own token guard and skips for superseded commits). Window-activation scripts keep their `activate` line — those are the explicit "bring this window to the foreground" path, only invoked from the verification fallback under proper token-checked conditions. 12 tab-script `activate` calls deleted in total; 11 window-script `activate` calls preserved.
- **Cloudflare WARP (and other LSUIElement apps) sometimes opened Chrome's "Bookmarks" menu instead of revealing their own status item.** `MenuBarExtrasService.fetchItemsViaLegacyAX` iterates every child of an app's `AXExtrasMenuBar` and treats it as a clickable status item titled with the app's name. In practice that container can hold non-status-item children (`AXGroup`, `AXLayoutArea`, decorative elements) whose frame happens to sit in the **left** half of the menu bar — where the frontmost app's regular menus live. Pressing one of those targets via AXPress reported `success=true` but routed the press to the wrong UI element, and the synthetic-click fallback at the same coordinates triggered the frontmost app's menu (observed: WARP commit → Chrome's Bookmarks menu opens). Added a deny-list role filter (`AXGroup`, `AXLayoutArea`, `AXImage`, `AXSplitter`, `AXUnknown`, `AXStaticText`, `AXScrollArea`) so only actual status-item AX elements survive into the pressable set. Forensic logging on every press (`menu-bar press start: id=… center=… frontmost=…`) so this class of bug is debuggable from the log stream.
- **Settings panel width regression: panel opened at ~1700pt instead of 715pt.** PR #19 in 0.1.917 had relaxed `panel.maxSize` so users could resize horizontally, but AppKit's auto-layout content fitting overrode that on initial show — any required-priority widget in any (even hidden) section grew the window past 715pt. Reverted to fixed-width 715pt with vertical resize, added a `widthAnchor = 715` constraint at required priority on the panel's content view, and lowered horizontal compression resistance on `SettingsRow` labels so long subtitles truncate within the row instead of widening it. `present()` always normalizes the panel back to 715 × computed-height regardless of any leaked frame state from prior sessions.

### Added
- **Settings → Debug → "Snap-back detector"** (currently default-on for the active debug push). Ring buffer of the last 80 activation events + watchdog at 250 / 750 / 1500 ms after every cycle commit; dumps the buffer at error level when frontmost diverges from the committed target with no superseding activation in flight. Captured the smoking-gun snap-back forensic for the AppleScript `activate` race in `0.1.929` — see the fix above. To find dumps: `log show --predicate 'subsystem == "com.agentic.wos"' --info | grep "SNAP-BACK"`.
- **"WOS" appears in Cmd+Tab+S search** as a synthetic entry that opens Settings. The running WOS itself never appears in LRS history (workspace observer skips own bundle) and is filtered out of `launcherApps` by the same-display-name guard (PR #17), so typing "WOS" or "settings" used to return nothing. New `SearchSelection.openSettings` case wires the entry to `openSettingsPanel()`. WOS-MAS and WOS.BKP siblings stay hidden by the same-display-name filter.
- **LSUIElement reveal in the Cmd+Tab cycle commit path.** Quick-Search Enter (0.1.914) already handled menu-bar-only apps; cycle commits to such apps now do the same via `revealMenuBarItem`. (Shipped in 0.1.924, expanded here.)

### Tooling
- **`scripts/CLAUDE_LIVE_MONITOR.md`** — workflow doc for the live-monitor debug pattern: user reproduces by hand, Claude tails the WOS subsystem log via a persistent `Monitor` task, surfaces snap-backs and anomalies, only speaks up on signal. Trigger phrase is "live monitor" plus a hint at what's being hunted. Pointer added in `agent.md` under Observability Requirements so future sessions discover it.

### Privacy / readability
- **`Committing switch to <target>`** log line now uses `, privacy: .public` so the target name surfaces in `log stream` instead of `<private>`.
- **Diagnostic logging on `MenuBarExtrasService.press`** captures the cached AX-element status, click coordinates, and frontmost-app context for every menu-bar press attempt. Made it possible to identify the wrong-target bug above from a single repro.

## 0.1.924 (2026-05-10)

### Fixed
- **Settings panel was opening at ~1700pt wide instead of the intended 715pt.** PR #19 (0.1.917) had switched the panel from a fixed-width 840pt window to a "default 715pt, user-resizable between 640 and 1400pt" layout, but `NSWindow.maxSize` only constrains user drag-resize — it does **not** prevent AppKit from growing the window to satisfy content's intrinsic / required-priority constraints. Section views are siblings under `contentContainer` with active leading/trailing constraints even when hidden, so any one section's widest widget (e.g. a long subtitle in `SettingsRow` with default `.required` compression resistance) silently widened the panel past 715pt to fit ALL sections. Reverted to a fixed-width 715pt design (vertical resize still works), added an explicit `widthAnchor = 715` constraint at required priority on the panel's content view to enforce the budget at the layout level, lowered horizontal compression resistance on `SettingsRow` title/subtitle labels so long descriptions truncate (or wrap to the configured 2 lines) within the row instead of widening it, and made `present()` always normalize the panel back to 715 × computed-height regardless of any leaked frame state from prior sessions.

### Added
- **Settings → Debug → "Snap-back detector"** (currently default-on, will flip to default-off in a future release after the active debug push). Forensic watchdog for the recurring "Cmd+Tab snap-back" symptom (focus drifts back to a previous app, typically a Chrome tab, ~250–1500 ms after a commit). Records a ring buffer of the last 80 activation events (commits, token bumps, manual switches, AppleScript completions, fallback fires) plus arms a watchdog at 250 / 750 / 1500 ms after every cycle commit; if the frontmost app diverges from the committed target with no superseding activation and no new cycle in flight, dumps the ring buffer to Console at error level (search "SNAP-BACK DETECTED"). Captured the first reproducible snap-back forensic in this build (Chrome tab commit followed by Claude commit → Chrome reactivated 257 ms later via late-draining commit-time AppleScript with `activate` keyword); root cause identified, fix queued for a follow-up.
- **LSUIElement reveal in the Cmd+Tab cycle commit path.** Quick-Search Enter (0.1.914) already handled menu-bar-only apps like Cloudflare WARP correctly; cycle commits to such apps fell through to plain `app.activate()` which has no visible effect when the app has no main window. Cycle commits now detect `activationPolicy != .regular` for running apps (and the `isMenuBarOnly` flag from the launchable-apps snapshot for not-running ones) and route to the existing `revealMenuBarItem` machinery — clicking the status item the same way Quick-Search does.

### Tooling
- **`scripts/CLAUDE_LIVE_MONITOR.md`** — workflow doc for the live-monitor debug pattern: user reproduces by hand, Claude tails the WOS subsystem log via a persistent `Monitor` task, surfaces snap-backs and anomalies, only speaks up on signal. Trigger phrase is "live monitor" plus a hint at what's being hunted. Pointer added in `agent.md` under Observability Requirements so future sessions discover it.

### Fixed (privacy / readability)
- **`Committing switch to <target>` log line was redacted to `<private>`** in `log stream` because the interpolated value lacked the `, privacy: .public` annotation, blocking forensic analysis of the snap-back. Now public.

## 0.1.916 (2026-05-08)

### Tooling
- **`scripts/dev_reset.sh`** — restores the developer-convenience pattern that used to live as a personal `wos-dev-reset` shell alias. Kills any running WOS, runs `tccutil reset` for every TCC service the app holds (`Accessibility`, `ListenEvent`, `AppleEvents`, `ScreenCapture`, `PostEvent`), and relaunches `/Applications/WOS.app`. Useful for validating first-run permission-prompt UX after re-signing or after the permission state has gotten weird. Wire it up by adding `alias wos-dev-reset='bash /Users/.../wosapp/scripts/dev_reset.sh'` to `~/.zshrc`.

### Documentation
- **`LESSONSLEARNED.md` gains two new entries** covering the recent fixes — the full root-cause + rule-for-future-agents writeup for both:
  - "Cmd+W Optimistic Prune Targeted The Wrong Tab Because `fetchFrontTab` Raced The Close" — why the in-memory LRS history is the right source of truth at Cmd+W time, and why a synchronous AppleScript query against a forwarded-event target races the event you just forwarded.
  - "LSUIElement Apps Looked Broken in Quick Search Because `openApplication()` Has No Visible Effect" — why menu-bar-only utilities need a status-item-click activation path instead of a launch path, the Info.plist-encoding gotcha (`Bool` vs `NSNumber.boolValue`), and why the launcher-section copy needs to be honest about what Enter will produce.
- **`ARCHITECTURE.md` gains §6.5 (Quick Search Activation: Launchable Apps and Menu-Bar-Only Reveal)** documenting the activation-table split between regular apps and `LSUIElement` apps, the three-state poll/press logic of `revealMenuBarItem`, and a list of common-target apps. Existing Cmd+W path documentation (under §2 Recency Engine) is extended with the history-first invariant and a pointer to the lessons-learned writeup.

---

## 0.1.915 (2026-05-08)

### Fixed
- **Cmd+W race: closed tab persisted in Cmd+Tab cycle for ~30 s instead of disappearing immediately.** [`handleBrowserCloseShortcutDetected`](Sources/WOS/AppDelegate.swift) called `scriptBridge.fetchFrontTab(for: browser)` first to identify which tab to optimistically prune from history — but by the time the handler ran, the event tap had already forwarded Cmd+W to the browser, and the browser had already moved focus to the sibling tab. `fetchFrontTab` therefore returned the *wrong* tab (the one that became frontmost *after* the close), so the wrong entry was pruned and the actually-closed tab stayed in history until the periodic background reconciler caught it ~30 s later. Inverted the lookup to **history-first, AppleScript as last-resort fallback**: the AX-driven `TabFocusObserver` continuously bumps LRS recency on every focused-tab change, so the most recent `.browserTab`/`.browserWindow` for that browser in history is — by definition — the tab that was frontmost when Cmd+W fired. AppleScript is kept as a fallback for the cold-start case where history is empty. Also promoted the existing optimistic-prune log line from `debug` → `notice` (with the lookup source attached) so future regressions are visible in `log show` without enabling debug-level capture.

---

## 0.1.914 (2026-05-07)

### Fixed
- **Menu-bar-only apps in Quick Search now reveal their menu-bar item on Enter instead of silently no-op-launching.** Cloudflare WARP, Loxone Config, password-manager helpers, and other utilities that ship with `LSUIElement = YES` (or `LSBackgroundOnly = YES`) in their Info.plist used to appear in the Quick Search **Launch** section with subtitle "Launch · Press Enter to start" — but pressing Enter just spawned the agent process invisibly (no dock badge, no window, just a status item somewhere in the menu bar). Indistinguishable from "nothing happened."

  Now, [`launchableAppsSnapshot`](Sources/WOS/AppDelegate.swift) reads `LSUIElement` / `LSBackgroundOnly` while scanning `/Applications`, `/System/Applications`, and `~/Applications`, and tags each `LaunchableAppRecord` with `isMenuBarOnly`. The flag plumbs through `SearchSelection.launchApp(... isMenuBarOnly:)` to the activation site, where it routes to a new `revealMenuBarItem(forBundleID:appURL:displayName:)` method:
  1. **Already running, item already enumerated** → reuse the existing `MenuBarExtrasService.press(itemID:)` machinery to click the status item, opening its dropdown.
  2. **Already running, item not yet in snapshot** → force-refresh the snapshot once and retry. Falls back to a plain `activate()` if no item registers (some agents run truly headless).
  3. **Not running** → launch with `configuration.activates = false` (don't steal foreground focus), then poll the menu-bar snapshot at 250 ms intervals up to 8 attempts (~2 s) for the new status item to appear. Press it as soon as it does.

  Subtitle copy updated to be honest about what Enter does: menu-bar apps now read **"Menu Bar App · Press Enter to reveal menu bar item"**, normal apps still read **"Launch · Press Enter to start"**.

---

## 0.1.913 (2026-05-07)

### Fixed
- **"Searching…" line and bouncing dots showed inside the Recall-Tip HUD card**, even though nothing was loading — the HUD's footer hint already shows the position indicator and controls, so the in-card progress row was both redundant and misleading. Added an explicit `showsProgressFooter: Bool = true` parameter to [`CreativeLoadingCardView`](Sources/WOS/CreativeLoadingCardView.swift); the `LoadingTipReviewHUDController` now constructs the card with `showsProgressFooter: false` so the divider + dots + count row are stripped at build time. Loading-context call sites are unchanged thanks to the default value.

---

## 0.1.912 (2026-05-07)

### Added
- **"Recall Last Loading Tip" HUD** — global hotkey that re-shows the most recent Cmd+Tab+S loading-card tip after the panel has already moved on to results. Users frequently catch a glimpse of a tip — a shortcut, a fact about their tabs — but the card auto-rotates or the load completes before they can finish reading; this fixes that. Default shortcut **⌥⌘T**, fully user-configurable in Settings → Shortcuts → "Recall Last Loading Tip" (free text via `ShortcutRecorderView`, persisted in the existing `shortcut_settings_v1` UserDefaults blob with `decodeIfPresent` fallback). Pressing the hotkey a second time toggles the HUD closed.
  - **Tip history**: new static `LoadingTipsBank.recentlyShown: [LoadingTip]` ring buffer (capacity 20). Every site that displays a tip — `installCreativeLoadingCard(initialTip:)`, the rotation timer in `QuickSearchPanelController`, `showNextCreativeTip()`, and `QuickSearchLoadingOverlayController.init(tip:)` — now calls `LoadingTipsBank.recordShown(...)`. In-memory only, no disk persistence.
  - **HUD**: new [`LoadingTipReviewHUDController`](Sources/WOS/LoadingTipReviewHUDController.swift) — 480 × 240 glass panel reusing the existing `CreativeLoadingCardView` for visual continuity (just with the bouncing dots stopped since nothing is loading). Footer hint reads `TIP N OF M · ← / → BROWSE · ⎋ CLOSE`. ← / → scrub through history; Esc or outside-click dismisses. Empty-state shown when no tips have been recorded yet (cold start before any Cmd+Tab+S).
  - **Hotkey wiring**: new `recallLastTip: KeyboardShortcut` on `ShortcutSettings`, dispatched through both the `CGEventTap` path and the MAS Carbon path (`masRecallLastTipID = 111`). User-remappable.
  - **Menu bar**: new "Recall Last Loading Tip…" entry under the existing WOS menu for discoverability and pointer-driven access.

### Notes
> Documented in [ARCHITECTURE.md §6.4](ARCHITECTURE.md). The session-local tip scrubbing inside the loading card (added in 0.1.911) is kept separate from the bank's global history — `creativeLoadingTipHistory` on the panel is reset on every new load, while `LoadingTipsBank.recentlyShown` persists across sessions for the recall HUD to read from.

---

## 0.1.911 (2026-05-07)

### Added
- **Arrow-key tip navigation in the loading card** — while the creative loading card is on screen and the search field is empty, **←** scrubs back through every tip the user has seen this loading session (initial pick + rotation ticks), and **→** moves forward; once at the most recent, → picks a fresh tip from the bank, appends to history, and shows it. Implementation: new `creativeLoadingTipHistory: [LoadingTip]` + `creativeLoadingTipHistoryIndex: Int` in [QuickSearchPanelController](Sources/WOS/QuickSearchPanelController.swift); new `.moveLeft` / `.moveRight` `NavigationCommand` cases (keyCodes 123 / 124); arrow handling gated by `canNavigateCreativeTips` (card visible + isLoading + searchField empty) so cursor movement inside the search field isn't stolen the moment the user starts typing. First arrow press calls `stopCreativeTipRotation()` so the auto-rotation timer can't yank a manually-selected tip back. Rotation timer also appends each tick to history so even tips that already rotated past are still scrubable. Preview-mode hint updated to `PREVIEW MODE  ·  ← / → Tips  ·  ⏎ Continue  ·  ⎋ Cancel`.
- **Creative Loading Card for Cmd+Tab+S** — the multi-second tab-enumeration warm-up no longer shows the bare "Loading open apps and browser tabs…" placeholder. A richer card now occupies the centre of the QuickSearch panel during the wait: a small SF Symbol header chip ("DID YOU KNOW?", "ABOUT YOUR TABS", "MACOS TIP", "CURIOUS BIT"), a 22 pt title, a 1–2 sentence body, a hairline divider, and a footer with bouncing dots plus the live "Searching N tabs…" count that ticks up as each browser's enumeration returns. Built around four content categories — ~80 hand-authored tips total — drawn with weights 40 / 25 / 20 / 15 (WOS shortcut · personal tab stat · macOS power tip · CS factoid). Anti-repeat de-duplicates by category and title across opens. Personal-stat tips are synthesised from the in-memory tab cache (e.g. "73 tabs open right now", "All 41 of those are in Chrome", "Spread across 3 browsers") — no I/O on the loading path. New files: [LoadingTipsBank.swift](Sources/WOS/LoadingTipsBank.swift), [CreativeLoadingCardView.swift](Sources/WOS/CreativeLoadingCardView.swift), [QuickSearchLoadingOverlayController.swift](Sources/WOS/QuickSearchLoadingOverlayController.swift). Visual style is monochrome — no per-category colour tints — so the card sits inside the existing WOS chrome rather than competing with it.
- **Tip rotation during long loads** — a 6-second timer (`creativeLoadingTipRotationTimer` in [QuickSearchPanelController](Sources/WOS/QuickSearchPanelController.swift)) crossfades the card to a fresh pick (0.18 s out, swap, 0.22 s in) for every 6 s the panel sits in loading state. 5–10 s loads see two tips; nobody finishes mid-read. Timer is invalidated by `setLoading(false, ...)` and `closePanel()`.
- **Settings toggle: General → Quick Search → "Creative loading screen"** (default on) — turns the rich card off in favour of the legacy minimal pill / dots-and-label loading. Stored under the existing `general_settings_v2` key with a `decodeIfPresent` fallback so installed users upgrade cleanly without losing other general settings.
- **Developer "Preview Loading Screen…" mode** — holds the loading state open indefinitely so the card can actually be inspected (otherwise the load completes faster than the user can read). Two entry points:
  - Menu bar → "Preview Loading Screen…"
  - URL handler `wos://preview-loading`

  In preview mode `presentQuickSearch(loadingPreviewMode: true)` skips the bootstrap entries (otherwise the panel would hide the central card and show the cached results table instead), forces the creative card on regardless of the user setting, and does *not* trigger the async tab enumeration. A footer hint reading `PREVIEW MODE  ·  ⏎ Continue  ·  ⎋ Cancel` makes the controls discoverable. Return triggers the real load (`onContinueLoadingPreview` → `runQuickSearchEntryLoad`); Esc uses the existing `cancelOperation` route to close the panel.

### Changed
- **`qsLoadingPill` is now typed as `QuickSearchLoadingPresenting`** (a new protocol both controllers conform to). Same `show(over:initialCount:) / updateCount(_:) / dismiss()` surface so AppDelegate's call sites are identical regardless of which presenter is active.
- **`BrowserScriptBridge.EnumerationProgressCallback` ticks both surfaces** during async load — the brief overlay (alive only during the synchronous warm-up) and the panel's embedded card (alive throughout the longer enumeration phase). The same tip is picked once and handed to both phases for visual continuity.
- **QuickSearch panel `setLoading(_:message:)` gates the central loading area on `useCreativeCard`** — when the creative card is installed, the original dots+label `centralLoadingStack` is hidden and the card owns the centre.

### Fixed
- **Plain `open "wos://..."` URLs were routed to `/Applications/WOS-MAS.app`** instead of the canonical `/Applications/WOS.app` because the LaunchServices default-handler binding favoured the MAS variant. Set the binding explicitly via `NSWorkspace.shared.setDefaultApplication(at:toOpenURLsWithScheme:)` against the dev install. Stale build-artifact bundles under various `.dist/` and `.dmg-mount/` paths were unregistered to clean up the duplicate-claim count (15 → 3).

### Notes
> The QuickSearch loading subsystem is now documented in [ARCHITECTURE.md §6.2](ARCHITECTURE.md) — phase-A vs phase-B split, the shared `QuickSearchLoadingPresenting` protocol, tip selection / handoff, and the developer preview-mode entry points. §6.3 covers the preview-mode plumbing including why an empty initial-entries list is required for the card to actually appear.

---

## 0.1.910 (2026-05-06)

### Added
- **Jump-to-File (⌘⇧G) is now a generic UI-path resolver** with three tiers:

  **Tier 1 — Any System Settings pane.** Selecting/copying `Settings → Privacy & Security → Automation`, `Settings → Bluetooth`, `Settings → Wallpaper`, etc. opens that pane directly. The catalog is no longer hand-curated — [`SystemSettingsPaneCatalog`](Sources/SwitchCore/SystemSettingsPaneCatalog.swift) scans `/System/Library/ExtensionKit/Extensions/*.appex` and `System Settings.app/Contents/PlugIns/*.appex` at first lookup, derives display names from bundle IDs via a humanizer (`com.apple.AirDrop-Handoff-Settings.extension` → `AirDrop & Handoff`), and merges with [`SettingsAnchor.curatedAnchors`](Sources/SwitchCore/SettingsAnchor.swift) for canonical labels (`Privacy & Security`) and deep sub-anchors (`?Privacy_Automation`). Curated entries win on collision.

  **Tier 2 — App settings.** `Slack → Settings`, `Todoist → Preferences`, `Google Chrome → Settings`. Resolver detects `<RunningApp> → Settings|Preferences|Preferences…`, dispatcher activates the app and synthesizes `Cmd+,` via `CGEvent` (the macOS HIG-standard preferences shortcut).

  **Tier 3 — Arbitrary app menu walk.** `Cursor → File → New File`, `Slack → View → Hide Sidebar`. New [`AXMenuWalker`](Sources/WOS/UIPathDispatch.swift) walks `AXMenuBar` via Accessibility, matching titles case-insensitively (and tolerating trailing `…`/`...`), then presses the leaf via `kAXPressAction`. Failure modes are explicit: `notAuthorized` (AX off), `pathNotFound(missing:)` (segment didn't match), `pressFailed` (AX press errored). Surfaced in the toast as the missing segment so the user can correct the path.

  Resolution order is Tier 1 → Tier 2 → Tier 3. Tier 1 wins on substring match (longest-key-wins) so `"Slack → Settings → Privacy & Security → Automation"` still routes to Settings even though the head segment is an app. Resolver returns a [`UIPathResolution`](Sources/SwitchCore/UIPathResolution.swift) enum; dispatcher in [`AppDelegate.dispatch(uiPathResolution:displayPath:)`](Sources/WOS/AppDelegate.swift) routes each variant.

  Tier-1 sub-anchored URLs still drive navigation via `NSAppleScript` `tell application "System Settings" … reveal anchor` — `NSWorkspace.shared.open` alone leaves you on the Privacy & Security parent without scrolling to the sub-pane in our LaunchServices context (same root cause as the file-URL issue fixed in 0.1.890).

- **Stable code-signing for `fast_dev_loop` rebuilds.** New [`scripts/setup_dev_signing.sh`](scripts/setup_dev_signing.sh) — one-time setup that generates a self-signed code-signing certificate (`WOS Dev Signing`), imports it into the login keychain (openssl PKCS#12 with `-legacy` for openssl-3 compat), and writes the SHA1 to `~/.wos-signing-identity`. [`scripts/dev_loop.sh`](scripts/dev_loop.sh)'s `--fast` path now reads that file and runs `codesign --force --options runtime --entitlements <…> --sign <hash>` on the bundle after the binary swap. Designated Requirement is now `identifier "com.agentic.wos" and certificate leaf = H"<hash>"` — byte-identical across rebuilds, so macOS no longer revokes Accessibility / Automation grants every time the binary changes.

### Fixed
- **`-10004 privilege violation` from subprocess tab enumeration is now surfaced.** Previously the `ERR:-10004:…` sentinel returned by `osascript` subprocesses was logged and `continue`d in [`fetchOpenTabsForBrowserIfAvailable`](Sources/SwitchCore/BrowserScriptBridge.swift), bypassing `handleTabEnumerationError` — so `lastPrivilegeViolation`/`automationDeniedBrowsers` were never set and the user saw silently-empty tab lists with no alert. New [`recordEnumerationErrSentinel(output:browser:)`](Sources/SwitchCore/BrowserScriptBridge.swift) parses the sentinel, records the violation through the same surfacing pipeline native NSAppleScript errors use, and suspends the browser with a `privilege-violation` reason. The downstream `processAutomationPermissionSignals` toast + per-app guidance alert now fire.

### Changed
- **Automation-permission guidance alert text** rewritten to explain the *root cause* — "macOS ties Automation grants to the exact binary signature; rebuild changes the signature and silently revokes the grant" — and the fix steps. Previously generic.
- **Jump-to-File error messages clarified.** "Path not found" toast now includes the path that was tried; "Clipboard is empty" only shows when the clipboard actually was empty (a non-resolving selection gets `"selection isn't a file path or known UI path"` instead). Diagnostic log captures Unicode codepoints of the input so non-rendering chars can be triaged after the fact.

---

## 0.1.890 (2026-05-04)

### Fixed
- **Jump-to-File (⌘⇧G) silently failing for folders carrying `com.apple.provenance`** — pressing the hotkey on a path like `/Users/honato/wosWebSiteGenerator100` showed the "Jumped to …" toast but Finder never came forward. Triage walk-through (every step logged at `subsystem=com.agentic.wos category=app`):
  1. `NSWorkspace.shared.open(folderURL)` → `NSCocoaErrorDomain` code `256` (`NSFileReadUnknownError`). LaunchServices' "what app handles this folder" resolution was being denied for any path tagged with `com.apple.provenance` (xattr macOS sets when a sandboxed creator — including AI agents — writes a folder). Other apps need an explicit Files-and-Folders TCC grant to open it; ad-hoc-signed WOS doesn't have one.
  2. `NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath:)` → returned `false`. Same TCC layer rejecting the reveal RPC.
  3. `Process(/usr/bin/open <path>)` → `EBADF` ("Bad file descriptor") at `posix_spawn` time, even after pinning `standardInput/Output/Error` to `FileHandle.nullDevice`. Foundation's `Process` spawner is unreliable from inside our ad-hoc-signed runtime regardless of how stdio is configured.
  4. `NSWorkspace.shared.open([url], withApplicationAt: Finder.app, configuration:)` → also `NSCocoaErrorDomain` 256 ("application Finder could not be launched"), Finder already running. LaunchServices was wholesale refusing to deliver opens from this WOS instance.
  5. **Working channel: Apple Events.** `osascript -e 'tell application "Finder" to open POSIX file "…"'` from Terminal, and a standalone Swift binary calling the same source via `NSAppleScript`, both opened the folder cleanly. Apple Events have a separate IPC path and a separate TCC permission (`kTCCServiceAppleEvents` / Automation → Finder), neither of which the LS denial affects.

  Switched [`AppDelegate.handleJumpToFileRequested`](Sources/WOS/AppDelegate.swift:6318) to drive the open through `NSAppleScript` against Finder via a new helper [`openInFinder(url:)`](Sources/WOS/AppDelegate.swift:6398). First press triggers a one-time "WOS wants to control Finder" prompt; once approved every subsequent jump works. The error path captures `NSAppleScript.errorNumber` so a later failure log identifies `-1743` (`errAEEventNotPermitted`) directly with a hint to grant the permission via System Settings → Privacy & Security → Automation → WOS → Finder. Falls back to `NSWorkspace.activateFileViewerSelecting([url])` if AE is unavailable, so the hotkey still produces *some* visible result rather than the prior silent failure.

### Changed
- **PDF / PNG / JPEG (and any non-folder) ⌘⇧G now opens the file in its default app instead of revealing it in Finder.** Same `openInFinder` helper drives both branches: Finder's `open` verb opens folders into themselves and hands files to their registered handler (Preview for images and PDFs, Xcode for `.swift`, etc.). Previous behavior used `activateFileViewerSelecting([url])` which only highlighted the file in its parent folder — a deliberate choice originally, but reverted now that the Apple-Events route handles the directory case correctly without needing the reveal-fallback.

### Diagnostics
- Public-by-default logging on the jump-to-file path: every step (`Jump to file requested` → `executing: <path> isDir=…` → `opening … via Apple Events` → `succeeded` / `failed code=N message=…`) is emitted with `privacy: .public` on the path and error fields so future failures are visible in `log show` without needing to disable log redaction. Filter with `log show --predicate 'subsystem == "com.agentic.wos" AND category == "app"' --info --debug --last 2m | grep -i "jump to file"`.

---

## 0.1.876 (2026-05-04)

### Fixed
- **Brave Browser activation** — `chromiumActivateWindowScripts` byWindowID variant used `repeat with w in windows ... if id of w is <int>`. Brave (and current Chrome 147) store window `id` as text, and AppleScript-side `is` between integer and text evaluates `false`, so the loop never matched and the script always returned `"0"`. The bug was latent the entire time, but pre-2026-05-04 it never surfaced: (a) Chromium tab enumeration was returning 0 tabs for everything (the `-1728` `index` bug fixed in 0.1.869), so activations went through the stale-id failure path before reaching here; (b) post-commit verification was silently skipping (the verify-token bug fixed in 0.1.869), so `activateWindow`-as-fallback never actually ran. Today's fixes finally let it run, and Brave specifically tripped it because Brave's slower AppleScript engine timed out the front-tab read within the 0.3 s budget more often, returning `.unknown` and firing the broken fallback. Replaced the `repeat ... if id of w is N` with `first window whose id is N` (the `whose` predicate is evaluated inside the browser's own engine, which coerces between integer and text correctly — same pattern `chromiumActivateTabScripts` already uses). Added an `on error` handler to surface failures via `ERR:<num>:<msg>` instead of returning empty. ([Sources/SwitchCore/BrowserScriptBridge.swift:2615](Sources/SwitchCore/BrowserScriptBridge.swift:2615))
- **`runActivationScriptsReturningSuccess` strict success check** — was `result != "0"`, would now treat any free-form output (including the new `ERR:...` sentinels) as success and skip fallback scripts. Tightened to require `result == "1"` exactly. Free-form errors are logged via `Activation script reported error: <text>` and the runner falls through to the next script in the variant list.

### Added
- **Live tab-count in the Quick-Search loading pill** — instead of just "Searching…", the pill now reads "Searching 67 tabs…" the moment Cmd+Tab+S is pressed (seeded from `cachedOpenBrowserTabCount`) and ticks up live as each browser's enumeration returns ("Searching 18 tabs…" → "Searching 36 tabs…" → final). Plumbing: new optional `EnumerationProgressCallback` parameter on [`fetchAllOpenBrowserTabs`](Sources/SwitchCore/BrowserScriptBridge.swift:1683) firing once per browser when its scan completes; new `updateCount(_:)` API on [`QuickSearchLoadingPillController`](Sources/WOS/QuickSearchLoadingPillController.swift); pill width nudged 248 → 280 pt to fit "Searching N tabs…". Both `show(initialCount:)` and `updateCount(_:)` force a `CATransaction.flush()` so the new label commits to the render server even while the main thread is about to block again.

### Changed (test coverage + module boundaries)
- Extracted `BrowserTabMatch` from `AppDelegate` into its own [Sources/SwitchCore/BrowserTabMatch.swift](Sources/SwitchCore/BrowserTabMatch.swift) so post-commit verification logic lives next to the rest of the activation primitives. Public, with regression-guard documentation referencing the 0.1.839 / 0.1.840 tristate migration.
- `HistoryReconciler.normalizedWindowIdentifier` is now `public` so other call sites that map a Chromium composite `windowIdentifier` (`<windowID>:<tabGUID>`) to a system resource can use the same canonical normalization. Doc comment now warns that `Int("…:…")` returns `nil` and silently fails.
- New focused unit-test files in `Tests/SwitchCoreTests/`: `BrowserTabMatchTests`, `HistoryReconcilerTests`, `BrowserClassificationTests`, `CompositeIdentifierTests`, `AppleScriptOutputParserTests`, `PrivacyTests`, `ConfigurationLintTests`. Tightened invariants around tab-id type handling, window-identifier composite parsing, and incognito/private-mode propagation through the snapshot pipeline.

### Notes
> **Skipped versions 0.1.874, 0.1.875**: VERSION auto-bumped during build_and_install dev iterations between this release and 0.1.873; retired to avoid renumbering local artefacts.

---

## 0.1.873 (2026-05-04)

### Fixed
- **`scripts/build_release.sh` was hanging indefinitely on `swift test`** — `Tests/SwitchCoreTests/AppleScriptConcurrencyTests.swift` and `Tests/SwitchCoreTests/BrowserScriptIsolationTests.swift` were both written under the pre-2026-05-01 two-engine `AppleScriptEngine` architecture (`shared` for discovery, `activation` for commits, on independent serial queues). After the 2026-05-01 collapse onto a single serial queue (`AppleScriptEngine.activation` aliased to `.shared`, see `ARCHITECTURE.md` §11.3), both tests deadlocked: the background task held the queue inside `engine.execute { … wait() }` and the foreground `engine.execute(…)` call was `queue.sync` waiting for the same queue to free. Smoke tests never completed, the release pipeline was unrunnable end-to-end. Replaced both tests with assertions of the *current* invariants: (a) `AppleScriptEngine.activation` aliases `.shared` (structural protection against re-introducing the split), (b) `execute` is reentrant on its own queue (no self-deadlock for nested calls), (c) `runWithBudget` returns `nil` rather than blocking when the queue is contended (the actual responsiveness contract the foreground path relies on today). 49/49 tests now pass in 0.7 s.

### Notes
> **Skipped versions 0.1.871, 0.1.872**: VERSION bumped during in-progress build_and_install dev iterations without a corresponding tagged release. Retired to avoid renumbering local artefacts.

---

## 0.1.870 (2026-05-04)

### Changed
- **Quick-Search loading pill** — new `QuickSearchLoadingPillController` ([Sources/WOS/QuickSearchLoadingPillController.swift](Sources/WOS/QuickSearchLoadingPillController.swift)) shows a 248×56 pt pill with three bouncing dots the instant Cmd+Tab+S is pressed, filling the 2–5 s warm-up window before the Quick-Search panel slides in. The dots are a `CAKeyframeAnimation` on plain `CALayer`s rendered out-of-process by the WindowServer, so they keep animating even while WOS's main thread is fully blocked by `makeSearchEntries` / `presentLoading` / lazy `QuickSearchPanelController` init. Pill panel uses `.popUpMenu` level (101), `.hudWindow` blending `.behindWindow`, appearance-aware hairline border. The pill anchors over the Cmd+Tab HUD when one is on screen.
- **Quick-Search central loading indicator** — replaced the plain `NSProgressIndicator` (which is timer-driven and freezes when the main thread blocks) with the same `BouncingDotsView` used in the pill, scaled up. Visually consistent across both loading phases.

### Added
- **Website NG previews** — three iterations of a redesigned marketing site (`websiteNG/`, `websiteNG2/`, `websiteNG3/`) at the repo root. Existing `website/` is untouched. NG dirs are visual-design exploration; only `website/` is the live site.
- **Cmd+Tab demo assets** — `website-v3/screenshots-draft/output/wosapp-cmd-tab-demo.{mp4,webm,gif}` plus poster image, generated by the `recorder/` tooling. Used by the website hero section.

---

## 0.1.869 (2026-05-04)

### Fixed — QUICK_SEARCH_ACTIVATION_RELIABILITY_MILESTONE

A three-bug stack that compounded into "Quick-Search Enter does nothing" for any tab in any Chromium browser. Each bug was masking the next:

- **Chromium tab enumeration returned 0 tabs for Chrome / Chrome Canary / Brave** because `chromiumAllTabsScript` opened with `set tabIndices to index of every tab of every window`, which Chromium rejects with `errAENoSuchObject (-1728)`. The bare `try ... end try` swallowed the error and the script returned an empty string, which the parser misclassified as "browser has zero tabs". `fetchAllOpenBrowserTabs` reported `totalTabs=8` (Safari only) on a machine with 67 tabs across four browsers. Quick-Search results, LRS pruning, the high-tab-count recommendation, and cross-browser cycle history all silently degraded. Fix: derive tab index from the in-process loop counter (Chromium returns the tab list in 1-based addressable order); drop the failing bulk query. See `LESSONSLEARNED.md` → "Chromium AppleScript: `index of every tab of every window` Returns errAENoSuchObject (-1728)".
- **Silent error swallowing in `chromiumAllTabsScript`** — the bare `try` had no `on error` handler, so any AppleScript failure (the `-1728` above, future TCC denials at `-1743`, dictionary mismatches, etc.) returned the empty string with no log. Adding `on error errMsg number errNum return "ERR:" & errNum & ":" & errMsg` plus a `output.hasPrefix("ERR:")` branch in the runner ([BrowserScriptBridge.swift:1872](Sources/SwitchCore/BrowserScriptBridge.swift:1872)) makes the actual error number visible in the unified log. Without this diagnostic, the bug above was undetectable from log analysis. See `LESSONSLEARNED.md` → "Bare `try` in Chromium Enumeration Script Swallowed Errors Silently".
- **Post-commit verification was a no-op for Quick-Search activations** — `scheduleBrowserTabActivationVerification` opens with `guard self.activationToken == token else { return }`, but `activationToken` was being bumped 5–30 ms after every WOS-initiated `app.activate(...)` by the NSWorkspace `didActivateApplicationNotification` observer, because the `programmaticTargetBundleID` flag was only set inside `finishCycle()` (cycle path) and not inside `activate()` itself. Quick-Search routed through `activate()` directly, never set the flag, and every verification 40 ms hop returned silently with no log. The activateWindow fallback designed to catch "activateTab returned `1` but the wrong tab is on screen" never ran. Fix: set `programmaticTargetBundleID` and extend `suspendBackgroundTabRefreshUntil` at the top of `activate()` itself ([AppDelegate.swift:2316](Sources/WOS/AppDelegate.swift:2316)), covering Quick-Search + cycle + any future caller automatically. See `LESSONSLEARNED.md` → "Self-Initiated Activations Bumped `activationToken` and Silently Skipped Post-Commit Verification".

### Added
- **Quick-Search activation tracing** ([AppDelegate.swift:4729](Sources/WOS/AppDelegate.swift:4729)) — three log lines (`Quick-search activation start` / `activateTab-done` / `verified-matches|differs-fallback|unknown-fallback (final)`) emit a single Enter→done elapsed time per Quick-Search activation. Token-scoped so a stale trace from a non-`browserTab` selection (`.launchApp`, etc.) cannot misattribute itself to a later cycle activation. Diagnostic instrumentation; not user-visible.
- **"Don't show again" button on the high-tab-count recommendation** ([AppDelegate.swift:5268](Sources/WOS/AppDelegate.swift:5268)). The popup that fires on the first Quick-Search load when the open-tab snapshot exceeds the threshold (`>20` tabs) now offers permanent suppression via `UserDefaults` key `wos.highTabCountRecommendationSuppressed`. Existing "Export Tabs to .txt" / "Later" behaviour unchanged.

### Notes
> **Skipped versions 0.1.857 – 0.1.868**: VERSION bumped during in-progress dev iterations across this window without intermediate releases. Those numbers are retired to avoid retroactive renumbering of the build artefacts that already carried them locally.

---

## 0.1.856 (2026-05-03)

### Fixed
- **Permissions dialog design language** — aligned `PermissionsPresenter` with all other WOS HUDs: `blendingMode .behindWindow` (was `.withinWindow`), `cornerRadius 22` chrome with appearance-aware hairline border, `RoundedRectangle(cornerRadius: 10)` 44×44 icon plates (was `Circle()` 40×40), neutral row hairline border (removed green tint on granted rows), row `cornerRadius 14` (was 12), typography 14.5 pt / 11.5 pt (was 14 pt / 12 pt). Panel resized 440×480 → 460×500 to accommodate 10 pt inset.

> **Note — skipped version 0.1.855:** Two release builds ran back-to-back; the first produced 0.1.855 (built but never committed or shipped) and the second produced 0.1.856. Version 0.1.855 is permanently retired to avoid confusion.

---

## 0.1.853 (2026-05-03)

### HUD_VISUAL_REDESIGN

Redesigned the Recent Files and Clipboard History HUDs to share the exact visual language of the Cmd+Tab and Cmd+Tab+S HUDs (SwitcherHUDController / QuickSearchPanelController).

### Changed
- **Recent Files HUD** — replaced the old 640×500 panel (18pt bold header, NSBox divider, 32×32 flat icon, 13.5pt/10.5pt text) with the SwitcherHUD design language: 572pt panel, `cornerRadius 22` glassmorphic chrome with appearance-aware hairline border (white 0.10 / black 0.08), uppercase tertiary section label ("RECENT FILES"), 44×44 icon plates with 32×32 document icons, 14.5pt semibold title + 11.5pt regular path (subtitle), 64pt rows, 4pt row spacing, footer bar (count left + keyboard hint right). Selection style matches HUDRowItemView exactly (dark: white 0.13 bg / 0.05 border; light: black 0.07 bg / 0.04 border; icon plate fill adapts).
- **Clipboard History HUD** — replaced the old 440×480 panel (15pt bold header, 13pt medium preview, 58pt rows, cornerRadius 12) with the same design language: 520pt panel, `cornerRadius 22` chrome, uppercase tertiary section label ("CLIPBOARD"), 40×40 icon plates with clipboard SF symbol, 14.5pt semibold preview, 64pt rows, confirm-clear state exposed in footer (red text). `FlippedClipView` added to both HUDs so content anchors at top regardless of list length.
- Both HUDs: removed `NSBox` divider, big bold `NSTextField` header, and single-line hint label. Replaced with `RFHUDFooter` / `CHHUDFooter` (36pt bar matching `HUDVerticalFooterView` vocabulary). Chrome border colour is now appearance-aware (calls `updateChromeColors()` on each `show()`) instead of static `NSColor.separatorColor`.

---

## 0.1.852 (2026-05-03)

### SETTINGS_UI_OVERHAUL + SECURITY_HARDENING_MILESTONE

This release completes a full Settings UI redesign pass and closes a documented security discrepancy: the `network.client` entitlement was present in `entitlements.plist` despite WOS's documented "Zero-Exfiltration / no network" guarantee. It also removes the Hard Reset feature, which spawned `tccutil` and `/bin/bash` subprocesses incompatible with the Seatbelt sandbox. All Settings sections now scroll correctly with top-anchored content, the window is properly sized, and the Performance Samples badge has a distinct visual identity.

### Security
- **Removed `com.apple.security.network.client` entitlement**: This entitlement had `<true/>` in `entitlements.plist` while the SBPL profile in `main.swift` already enforced `(deny network*)` at the kernel level. Since `com.apple.security.app-sandbox` is `false`, the entitlement was a no-op — it neither granted nor restricted anything — but it contradicted the documented Zero-Exfiltration model. Removed for consistency with `SECURITY.md` and the website security page. See `LESSONSLEARNED.md` → "Dead Entitlement Contradicting Documented Security Model (2026-05-03)".
- **Removed Hard Reset feature**: Hard Reset spawned `tccutil reset Accessibility` and `/bin/bash` subprocesses. The Seatbelt profile in `main.swift` denies `process-exec` for arbitrary binaries, making these subprocess launches fail silently or violate the sandbox. The feature also never reliably surfaced the Accessibility permission prompt after restart. See `LESSONSLEARNED.md` → "Subprocess Spawning Inside Seatbelt Jail (2026-05-03)".

### Fixed
- **Settings sections bottom-aligned**: All 12 Settings sections were bottom-aligned because `buildSections()` pins each section view to both the top and bottom anchors of `contentContainer`, stretching plain `NSStackView` to fill full height. Content anchored at the unflipped coordinate origin (bottom). Fixed by wrapping every section in an `NSScrollView` with a custom `FlippedClipView` (`isFlipped = true`), so `(0,0)` is top-left and content anchors at the top.
- **Scrollbar overlapping rightmost content**: Overlay scrollbars floated on top of the rightmost settings control in each section. Fixed with `scrollerStyle = .overlay` + `scrollerInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: -12)` on every section scroll view, pushing the bar into the window's right margin.
- **Per-Tab Pinned Shortcuts empty card**: When no tabs had assigned shortcuts, the empty `SettingsGroupView` card still rendered. Replaced with a clean info label when the list is empty.
- **Performance Samples badge indistinguishable from metric chips**: The "Samples" count chip used the same `MetricChipView` as Min/Max/Mean/Median/Mode. Replaced with a dedicated `SamplesBadgeView`: large bold count (16pt monospaced), small "Samples" annotation (9pt), subtle border, tighter padding — reads as a count badge, not a metric row.
- **Settings window near full-screen**: Was set to 95% of screen height. Reduced to 80% capped at 820px; width reduced from 960px to 840px.

### Changed
- **NSPopUpButton for Overflow Threshold and Slide Duration**: Replaced `NSTextField + NSStepper` pairs with native `NSPopUpButton` dropdowns matching Apple's pattern for bounded discrete values.
- **Version label moved to sidebar bottom**: Always visible regardless of which section is active, instead of buried in the right panel footer.
- **`makeSectionScrollView` helper**: Centralises `FlippedClipView` + overlay-scroller setup so all section builders share the same correct configuration.

### Documented
- **`ARCHITECTURE.md` §10**: Updated §10.1 to document that `com.apple.security.network.client` must never be re-added, and clarified the relationship between App Sandbox entitlements (disabled) and the runtime Seatbelt jail.
- **`LESSONSLEARNED.md`**: 2 new entries (2026-05-03).
- **`SECURITY.md`**: Confirmed entitlement removal with rationale.

## 0.1.846 (2026-05-02)

### MULTI_BROWSER_STABILITY_MILESTONE

This release closes out a wave of cross-browser correctness bugs surfaced after the LRS_STABILITY_MILESTONE (0.1.835): an AppleScript-engine race that crashed WOS after long uptime, a verification fallback that oscillated between "tab drift" and "cross-profile broken", and a latent Chromium window-activation parse bug that masked itself behind a generic title-match fallback. The release also lands a discipline pass on `os_log privacy` markers — the same anti-pattern flagged in the 2026-03-30 LESSONSLEARNED entry was silently making the regression suite report most browser tests as "no run observed". Verified across Safari, Chrome, Brave, and iTerm under the in-app regression suite (after permissions are re-granted post-install).

### Fixed
- **AppleScript concurrency crash (`EXC_BAD_ACCESS` in `TASLexer::UseEvent`)**: WOS crashed after ~44h uptime when concurrent AppleScript work hit Apple's parser state from two different `NSAppleScript` instances. The 2026-04-15 design split `AppleScriptEngine` into `Shared` and `Activation` queues for QoS isolation, on the assumption that distinct instances on distinct queues would isolate via `gAppleScriptMutex`. The crash report proved that wrong: the mutex serializes invocation but the parser's internal state is shared, so concurrent compile (`OSACompile`) + dispose (`OSADispose`) corrupted the lexer. **Collapsed both engines into one serial queue at `.userInteractive` QoS.** `AppleScriptEngine.activation` is now an alias for `.shared`. The lost QoS isolation is replaced by structural budget protection (`cycleEagerFetchBudget = 25ms`) which bounds the main thread without depending on queue priority. See `LESSONSLEARNED.md` → "AppleScript Two-Engine Design Was Unsafe (2026-05-01)".
- **Verification fallback tristate (tab drift AND cross-profile, in one shape)**: `scheduleBrowserTabActivationVerification` runs 40 ms after every browser-tab commit and asks "is the front tab the target?" via a 300 ms-budgeted AppleScript read. The boolean return value conflated three distinct outcomes — `matches`, `differs`, and `unknown` (budget timeout). Treating `unknown` as `differs` (0.1.839) caused tab drift: under AppleScript backlog the verification falsely concluded activation failed, fired the `activateWindow` fallback, and stomped a successful activation ~700 ms later. Treating `unknown` as `matches` (0.1.840) broke cross-profile Chrome: multi-profile Chrome runs as separate processes, `tell application "Google Chrome"` queries the wrong process, `fetchFrontTabWithinBudget` legitimately returns nil → `unknown` → no fallback fires → user is stuck on the source profile. **Fixed with a tristate `BrowserTabMatch { matches, differs, unknown }`** that fires the `activateWindow` fallback on `.differs` AND `.unknown`. Single-queue serialization from the prior fix makes `.unknown` rare in normal use, so the original tab-drift symptom does not return. See `LESSONSLEARNED.md` → "Verification Fallback Tristate: `.unknown` Is Not `.differs` (2026-05-01)".
- **Chromium `activateWindow` silently failed for every Chromium browser**: `chromiumActivateWindowScripts` parsed the windowIdentifier as `Int(windowIdentifier) ?? -1`. Background tab enumeration emits Chromium identifiers in composite form `"windowID:tabGUID"` (because Chromium's persistent tab identity is the GUID), so `Int("1723211242:1723211243")` returned nil → `windowID = -1` → no window matched → script returned `"0"` → fell through to the `byTabMetadata` fallback, which has no tab title/URL when the target is a window-level entry → activation failed. Tab-level activation worked because it looks up by GUID. **Now strips on `:` first**, matching the existing pattern in `windowIdentityKey()` and Arc's `byID` script. Affects Chrome, Brave, Chrome Canary, Brave variants. See `LESSONSLEARNED.md` → "Chromium activateWindow Must Strip :tabGUID from Composite Identifiers (2026-05-01)".
- **iTerm regression test always failed because of "Close 3 tabs?" popup**: The iTerm window-cleanup script issued `close (window with N tabs)`, which triggers macOS's confirmation dialog. The dialog blocked cleanup, stole focus, and corrupted any subsequent test in the run. **Now closes each session/tab individually first** (single-child closes don't prompt), then closes the now-empty window silently. Pattern generalizes to any future "close container with multiple children" code path.
- **Regression suite parser silently treated 6 of 8 tests as "no run observed"**: `PerfAutomationRunner.logRegressionResult` interpolated the test status without `privacy: .public`, so the bash parser saw `status=<private>` and matched none of `(pass|fail|skip)`. Real test failures appeared as test absence — the worst possible silent failure. **Added `privacy: .public` to status, check name, and step name** in PerfAutomationRunner; same treatment applied to `Window activation failed for X window Y` and `Primary enumeration failed for X (pid=Y, error=Z)` so post-mortem on regression failures is now actually possible.

### Improved
- **OS log privacy marker discipline**: 57 markers added across 38 log call sites in AppDelegate.swift covering app/browser identifiers (`bundleID`, `browser.appName`, `browser.rawValue`, `displayName` for app targets, enum `rawValue`s, literal-selecting ternaries). Tab/page titles, URLs, file paths, error strings, and Chrome profile names are deliberately *not* marked — those carry user content and remain redacted in production logs. Same selection rule documented in `LESSONSLEARNED.md` → "OS Log Privacy Markers Bit Us Again (2026-05-01)".
- **Diagnostic visibility for browser-window regression**: the `Browser window regression check` and `Browser window regression seed failed after navigation` log lines now expose the browser name, expected window ID, and expected title — making it possible to diagnose regression failures from the log alone instead of having to re-run with a debugger attached.
- **Single AppleScript queue label**: `wos.apple-script-engine` and `wos.apple-script-activation` are gone; the unified queue is labelled `wos.apple-script`. Easier to grep in logs and `sample WOS` output.

### Documented
- **`AppleScriptEngine.swift`**: top-of-file docstring now explains why both `shared` and `activation` route to the same queue, citing the 2026-04-28 crash report. Future agents who consider re-splitting see the warning before the API.
- **`BrowserScriptBridge.swift:chromiumActivateWindowScripts`**: inline comment block above the windowID parse explains the composite-identifier format and references the symptom that proves the strip is load-bearing.
- **`AppDelegate.swift:scheduleBrowserTabActivationVerification`**: comment block on the fallback decision explains why `.unknown` MUST fire the fallback (cross-profile recovery), and references the single-queue collapse as the reason `.unknown` is now rare.
- **`AppDelegate.swift:waitAndSeedBrowserTargetFromOpenTabs`**: comment on the iTerm cleanup path explains the `close N-tab window → confirmation popup` pattern.
- **`PerfAutomationRunner.swift:logRegressionResult`**: comment on the privacy markers explains the parser-blind-to-private-redaction trap that justified the markers being load-bearing rather than cosmetic.
- **`ARCHITECTURE.md` §11.3 ("AppleScript Robustness")**: rewritten to reflect single-queue serialization. The "Dual Engine Isolation" recommendation from the 2026-04-15 entry is explicitly marked as reversed.
- **`ARCHITECTURE.md` §11.4 ("Performance Isolation — The 50ms SLA")**: rewritten to reflect structural budget protection (`cycleEagerFetchBudget = 25ms`) replacing the old lane-priority scheme.
- **`ARCHITECTURE.md` §4 ("Activation Staleness Protection")**: added a new "Post-commit verification" subsection documenting the tristate invariant.
- **`ARCHITECTURE.md` §"Chromium Composite Identifiers"**: new section documenting the `windowID:tabGUID` wire format and the `Int(windowIdentifier)` strip-on-colon rule.
- **`LESSONSLEARNED.md`**: 5 new dated entries (2026-05-01) — "AppleScript Two-Engine Design Was Unsafe", "Verification Fallback Tristate", "Chromium activateWindow Must Strip :tabGUID from Composite Identifiers", "Closing Multi-Tab Windows Triggers macOS Confirmation", "OS Log Privacy Markers Bit Us Again".

## 0.1.835 (2026-04-21)

### LRS_STABILITY_MILESTONE

This release closes out the long arc of LRS recency bugs that started with
Chrome Canary tab reorder (0.1.827) and culminated in the Gmail PWA
cycling regression fixed here. Cmd+Tab now reliably cycles to the correct
previous target from inside Chrome web apps, after tab reorders, across
window moves, and through rapid focus churn — verified on real hardware
with the exact symptom that motivated this wave.

### Fixed
- **Gmail PWA Cmd+Tab regression, root fix**: 0.1.833 and 0.1.834 guarded the two *notification* paths (didActivate + AX tab-switch), but the real corruption was inside `captureFrontmostBrowserTabIfNeeded`: the AppleScript `fetchFrontTab` runs asynchronously (30–500 ms), and the final `history.markSeen(tab)` hop back to the main thread could land after the user had already switched to a Chrome PWA. The markSeen then stamped the now-stale background Chrome tab with `Date()=now`, silently promoting it above the actual frontmost PWA in LRS order. The main-thread callback now re-verifies that the browser whose tab it just captured is still the current frontmost bundle before writing — otherwise the result is dropped. The 300 ms sampler and the next AX event will supply a fresh capture when the user genuinely returns to that browser.

## 0.1.834 (2026-04-21)
### Fixed
- **Gmail PWA Cmd+Tab regression, part 2**: Same symptom as 0.1.833 but different code path. AX fires `kAXTitleChangedNotification` on the Chrome app element whenever any tab's title changes — **including background tabs while Chrome isn't frontmost**. Dynamic counters (Gmail's unread badge, YouTube timers, etc.) triggered this ~once per second. WOS's `TabFocusObserver.onTabSwitch` handler then marked the Chrome front tab as seen, promoting it above the actual frontmost Gmail PWA in LRS order and causing Cmd+Tab from inside Gmail to show Gmail at index 1 again. The handler now cross-checks the firing bundle against `NSWorkspace.shared.frontmostApplication` and drops events from background browsers. The 300 ms sampler still covers the "AX is silent" case.

## 0.1.833 (2026-04-21)
### Fixed
- **Gmail PWA (and other Chrome web apps) returning to itself on Cmd+Tab**: Chrome's parent process fires `didActivateApplicationNotification` for `com.google.Chrome` multiple times per second while one of its PWA children (Gmail.app, GitHub.app, Claude.app — anything under `Chrome Apps.localized`) is the actual visibly-frontmost app. Every spurious notification bumped Chrome's LRS `lastSeenAt` above the PWA's, so when you were on Gmail and pressed Cmd+Tab the HUD showed Gmail itself at index 1. WOS now cross-checks each `didActivate` against `NSWorkspace.shared.frontmostApplication` and drops the notification when the reported app isn't actually frontmost — keeping LRS order honest and also preventing the bogus `activationToken` bumps those transient activations were causing. Symptom should disappear immediately after install.

## 0.1.832 (2026-04-21)
### Fixed
- **LRSHistory self-rename recency guard (H1)**: `renameStableID` now early-routes to `refreshTargetPreservingRecency` when caller passes matching old/new stableIDs — previously the atomic remove-then-upsert would drop the existing entry before the max-preserving check could read it, silently regressing recency. No current caller hit this, but the engine must not depend on caller discipline.
- **TabFocusObserver deinit use-after-free hardening (H2)**: `deinit` now calls `stopAll()` before freeing the UUID token pointer, guaranteeing no AX callback can deref a freed refcon if the observer is ever deallocated while notifications are in-flight. Latent today because AppDelegate holds the observer for the app's lifetime, but a lifecycle refactor would have surfaced this as random crashes.

### Improved
- **CycleEngine preserves user selection across cycle changes (M2)**: when the target list genuinely changes mid-cycle (a tab opened/closed), if the user's previously-selected target's `stableID` is still present, we now resume from that target's new index and advance in the requested direction — instead of forcing the cursor back to index 1 under the user's finger.
- **HistoryReconciler single-use contract documented + explicit reset()**: added an explicit `reset()` method for test/advanced use and made the single-use-or-reset contract explicit in the class doc. The consumption sets still accumulate across `match()` calls, but reuse now has a sanctioned escape hatch.
- **Rate-limited privacy-unknown log (M3)**: the fail-safe "Privacy read failed for …" diagnostic now fires once per browser per session instead of on every sample. A Chromium fork that permanently lost `mode` no longer drowns the log.
- **Main-thread assertions on TabFocusObserver observe/stopObserving**: failures surface immediately via assert rather than as intermittent UI glitches on a future off-main caller.

### Documented
- Design rationale for `HistoryReconciler` content-match-only-when-present, app-entry skip, and single-metadata-key policies.
- `LRSHistory.trimIfNeeded` perf note on the O(H log H) sort.

## 0.1.831 (2026-04-21)
### Fixed
- **Hotfix: revert perf wave 1 from 0.1.830.** The perf changes shipped in 0.1.830 introduced a regression in the Cmd+Tab LRS cycle — the currently-active target would appear as the next cycle destination on activations from certain browser-backed apps (e.g. Gmail as a Chrome PWA). Reverted `AppDelegate.swift`, `LRSHistory.swift`, `AppleScriptEngine.swift`, and `StatusItemController.swift` to their 0.1.829 state; no other 0.1.830 changes are affected.

## 0.1.830 (2026-04-21)
### Improved
- **Idle CPU Reduction (Perf Wave 1)**: Six targeted fixes collapse background CPU use substantially on real workloads.
  - **Sampler gated on AX liveness (P1)**: The 300 ms front-tab sampler now skips its tick when the AXObserver delivered a tab-focus event within the last 5 seconds. Previously the sampler fired ~3 AppleScripts per second while any browser was frontmost, even though AX was already handling the same work. On cooperating browsers (Chrome, Brave, Arc, Safari, etc.) the sampler now stays quiet almost all the time; it re-engages only when AX has been silent — preserving the "fallback for uncooperative forks" behaviour it was designed for.
  - **Status-bar menu no longer rebuilt every second (P2/P3)**: The accessibility watchdog fires `reconcileHotkeyState` once per second forever. Each call used to unconditionally rebuild the entire status-bar menu (15 items, a full history sort, tooltip computation) whether the state had changed or not. Both the reconcile function and `StatusItemController.setHotkeyStatus` now short-circuit when the snapshot tuple is unchanged. On a typical session this eliminates ~60 full menu rebuilds per minute.
  - **`LRSHistory` sorted-entries cache (P4)**: `topEntries` and `orderedTargets` hit the shared LRS store dozens of times per minute (cycle, menu, reconcile). Each call used to re-sort the whole dictionary. Now a recency-descending view is materialised once per mutation and reused; callers pay O(limit) for a prefix read instead of O(H log H) per call.
  - **AppleScript compile cache (P5)**: Every `NSAppleScript` call used to re-parse the target browser's scripting dictionary from source. Stable scripts (front-tab sampler, full-tab enumerator) are now compiled once per source string and reused on the engine's serial queue — safe because serial-queue access is the only NSAppleScript reuse constraint. Each repeat call saves 5-20 ms of compile work.
  - **Cached running-bundle-ID set (P6)**: `pruneUnavailableHistoryTargets` used to call `NSWorkspace.shared.runningApplications` every cycle — an expensive bridged query. Now the set is cached and refreshed only when an app actually launches or terminates (via the existing `NSWorkspace` notifications).
  - **Pre-computed private-window keys (P12)**: `filteredHistoryEntries` used to re-derive the set of "windows currently known to be private" on every cycle by walking the cached tab list. Now derived once per authoritative snapshot inside `rebuildCachedOpenTabIndices`.
- **Log noise reduction (P9)**: Per-sample "Captured tab X" log downgraded from `.notice` to `.debug`. At AX+sampler rates, the eager string interpolation was meaningful background CPU in its own right.

## 0.1.829 (2026-04-20)
### Fixed
- **Hotfix: revert Settings UI overhaul from 0.1.828.** The Settings panel changes shipped in 0.1.828 introduced severe visual regressions on real installs. Reverted `SettingsPanelController.swift` to the 0.1.827 state; no other 0.1.828 changes are affected.

## 0.1.828 (2026-04-20)
### Improved
- **Settings Panel UI Consistency Overhaul**: Settings rebuilt to match current macOS System Settings conventions.
  - Every section now wraps its content in a scroll view with identical pattern — previously only Window Management and Performance scrolled, other long sections could clip on smaller windows.
  - Removed the Shortcuts section's hard-coded 700pt height that clipped rows on small windows and left dead space on large ones.
  - Removed the inline `20pt` / `28pt` hero titles in Window Management and Performance — the window title bar now dynamically reflects the current section name (e.g. "WOS Settings — Performance"), matching System Settings' chrome.
  - `GroupCardView` dropped the 1pt stroke; now a flat inset-grouped container matching current macOS style (Ventura+).
  - Group titles re-typography'd to `11pt` semibold with letter-spacing — the Apple-standard group-header size, distinct from the `13pt` row titles.
  - Sidebar row height `30pt` → `28pt` to match System Settings.
  - Permission status indicators replaced with SF Symbol glyphs (`checkmark.circle.fill` / `exclamationmark.triangle.fill`), matching Apple's own "Allowed / Not allowed" treatment in Privacy & Security.
  - About section no longer has a hard-coded `500×530pt` frame; uses a natural centred layout with a 128pt app icon that responds to window resize.
  - Dropped the "Done" button from the footer — System Settings has no equivalent; the standard window-close button handles it. `windowWillClose` correctly stops the permission-polling timer.
  - Content area no longer wraps content in an `NSVisualEffectView` with `.windowBackground` material; plain `windowBackgroundColor` avoids the material clash with the sidebar's `.sidebar` vibrancy.
  - Standardized all action button bezel styles to `.rounded`; dropped the one-off `.inline` button in the dual-recorder stack.
  - Performance stat cards mirror the new `GroupCardView` style (no stroke) for visual coherence.

## 0.1.827 (2026-04-20)
### Fixed
- **Chrome Canary Tab Reorder**: Activating a tab that was recently dragged left or right now reliably lands on the right tab. The old code silently fell through to the stored (stale) tab index if Chromium's `whose id` filter hiccupped, selecting whichever tab happened to occupy the old position; the new code adds a procedural id-lookup fallback that uses the runtime index and fails loudly if the tab can't be found at all.
- **Chromium Incognito Privacy Leak**: If the AppleScript `mode of window` read failed for any reason, incognito tab titles and URLs could silently be persisted to LRS history. Scripts now emit an explicit `"unknown"` sentinel on failure; the Swift parser treats unknown as `isPrivate=true` (fail-safe) and logs a diagnostic so a future Chromium dictionary change is immediately visible.
- **Chromium Window Reconcile**: Browser-window drift reconciliation compared the stored entry's normalized window identifier (`"windowA"`) against the candidate tab's full identifier (`"windowA:GUID"`), which silently never matched for any Chromium browser. Fixed by normalizing both sides of the comparison.
- **Post-Activation Verify False Positives**: `frontBrowserTabMatchesTarget` now normalizes window identifiers before comparing, so a transient empty GUID in the front-tab sample doesn't falsely conclude that activation failed and trigger a spurious `activateWindow` fallback.

### Improved
- **Cycle Stability Under Reconcile**: Same-cycle detection now keys on `stableID` sequence instead of full structural equality. A background reconcile that updates only a tab's metadata (title, URL, drifted tabIndex) no longer forces the Cmd+Tab cycle to reset mid-keystroke; the cursor stays where the user left it while the HUD silently adopts the fresh labels.
- **Reconcile Performance**: Drift reconciliation now uses pre-built lookup indices (stableID, per-window, per-content-key) built once at O(T). Per-entry lookups drop from O(T) linear scan to O(1) average. At power-user scale (200 history × 500 tabs) this is ~40 ms of main-thread work saved on every cycle — verified by a new performance regression test that asserts <25 ms wall time and linear (not quadratic) scaling.
- **HUD Stability**: Re-render signature now includes the private-tab vignette flag and is documented to exclude fields that change on metadata drift — no more flicker when a reconcile fires mid-cycle with stable visual content.
- **AX Observer Memory Safety**: `TabFocusObserver` replaces the unmanaged raw-pointer `self` pattern with a UUID-token weak-ref registry. A future observer-lifecycle refactor can no longer introduce a use-after-free through a mid-flight AX callback.
- **Chromium GUID-Missing Diagnostics**: Whenever a Chromium tab is captured into LRS without a native GUID (which makes activation after reorder unreliable), a `.notice`-level log fires identifying the browser and source path. Previously the condition was silent.

## 0.1.826 (2026-04-20)
### Fixed
- **LRS Reconcile Recency Safety**: Background tab-drift reconciliation no longer regresses freshly-committed tabs out of Cmd+Tab. A new atomic "preserve-newer-recency" API on the history engine replaces the naive remove+markSeen repair, closing a race where a concurrent cycle commit could be silently overwritten with a stale snapshot timestamp.
- **Deterministic Drift Healing**: Reconcile now iterates history in recency-descending order so the freshest entry always wins when multiple stale rows collide on the same live tab (previously the dictionary's undefined iteration order let older rows claim the slot).
- **Gentler Post-Commit Cleanup**: Post-commit stale sweep delegates to the standard miss-counter pruner instead of single-shot aggressive removal, so a single spurious URL change during activation navigation no longer flushes valid tabs.
- **AnyDesk-Class Activation**: Apps that refuse `activate(options:)` (AnyDesk, some RDP clients) are now raised via `NSRunningApplication.activate(options: .activateAllWindows)` fallback, so they reliably come to the foreground on cycle commit.
- **Tab-Switch Reliability**: Fixed several edge cases in front-tab sampling — eager fetch is now budgeted to stay within the activation SLA, pending samples are never dropped, and AX tab-focus coverage was widened to more Chromium variants.

### Improved
- **Full-URL Quick Search**: Browser tabs index the complete URL (including path and query) into the quick-search corpus, so you can find tabs by deep-link fragments — not just domain + title.
- **Settings UI Overhaul**: Settings panel rebuilt with cleaner sectioning, live shortcut recording feedback, and consistent control sizing across tabs.

## 0.1.808 (2026-04-16)
### Fixed
- **History Pruning Resilience**: Hardened the LRS history against transient background refresh failures.
- **Improved Context Differentiating**: Differentiated between authoritative user-triggered scans and periodic background updates.

## 0.1.807 (2026-04-16)
### Fixed
- **History Stability**: Resolved a critical issue where background scans could prematurely prune history entries due to transient browser unresponsiveness.
- **Resilient Pruning**: Increased background pruning threshold from 2 to 8 misses and extended recency protection to 10 minutes.
- **Chromium Scans**: Hardened AppleScript to ensure proper error propagation instead of returning empty results on timeout.

## 0.1.805 - 2026-04-16

### PERFORMANCE_STABILITY_MILESTONE

### Fixed
- **iTerm/Terminal Tab Synchronization**: Resolved an issue where terminal tabs were not immediately visible in the HUD upon application focus. 
- **AppleScript Engine Resilience**: Hardened `iTerm2` and `Terminal` front-tab capture scripts to handle transient null states and focus-switching races.
- **Synchronous Fetch Unblocking**: Modified the `Cmd+Tab` logic to allow non-contending browsers (Safari, iTerm, Firefox) to perform synchronous front-tab fetches even if a background scan is in flight for another browser, ensuring zero-latency tab registration.

### Improved
- **Staleness Guard System**: Implemented `activationToken` invalidation on manual app switches to prevent "snap-back" focus theft.
- **Diagnostic Logging**: Added high-resolution latency and success tracking for front-tab sampling to monitor interaction health.

## 0.1.802 - 2026-04-15

### PERFORMANCE_STABILITY_MILESTONE

### Fixed
- **Multi-Instance Browser Collision**: Implemented PID-targeted AppleScript execution for all Chromium-based browsers (Chrome, Brave, Arc, Atlas, etc.). This ensures WOS accurately tracks and switches targets when multiple instances/profiles of the same browser are running concurrently.
- **Cross-Instance Identity Hardening**: Updated `LRSTarget.stableID` to incorporate the process ID (PID) for browser windows and tabs lacking native GUIDs. This prevents history collisions and "target jumping" in multi-instance environments.
- **Terminal Same-Title Disambiguation**: Hardened tab identification and drift healing for iTerm and Terminal to ensure reliable switching even when multiple tabs share identical titles.

### Improved
- **AppleScript Engine Isolation**: Refactored the internal engine into twin serialized lanes—Shared (background enumeration) and Activation (high-priority switching). This guarantees the **<50ms SLA** for user actions by preventing background scans from blocking foreground commits.
- **Architectural Documentation**: Expanded `ARCHITECTURE.md` and `LESSONSLEARNED.md` with detailed breakdowns of the lane isolation strategy and Chromium PID fallbacks.
- **Test Coverage**: Added 11 new test suites (30+ tests) validating concurrency, cross-instance isolation, LRS persistence, and performance SLAs.

## 0.1.747 - 2026-04-14

### Added
- **Logical Identity Deduplication**: Introduced a surgical deduplication pass in the `Cmd+Tab` snapshot engine. WOS now recognizes targets by their logical content (URL + Title) rather than just internal IDs, completely resolving "tab drift" issues in Safari and Firefox caused by tab index mutations.

### Improved
- **HUD Focus Stealing Recovery**: Implemented an `onTapDisabled` hook to automatically detect and notify the user when the keyboard event tap is temporarily intercepted by system-level prompts or secure fields (e.g., 1Password).
- **Sub-50ms Interaction Latency**: Optimized all HUD-related dispatches to use the `.userInteractive` Quality of Service (QoS). This ensures cycle actions are never delayed behind background tab-cache processing on the main thread.
- **Developer Documentation**: Added `LRSAnalysis.md` to the repository, providing a detailed technical breakdown of the history drift root causes and the "Shadow Duplicate" design pattern.


## 0.1.743 - 2026-04-12

### Improved
- **Recent Files HUD Polish**: Aligned UI components with the Clipboard History HUD, including consistent 24px margins and a crisp horizontal divider (removed alpha transparency for better contrast).
- **HUD Consistency**: Standardized vertical spacing (14px header-to-divider) across all HUDs for a premium, unified design language.
- **Finder Proxy for Recents**: Implemented a robust `NSAppleScript` proxy to bypass macOS Seatbelt (Sandbox) restrictions when adding files to Recents. It now applies an Orange label, adds the `WOSRECENT` tag, and triggers a Spotlight re-index via `mdimport -u` for instant visibility in Finder.

### Fixed
- **"Add to Recents" (Shift+Enter)**: Restored functionality in the sandboxed environment by routing file metadata updates through the Finder Proxy, ensuring files appear in Finder's recents list even under strict sandbox constraints.


## 0.1.720 - 2026-04-11

### Improved
- **Intelligent "Jump to File" (`Cmd+Shift+G`)**: Re-engineered path detection to work on **selected text** across any application, not just the clipboard.
- **Universal Selection Retrieval**: Implemented a robust "Copy Trick" fallback for apps that don't support standard Accessibility text attributes (like Chrome, Discord, or VS Code).
- **Clipboard Restoration**: Using the Jump-to-File selection retrieval is now completely transparent; WOS automatically saves and restores your original clipboard items within milliseconds.
- **Smart Path Cleaning**: Added aggressive trailing punctuation stripping (e.g., handles `/tmp/test.log.` or `(/Users/name/file)`) and support for non-breaking spaces, ensuring jump success even when paths are part of conversational text.

### Fixed
- **Finder Recents Visibility**: Resolved a deep-rooted issue where files added to Recents via the Recent Files HUD did not immediately appear at the top of the macOS Finder "Recents" list. WOS now explicitly updates both `contentModificationDate` and `contentAccessDate` (which maps to Spotlight's `kMDItemLastUsedDate`), applies a **`WOSRECENT` Finder tag** for reliable manual grouping, and triggers an immediate **Spotlight re-index via `mdimport`**, ensuring instantaneous visibility in Finder's Date-Opened and Tag-grouped views.

### New
- **Advanced HUD Shortcuts**: Added `Cmd+Shift+R` and `Shift+R` as native keyboard shortcuts in the Recent Files HUD to "Add to Recents" (Touch) the selected file, supplementing the existing `Shift+Return` workflow.

## 0.1.714 - 2026-04-11

### Improved
- **High-Performance Browser Automation**: Optimized Chromium-based tab switching (Chrome, Brave, Arc) by replacing slow procedural loops in AppleScript with native `whose` filters. Activation latency for sessions with 100+ tabs reduced from ~20s to <100ms.
- **Bulk Tab Discovery**: Redesigned background tab enumeration to use "Bulk Property Queries." Instead of querying titles/URLs one-by-one, WOS now retrieves all metadata in monolithic blocks, reducing full-profile discovery time from >30s to <500ms.
- **Visual Feedback & UI Polish**: Added global status notifications for "Copy Path" (Cmd+C) and "Note File" (Shift+Return) with a consistent 5-second auto-fade logic.
- **Clipboard HUD Unified**: Refined Clipboard History HUD with vertical centering, smart text truncation, and movable panel support to match the Recent Files HUD aesthetics.

## 0.1.712 - 2026-04-11

### Fixed
- **Recent Files & Clipboard HUDs**: Fixed a critical crash (Must only be used from the main thread) when using keyboard navigation in the HUDs. All key handling is now correctly synchronized with the main thread.

## 0.1.701 - 2026-04-10

## 0.1.700 - 2026-04-10

## 0.1.699 - 2026-04-10

## 0.1.697 - 2026-04-10

## 0.1.696 - 2026-04-10

### Fixed
- **Stale Activation Logic (Phase 1)**: Added a validation gate inside the AppleScript serial execution lock to prevent stalled tasks from stealing focus if a newer activation has since been requested.

## 0.1.691 - 2026-04-09

### Added
- **Jump to File (`Cmd+Shift+G`)**: Directly reveal a file in Finder if its path is in the clipboard. Automatically handles tilde expansion (e.g., `~/Desktop`) and validates file existence before jumping.

## 0.1.574 - 2026-04-04

### Improved
- **Auth Pipeline Resolution**: Successfully integrated GitHub CLI capabilities back into the primary release pipeline.


## 0.1.572 - 2026-04-04

### Added
- **Local AI Inference (Apfel)**: Introduced seamless integration with the open-source Apfel engine for 100% local, privacy-preserving AI inference directly within WOS.
- **Glassmorphic AI Chat UI**: Designed a beautiful, transient chat panel that slides over active windows, providing instant access to intelligent completion without data leaving your Mac.
- **Global AI Shortcut**: Assigned `Cmd+Opt+A` as the default global trigger for Apple Intelligence features, designed to minimize system overrides.
- **Configurable Intelligence Shortcut**: Users can now freely remap the AI chat trigger alongside primary keyboard cycling in Settings -> Shortcuts.

## 0.1.566 - 2026-04-04

### Fixed
- **Private Browsing Ghost Item Restoration**: Resolved a regression where private and incognito browser tabs stopped appearing in the Cmd+Tab HUD as anonymized ghost items. Removed the hardcoded UI filters that incorrectly dropped them from display.
- **Chromium Private Tab Detection**: Corrected the AppleScript `mode` check dictionary string from `"private"` to `"incognito"` for Chromium-based browsers, ensuring accurate identification of active private windows.
- **LRS Ordering for Private Windows**: Fixed an issue where private tabs were frozen in the LRS history order by removing hardcoded drops in both the eager Cmd+Tab sampler and the periodic background sampler. Private tabs are now securely anonymized and reliably shuffled to the front of LRS history seamlessly when activated.
- **Chromium Window Identity Alignment**: Updated `windowIdentityKey` to continuously strip the `:tabID` suffix for Chromium browsers, guaranteeing that the stale history cross-check accurately categorizes and purges window-level history.

## 0.1.558 - 2026-04-03

### Added
- **Activation staleness protection** (`activationToken` + `cycleGeneration`): Two-layer guard that prevents queued preview/commit activations from stealing focus after the user has moved on. During rapid Cmd+Tab cycling, only the latest activation runs — all superseded closures bail immediately. Commit activations additionally check `NSWorkspace.shared.frontmostApplication` to detect manual app switches.
- **Slow-enumeration warning banner**: When a browser's tab scan exceeds the configurable threshold (default: 5s), a 🐢 tortoise-icon StatusHUD toast appears above the Cmd+Tab HUD: `"Chrome tab scan took 7.2s — switching may feel slow"`. Auto-dismisses after 5 seconds.
- **Settings → Performance → Slow Scan Warning**: New stepper field (0–30 seconds) to configure or disable the slow-enumeration warning threshold.
- **Per-browser latency in `BrowserTabsSnapshot`**: Added `browserLatencies: [BrowserKind: Double]` to expose per-browser enumeration timing to the caller.

### Fixed
- **Frontmost-app guard for commit activations**: Commit-phase AppleScript work now checks whether the user manually switched away before executing. Prevents focus-stealing when the user clicks into another app while a commit's AppleScript is still queued behind Chrome's serialised handler.

### Reverted
- **`runAppleScriptDirect` experiment**: The direct NSAppleScript bypass of AppleScriptEngine caused instability due to NSAppleScript's process-global mutex. All activation calls remain on the serialized engine queue. The correct latency mitigation is staleness protection, not queue bypass.

### Documentation
- **ARCHITECTURE.md**: Added "Activation Staleness Protection" section documenting the `activationToken`, `cycleGeneration`, and frontmost-app guard patterns. Added "Slow-Enumeration Warning" section documenting the StatusHUD overlay, priority chain, and `BrowserTabsSnapshot.browserLatencies`. Added anti-patterns for removing token/frontmost guards.
- **LESSONSLEARNED.md**: Added "Activation Staleness: The activationToken + cycleGeneration Pattern" and "NSAppleScript Thread-Safety Violation: The runAppleScriptDirect Experiment" entries.

## 0.1.544 - 2026-04-01

### Added
- **AXObserver push-based tab tracking** (`TabFocusObserver.swift`): WOS now registers one `AXObserver` per running browser and iTerm process. When the user clicks a tab or switches sessions, macOS delivers a `kAXFocusedUIElementChangedNotification` immediately on the main run-loop — zero polling latency. Tab switches are now detected and recorded in LRS history before the 300ms timer even fires.

### Fixed
- **Restored `!tabCacheRefreshInFlight` guard in `performCycleStep`**: The exact v0.1.530 algorithm is back. `fetchFrontTab` runs synchronously (~5-20ms) when Chrome is idle, and is silently skipped when a full tab enumeration is in flight — preventing the ~900ms main-thread block that was causing Cmd+Tab latency.
- **Multi-instance Chrome detection**: WOS detects when more than one Chrome instance is running (e.g. spawned by an AI automation tool) and shows a HUD warning. This is documented in `KNOWNISSUES.md` as the root cause of all tab-drift issues between v0.1.530 and v0.1.542.

### Improved
- **300ms timer kept as fallback**: The periodic sampling timer now acts as a staleness-correction safety net for apps that don't reliably fire AX notifications, rather than the primary tracking mechanism.
- **KNOWNISSUES.md**: Added detailed multi-instance Chrome post-mortem — why it causes tab drift, how Launch Services routes AppleScript to the wrong instance, and the full regression chain from v0.1.530–v0.1.542.
- **LESSONSLEARNED.md**: Documented the `tabCacheRefreshInFlight` guard as load-bearing. Added rule: "if tab drift is reported, check for multiple browser instances first — it is almost certainly an environment problem, not a code bug."

## 0.1.530 - 2026-03-31

### Changed
- **"Other space" HUD message**: Updated text from "On another desktop — preview skipped" to "On another desktop space. Preview skipped to avoid UI flickering." — clearer wording that explains *why* the preview is suppressed.
- **Status HUD icon color**: Changed from `systemBlue` (saturated bright blue) to a muted slate blue-grey (`#61708C`) — keeps the breathing animation but with a more neutral, less eye-catching tint.

## 0.1.527 - 2026-03-31

### Build
- Rebuild of v0.1.526 for local testing; no code changes.

## 0.1.526 - 2026-03-31

### Fixed
- **Live preview "switches back" bug**: After hovering over a Chrome/Brave/Canary tab during Cmd+Tab, the live preview enqueued an async `activateTab` work item on `targetActivationQueue`. This work item ran `chromiumActivateScripts` which called AppleScript `activate` unconditionally — even during preview. Because `targetActivationQueue` is fire-and-forget, the AppleScript `activate` could fire 100–300ms after dispatch, well after the user had committed to a different app. The browser would be pulled back to the foreground, overriding the committed target. Fixed by adding `if not (isPreview) then activate end if` guard to all activation scripts: `chromiumActivateScripts` (`byWindowID`), `chromiumActivateWindowScripts` (`byWindowID` + `byTabMetadata`), and `activateWindow` inline scripts for Safari, iTerm, and Terminal.

## 0.1.525 - 2026-03-31

### Documentation
- **LESSONSLEARNED.md**: Added three new lessons — Chromium AppleScript window IDs vs macOS CGWindowNumbers, concurrent `fetchAllOpenBrowserTabs` blocking the main thread via Chrome's serialised AppleScript queue, and the visual cue pattern for silent feature suppression.
- **ARCHITECTURE.md**: Added `Chromium AppleScript Window IDs vs macOS CGWindowNumbers` subsection in Browser Strategy (two-stage PID fallback pattern), `§8 Post-Commit Tab Enumeration and AppleScript Queue Contention Guard` in Performance Architecture (`tabCacheRefreshInFlight` as a global congestion signal with measured impact), and three new `What Must Not Be Reintroduced` rules.

## 0.1.524 - 2026-03-31

### Fixed
- **2nd Cmd+Tab HUD delay (1-2 seconds)**: After committing a browser tab, `schedulePostCommitStaleCleanup` fires a full `fetchAllOpenBrowserTabs` 350 ms later. With 193 open Chrome tabs this took ~900 ms and monopolised Chrome's AppleScript handler. A rapid 2nd Cmd+Tab would (a) launch a duplicate concurrent enumeration and (b) call `fetchFrontTab` synchronously on the main thread — which Chrome serialised behind both in-flight enumerations, blocking the main thread for ~900 ms. Fixed by having `schedulePostCommitStaleCleanup` claim `tabCacheRefreshInFlight = true` before dispatching (deduplicates fetches) and skipping the synchronous `fetchFrontTab` in `performCycleStep` when any tab fetch is already in-flight.

## 0.1.522 - 2026-03-31

### Fixed
- **Antigravity invisible in Cmd+Tab HUD**: `com.google.antigravity` was incorrectly classified as a Chrome browser variant, causing WOS to suppress it from the switcher whenever Chrome had open tabs. Removed it from Chrome's bundle ID list and AppleScript name tables.
- **Live preview (peek-a-boo) broken for Chrome, Brave, Chrome Canary**: Chromium browsers report internal window IDs from AppleScript that don't match macOS `CGWindowNumber` values, so the "Current Space Only" check always returned false for all Chromium tabs — silently blocking preview. Fixed with a PID-based Space check fallback for Chromium.
- **"On another desktop" preview now shows a visual cue**: When live preview is suppressed because a browser tab is on a different Space, WOS now shows a brief status HUD toast — *"On another desktop — preview skipped"* — instead of silently doing nothing.

### Removed
- **"Show Permissions…" menu item**: Permissions are fully accessible in Settings. The redundant menu item has been removed to keep the status bar menu clean.


### Improved
- **Reliable Rapid Tab Tracking**: Clicking through multiple tabs quickly in Safari, iTerm, or any tracked browser now reliably captures all visited tabs in Cmd+Tab.
    - **Root Cause**: The front-tab sampler ran every 2.0 seconds. Visiting 4 tabs within a 2-second window meant only 1-2 of them were recorded in LRS history; the rest were invisible to Cmd+Tab.
    - **Fix**: Reduced the sampling interval from 2.0s to 300ms. At this rate, any tab visited for more than 300ms is guaranteed to be tracked.
    - **Why 300ms is safe**: WOS now uses in-process `NSAppleScript` (~10-15ms/call) instead of osascript subprocesses. The built-in coalescer prevents pile-up. Net CPU overhead is ~3-5%, and only while a browser or iTerm is the frontmost app.
    - **Why not faster**: The old 20-40ms approach using osascript subprocesses cost ~14% CPU continuously. 300ms with in-process AppleScript is the right balance between responsiveness and efficiency.

## 0.1.512 - 2026-03-30
### Fixed
- **Orion Browser Tabs Restored**: Orion tabs were completely invisible in Cmd+Tab due to three compounding script bugs.
    - **Bug 1 - Undefined Variable**: `orionFrontTabScript` used `tabURL` in its return statement but only ever defined `tURL`. This caused an immediate AppleScript runtime error, making the primary Orion fetch script return nil on every call.
    - **Bug 2 - Unprotected WebKit Fallback**: `webKitFrontTabScript` assumed `index of current tab of frontWindow` is always available. Orion lacks this property (documented in a previous comment). The unprotected call threw, failing the first fallback script too.
    - **Bug 3 - Mode Check Killing All Tab Enumeration**: `webKitAllTabsScript` called `mode of w is "private"` without a nested `try`. If Orion's window didn't support `mode`, the outer try block aborted, silently skipping every tab in that window during background scanning.
    - **Fix - `orionFrontTabScript`**: Complete hardening - uses `front window` properly, all property reads wrapped in `try`, adds correct `isPrivate` detection via `mode is "private"`, returns the required 5 fields.
    - **Fix - `webKitFrontTabScript`**: `tabIndex` now defaults to `1` with a `try` block around `index of current tab`. Safe for all WebKit browsers that lack this property.
    - **Fix - `webKitAllTabsScript`**: `mode of w is "private"` wrapped in nested `try` so a failed privacy check never silently aborts a window's entire tab enumeration.

## 0.1.509 - 2026-03-30
### Improved
- **Zero-Lag Cmd+Tab Tab Tracking**: Eliminated the 2-second delay before newly-visited tabs appear in Cmd+Tab.
    - **Eager Front-Tab Fetch**: On the first keypress of every Cmd+Tab session (`isInitialStep`), WOS now synchronously fetches the frontmost browser/terminal tab and calls `markSeen` before building the HUD snapshot.
    - **No Polling Overhead**: This replaces the previous approach of relying solely on the 2-second background sampler. The eager fetch adds zero CPU overhead because it only runs once per Cmd+Tab session, on demand.
    - **Always Current**: The tab you are looking at right now is always visible the instant you press Cmd+Tab, with zero wait time.

## 0.1.508 - 2026-03-30
### Fixed
- **iTerm and Terminal Tabs Restored in Cmd+Tab**: iTerm and Terminal tabs had silently disappeared from the switcher after the `isPrivate` field was added.
    - **Root Cause**: When `isPrivate` detection was introduced, `fetchFrontTab`'s parser was updated to require 5 fields. However, iTerm, Terminal, and Firefox `fetchScripts` were never updated and still only returned 4 fields (they have no private mode). This caused every iTerm/Terminal/Firefox front-tab parse to silently fail, so those tabs were never added to LRS history.
    - **Fix - Scripts**: iTerm, Terminal, and Firefox `fetchScripts` now emit `""` (empty URL) and `"false"` (not private) as the 4th and 5th fields.
    - **Fix - Parser**: `fetchFrontTab` now accepts 4 or more fields (`>= 4`). If the 5th `isPrivate` field is absent, it defaults to `false`. This makes the parser resilient to any future app type that doesn't expose a private-mode concept.

## 0.1.507 - 2026-03-30
### Fixed
- **Persistent -2741 AppleScript Syntax Error Eliminated**: A compile-time AppleScript error was firing every 2 seconds, affecting front-tab sampling for Chrome, Brave, and Arc.
    - **Root Cause**: `chromiumFrontTabScript`, `arcFrontTabScript`, and `arcAllTabsScript` all contained unprotected `mode of window` calls. Not all Chromium-based browsers expose `mode` in their AppleScript dictionary, causing OSA parse error -2741 ("Expected end of line but found property").
    - **Fix**: All three scripts now initialize `isIncognito`/`isPrivate` to `"false"` before a `try` block. If the `mode` property is unavailable, the default propagates safely through the pipeline.
    - **Guarantee**: Normal tabs can never be blocked by a failed `mode` check. `isPrivate` only becomes `true` when the browser explicitly confirms it.

## 0.1.506 - 2026-03-30
### Added
- **Proactive Automation Permission Check**: WOS now probes TCC (Transparency, Consent, and Control) permissions for all registered browsers and iTerm at startup (3-second delay) and on demand via the menu bar.
    - **Startup Alert**: If any app is TCC-blocked, a warning dialog lists the blocked apps and offers three actions: "Request Permission" (triggers system prompts), "Open Automation Settings", or "Later".
    - **"Show Permissions..." Menu Item**: Added to the status bar menu between Settings and Help so the permission status can be checked anytime after launch.
### Fixed
- **Safari Start Page / Favorites Crash**: Safari's `mode of window` AppleScript call threw a runtime error on non-standard window types (Favorites, Start Page). Wrapped in `try/catch` with a `"false"` default in both `safariAllTabsScript` and `safariiFetchScript`.


## 0.1.497 - 2026-03-30
- **Restored Architectural Guardrails**: Re-implemented comprehensive source code documentation that was lost during a recent repository revert.
    - **Self-Documenting Codebase**: Added detailed architectural comments to `AppDelegate`, `BrowserScriptBridge`, `LRSHistory`, `LRSTarget`, `CycleEngine`, and `AppleScriptEngine`.
    - **Design Constraint Visibility**: Explicitly documented the Hybrid Adaptive Discovery (TCC fallback), Static Cycle Snapshot (UI Lockdown), and Serialized AppleScript execution patterns in the source files.


## 0.1.496 - 2026-03-30
- **Incognito & Private Tab Protection**: Implemented a comprehensive privacy architecture to ensure private browser tabs are never persisted to disk or included in exports.
    - **Proactive Filtering**: Added a centralized guard in `LRSHistory` that prevents incognito/private targets from ever being recorded in history.
    - **Hardened Detection**: Updated AppleScripts for Safari, Orion, Chrome, Arc, and Edge to support privacy mode detection with robust `try/catch` fallbacks.
    - **Therapy Exclusion**: Tab Therapy now explicitly ignores private tabs during stale-tab analysis and multi-format exports (.txt, .html, bookmarks).
    - **Pattern Matching Cleanup**: Updated over 30 internal `LRSTarget` pattern matches to support the new privacy-aware enum signature.

## 0.1.485 - 2026-03-30
- **Tab Therapy Analysis Polish**: Terminal sessions (iTerm, Terminal) are now explicitly excluded from therapy scans to avoid accidental closure of development environments.

## 0.1.480 - 2026-03-30
- **Tab Therapy V2**: Automatic identification of stale tabs (inactive >24h).
- **Multi-Format Export**: Archive stale tabs to .txt, .html, and Netscape Bookmark files.
- **Bulk Cleanup**: One-click reclaim of system performance by closing all stale tabs.
- **Improved Therapy UI**: New SwiftUI-based review list for bulk analysis.


## 0.1.477 - 2026-03-30

### Added
- **Peek-a-Boo (Level Shift) Previews**: Introduced a professional window management mode where browser windows "jump to front" (behind the HUD) when pausing during a cycle, providing an immediate visual context for the selected tab.
- **Current Space Intelligence**: Added a "Current Space Only" setting to skip live previews for tabs on other desktops, preventing "nervous" screen jumping while maintaining focus on the active workspace.
- **WhatsNew Onboarding**: Implemented a high-fidelity SwiftUI-based onboarding screen (`v0.1.476`) that automatically appears on upgrade to showcase new capabilities.
- **Live Preview Discovery Hint**: Added a contextual, non-intrusive hint ("Cmd+Tab+L to toggle") that appears during the first cycle with Peek-a-Boo enabled.
- **Shortcut Toggle**: Integrated `Cmd+Tab+L` as a global shortcut to instantly enable or disable live previews from within any HUD context.

### Improved
- **Settings HUD Reorganization**: Moved the "About WOS" section to the final position in both the Settings sidebar and the status menu for better OS-native alignment.
- **WhatsNew Layout Polish**: Refined the onboarding UI with perfect leading alignment, improved vertical spacing, and monochromatic typography.
- **Iconography**: Updated the Status HUD with semantic SF Symbols (`eye.fill` and `eye.slash.fill`) for the preview toggle feedback.


## 0.1.463 - 2026-03-29

### Added
- **Tab Therapy Countdown**: Integrated a real-time, live-updating countdown into the auto-advance toggle, showing exactly when the next tab will be processed (e.g., "Auto-skip 5s").
- **Tab Therapy Focus Suspend**: Verified and hardened the focus mode to explicitly suspend all automation timers, ensuring users remain undisturbed during focus periods.
- **Tab Therapy Cancellation**: Added a dedicated "Stop Tab Therapy Session" action to the HUD and mapped the **ESC** key to instantly terminate the session.

### Improved
- **Tab Therapy UI/UX Redesign**: Executed a professional layout overhaul with increased vertical breathing room (40px padding), refined typography, and a left-aligned full-width termination control.
- **Status HUD Animations**: Replaced aggressive 1s rotation animations with a premium "breathing" fade effect (1.5s duration, ease-in-out) for a more subtle status indication.
- **Onboarding Iconography**: Updated the system permissions header with a professional `lock.shield.fill` icon to better communicate security and privacy during first-run.

## 0.1.457 - 2026-03-29
### Added
- **Build Metadata Injection**: Integrated Git commit ID and build timestamp into the binary's Info.plist for better version transparency in the "About" screen.
- **Accessibility Automation**: Improved the `grant_accessibility.applescript` to use search-based UI discovery, making it significantly more reliable across macOS versions (Sequoia/Sonoma).
- **Tab Therapy HUD Redesign**: Simplified the "Tab Therapy" HUD with a monochromatic, glassmorphic aesthetic and minimalistic icons.
- **Auto-Advance Toggle**: Added a direct toggle for the auto-skip timer in the Tab Therapy HUD.

### Fixed
- **Release Pipeline Lock**: Moved the `pkill` command to the start of the build/install cycle to prevent file locks during DMG generation and smoke tests.
- **Dev Cycle Speed**: Optimized `dev_reset_install.sh` to skip TCC resets by default, reducing developer iteration time from ~300s to <20s.


## 0.1.435 - 2026-03-27

### Fixed
- **Permissions Status Reliability**: Implemented a robust check for Screen Recording permission that bypasses macOS TCC false-negative system bugs. WOS now verifies status by attempting to read live window titles, ensuring the status dot turns Green immediately when granted.
- **UI Responsiveness**: Refactored the Permissions Panel to update the existing view state instead of re-creating the hosting view. This provides smooth, real-time feedback when toggling permissions in System Settings.
- **Settings Layout**: Fixed the layout bug in the Privacy settings where hint labels were truncated.
- **Automation Baseline**: Restored the stable, high-performance browser activation architecture (v0.1.298 baseline) following a brief experimental divergence.

## 0.1.420 - 2026-03-26

### Fixed
- **Performance**: Removed 8-second background thread latency when activating previously minimized windows by placing `NSRunningApplication.activate` back onto the Main Thread. 
- **Cycle Cancel Bug**: WOS will now elegantly ignore synthetic `Escape` key inputs dispatched by third-party password mangers (like Passpack/Passwork) over the Apple Accessibility API, preventing unexpected termination of the Cycle HUD when traversing protected web pages.

## 0.1.300 - 2026-03-26
### Fixed
- **Performance**: Reverted the automation architecture completely back to the `v0.1.298` (Tab Therapy V1) baseline to permanently resolve the severe cross-browser latency and HUD stalling issues introduced in recent builds.

## 0.1.297 - 2026-03-24

### Added
- **Tab Therapy V1**: A premium browser management workflow to cycle through and clear tab bankruptcy.
  - Decision Panel: Bookmark, Focus (Pomodoro), Snooze, or Close tabs with one-key shortcuts (1-4).
  - Maximized Focus: Automatically activates and maximizes the browser tab for clear review.
  - Decisions as Code: Bookmarks and snoozed tabs are saved to local JSON manifests.
- **Resilient Browser Discovery**: Optimized Tab Therapy scans to use in-process AppleScript and window-level `try/catch` wrappers. This prevents a single "zombie" browser window from stalling the entire scan.
- **Status HUD Integration**: Added real-time feedback for scanning progress and error states (e.g., "No tabs found") using the centralized Status HUD.
- **Hard Reset (TCC)**: Added a "System Reset" menu option to clear Accessibility/Automation permissions and force a clean reinstall when macOS TCC permissions become unstable.

## 0.1.296 - 2026-03-24

### Added
- **Browser Issue HUD Icons**: Improved the "Chrome needs a restart" notification by adding native application icons (Chrome, Brave, Arc) and a clearer restart status symbol (`arrow.clockwise.circle.fill`). This provides immediate visual context for which browser requires attention.

### Improved
- **Quick Search UX Redesign**: Overhauled the `Cmd+Tab+S` footer for a premium, Raycast-like aesthetic. 
  - Reduced footer height from 82px to 48px.
  - Reorganized layout into a single, space-efficient horizontal bar.
  - Refined typography and opacity for better visual hierarchy.
- **HUD Layout Stability**: Fixed overlapping issues in the search footer by making the loading status and result count mutually exclusive. Shortened status text to "Updating..." for a cleaner look.
- **Global Keyboard Support**: Added standardized **ESC** key support to all modal dialogs, including About, Check for Updates, Feedback, Help, and Automation Permission guidance.

## 0.1.272 - 2026-03-24

### Fixed
- **AppleScript Privilege Violations (-10004)**: Resolved the "Only apps are shown" issue by implementing a robust fallback to in-process `NSAppleScript` when background `osascript` subprocesses are blocked by TCC. This ensures reliable tab enumeration even for ad-hoc signed builds.
- **Automation Permission Guidance**: Updated the permission watchdog to recognize `-10004` (Privilege Violation) as a denial signal, ensuring the "Automation Permission Needed" prompt appears correctly to guide users to System Settings.

### Added
- **Tab Therapy**: Introduced a new guided workflow for managing excessive browser tabs. Users can now "sprint" through their open tabs and decide to bookmark, focus, snooze, or close them.
- **Secondary Keyboard Shortcuts**: Users can now assign an additional global hotkey to existing actions for more flexible control schemes.
- **Window Management Alt**: Added support for alternative window tiling shortcuts to support diverse multi-monitor and ultra-wide layouts.

### Improved
- **Quick Search UX**: Refined the search panel footer layout for better typographic balance and removed redundant shortcut hints.

## 0.1.270 - 2026-03-24

### Added
- **Launch WOS at Startup**: Added a new "Launch WOS at startup" toggle in the General settings. This uses the modern `SMAppService` API for reliable, system-native login item management.
- **Customizable Settings Shortcut**: Introduced a dedicated global hotkey to open the Settings panel instantly.
- **New Default Shortcut**: Set the default settings shortcut to `Cmd+Shift+S`.
- **Shortcut Recorder in Settings**: Users can now view and rebind the settings hotkey via the Raycast-style shortcut recorder in the Shortcuts tab.

### Improved
- **Quick Search Footer Refinement**: Cleaned up the `Cmd+Tab+S` footer by removing the redundant "Export" hint and increasing vertical spacing for a more premium, typographically balanced layout.
- **Status Message Visibility**: Added more breathing room for critical status notes (like "Chrome needs a restart") in the search panel footer, improving legibility on high-resolution displays.
- **Menu Bar Integration**: The "Settings..." menu item now dynamically reflects the currently configured global shortcut and key equivalent.

## 0.1.266 - 2026-03-23

### Fixed
- **Zero-Exfiltration Hardening**: Enforced a strict no-network security posture by removing the `com.apple.security.network.client` entitlement. WOS now adheres to a "trustless" architecture where the application process is physically incapable of making outgoing socket connections, fulfilling the project's core privacy promise.
- **Update Check UI Overhaul**: Redesigned the "Check for Updates" dialog with a premium typographic layout using `NSStackView` and `NSTextField`. This eliminated text overlap issues and restored perfect visual balance within a compact 2/3 size (240px wide) frame.
- **Security Transparency**: Removed all automatic browser-launch attempts (AppleScript and NSWorkspace) in favor of a purely informational, user-initiated model. The dialog now clearly explains why it cannot connect to the internet, citing macOS Sandbox/Seatbelt restrictions.
- **Menu Hierarchy**: Swapped the order of "About WOS" and "Check for updates..." in the status menu to better align with standard macOS application patterns where "About" is typically the final item in its group.

### Improved
- **Architectural Guardrails**: Added permanent security constraints to `ARCHITECTURE.md` and `MEMORY.md` to prevent autonomous agents from weakening the sandbox profile in future iterations.

## 0.1.255 - 2026-03-23

### Fixed
- **Zero-Interruption Launch**: Completely removed the startup self-check dialog for an immediate "pure-flow" application start.
- **HUD UX Overhaul**: Widened the `cmd+tab+s` quick search panel to 840pt and moved the "Chrome needs a restart" status alert to a dedicated line in the footer to prevent text crowding.
- **Improved Update Visibility**: Redesigned the "Check for Updates" flow to automatically dismiss HUDs and pin the version alert to the highest window level.
- **Enhanced Downloads Page**: Created a premium, glassmorphic downloads landing page at `wosapp.io/downloads` featuring version information, changelogs, and direct DMG access.

## 0.1.223 - 2026-03-23

### Fixed
- **ChatGPT Atlas, Comet, and Dia Support**: Fixed ChatGPT Atlas tab activation by adding its missing bundle identifier (`com.openai.atlas.web`) and app name alias ("ChatGPT"). Switched Atlas, Comet, and Dia to use dedicated Chromium-based AppleScript properties (`active tab index`) to ensure reliable tab switching.
- **Improved Browser Discovery Performance**: Explicitly confirmed and maintained the existing performance-optimized subprocess tab enumeration for all browsers. This avoids regression in HUD discovery speed and prevents previous `NSAppleScript` thread-safety crashes while ensuring all browsers (including Atlas) are scanned reliably.

## 0.1.222 - 2026-03-23

### Fixed
- **Check for Updates Logic**: Replaced unreliable `NSAppleScript` with the standard `NSWorkspace.shared.open(url)` method for opening landing pages. This resolves an issue where the sandbox was blocking browser triggers during version checks.

### Added
- **Premium Downloads Landing Page**: Built a high-fidelity, interactive `/downloads/index.html` page for the WOS website to provide an elegant version comparison and manual update experience.

### Improved
- **Website Version Alignment**: Automated the synchronization of version metrics and release manifests across all active website variants (`website`, `website-v2`, `website-v3`).

## 0.1.218 - 2026-03-23

### Fixed
- **ChatGPT Atlas, Comet, and Dia Support**: Expanded the hybrid AppleScript strategy to include Perplexity Comet and Dia browsers. This ensures reliable tab activation and window management for these customized applications that use a blend of WebKit and Chromium scripting dictionaries.
- **Unified Hybrid Logic**: Refactored browser-specific switching cases to leverage the shared hybrid helper, improving maintainability for third-party browser wrappers.

## 0.1.216 - 2026-03-23

## 0.1.212 - 2026-03-23

### Fixed
- **Export Alert Visibility**: Increased the window level of the export success and error alerts to `.statusBar + 5`. This ensures they always appear in front of the Switcher HUD and Search Panel, resolving an issue where the "2nd popup" was hidden behind the visual overlays.
- **Regression Test Stability**: Hardened the browser window regression check by utilizing synchronous history seeding, eliminating main-thread async dequeuing delays that were causing intermittent test failures during release builds.
- **Improved Browser Support**: Added preliminary support detection for **Comet** and **Dia** browsers in the `AppDelegate` browser-aware logic.

## 0.1.206 - 2026-03-23

### Fixed
- **Regression Test Reliability**: Hardened internal browser automation timing and history-sampling delays to ensure verification stability during parallel AppleScript execution.

## 0.1.205 - 2026-03-23

### Fixed
- **Thread-Safe AppleScript Execution**: Architected a global, serialized `AppleScriptEngine` to eliminate the multi-threaded race conditions that were causing "rainbow wheel" hangs and intermittent `EXC_BAD_ACCESS` crashes.
- **Synchronization Unification**: Refactored `BrowserScriptBridge`, `AppDelegate`, and `SlideshowController` to use the same shared engine, ensuring all browser automation remains perfectly sequenced.

### Improved
- **Parallel Browser Enumeration**: Optimized background tab discovery to use concurrent subprocesses. This allows WOS to scan multiple browsers (Chrome, Safari, etc.) in parallel while completely bypassing the main AppleScript execution mutex, drastically reducing HUD and Search latency.

## 0.1.202 - 2026-03-23

### Added
- **Orion Browser Support**: Implemented a specialized front-tab sampling strategy for Orion. Since Orion doesn't natively expose active tab indices via AppleScript, WOS now uses a new loop-based resolver to track and switch Orion tabs with 100% accuracy.
- **Immediate Loading Indicator**: Added a professional, minimalistic progress spinner and overlay that appears instantly when `cmd+tab+s` is pressed, providing immediate visual feedback during heavy tab-enumeration stalls.
- **Async Panel Latency Optimization**: Pre-initialized the Search and MenuBar controllers at startup to eliminate the first-run delay.

### Improved
- **Browser Compatibility Sanitization**: Moved **ChatGPT Atlas** back to the "Planned" phase on the website after verifying it currently lacks an AppleScript dictionary. 
- **Architectural Documentation Pass**: Comprehensive updates to `ARCHITECTURE.md` covering Window Management, Security Sandbox, and Arc-specific automation strategies.
- **Log Robustness**: Updated `SESSIONLOG.md` and `DIALOG.md` to reflect the latest project state.

## 0.1.201 - 2026-03-23

## 0.1.189 - 2026-03-23

### Added
- **Full Arc Browser Tab Support**: Deeply refactored Arc automation to iterate through all **Spaces** and **Tabs**. WOS now discovers every open tab in Arc, not just the active one in each window.
- **Stable Tab Identity for Arc**: Switched to using Arc's internal unique `tab id` for identification and activation. This ensures crossing windows or moving tabs between spaces does not break recency tracking or switching.
- **Authoritative Arc Activation**: Activation now uses `tell tab id ... to select`, which reliably brings the correct space and window to the front in a single operation.

### Improved
- **Architecture Documentation**: Updated `ARCHITECTURE.md` with detailed breakdowns of the specialized Arc spaces automation strategy and documented the current technical limitations of Firefox's AppleScript dictionary.

## 0.1.188 - 2026-03-22

### Added
- **Mandatory Permission Guard**: WOS now strictly defers startup and background observation until Accessibility permissions are explicitly granted. A polling loop automatically detects when trust is enabled, providing a seamless "zero-action" onboarding experience once the system toggle is flipped.

### Improved

## 0.1.186 - 2026-03-22

### Improved
- Cleaned the documentation and website assets of all em dashes (—) and en dashes (–) for a more professional, human-readable tone.
- Updated the project roadmap in `TODO.md` with "Apple App Store Registration".
- Hardened the `MEMORY.md` style rules to strictly prevent "AI-generated" punctuation artifacts.

## 0.1.183 - 2026-03-22

### Added
- **Export All Tabs** dedicated keyboard shortcut (`⌥⌘E`) for archiving all open browser tabs instantly.
- Integrated the new **Export Tabs** shortcut into the Settings UI and added a quick-search footer hint.
- Added a **"Reveal in Finder"** button upon successful export to jump directly to your backups.

### Security
- **Strict Filesystem Isolation**: Whitelisted exactly one directory (`~/Documents/WOS-Exports/`) in the kernel sandbox profile for writes. Every other filesystem write is physically blocked by the kernel.
- **Improved Security Documentation**: Comprehensively updated `security.html` to detail the custom kernel Seatbelt architecture, network blackout guarantees, and verifiable sandbox extraction methods.
- **User-Verifiable Trust**: Documented real-world terminal commands (strings/log stream) so users can independently verify WOS's security posture.

### Fixed
- Fixed an issue where the `Could not save tab backup` alert was hidden behind the HUD by moving all alerts to a floating window level.
- Fixed `NSHomeDirectory()` interpolation in the sandbox profile to correctly resolve the user's home path in the C-level `sandbox_init`.
- Resolved a race condition in `Cmd+Tab+S` tab loading during initial launch.


## 0.1.169 - 2026-03-22

### Added
- Integrated premium Raycast-style shortcut recorder with a live popover tracking modifiers instantly.
- Added 'Isolate from Background' toggle to the Visual HUD settings.
- Added Automated QA via Computer Use Agent (CUA) strategy utilizing models like GPT 5.4 or Project Mariner to the TODO pipeline.

### Improved
- Fixed a bug where the `Performance` settings tab appeared completely blank by replacing the legacy `NSTextField` with a fully scrollable, native AppKit `NSTextView`.
- Fixed an initialization bug in the Settings panel requiring users to click it twice after restarting the app by enforcing a `.orderFrontRegardless()` window state.
- Improved Visual HUD appearance consistency across light and dark themes.
- Enhanced all automated smoke tests and regression checks to aggressively clean up their generated browser testing windows upon completion.

## 0.1.145 - 2026-03-21

### Added
- New **Support the Flow** donation page featuring Patreon and multi-network cryptocurrency support (BTC, ETH, SOL, XRP, TON and TRX).
- Integrated premium **"Copied to clipboard" tooltips** for a more interactive payment experience.

### Improved
- Complete **Global Navigation Unification** across all website variants with a professional, anti-wrap design.
- Shortened navigation labels (e.g. "Devs", "How", "Feedback") and refined active-state indicators for better legibility.
- Global sweep to **remove em dashes and en dashes** across all site copy for a more consistent, human-readable tone.
- Fixed missing icon in the "Kernel-Level Privacy & Isolation" feature card and clarified browser compatibility text.

## 0.1.143 - 2026-03-20

### Added
- Comprehensive **Security Architecture** documentation detailing the technical necessity of `CGEventTap` and the robust safeguards in place.
- **Kernel-Level Privacy & Isolation** guide, highlighting the "Zero-Exfiltration" guarantee enforced by macOS's App Sandbox.
- Detailed **Don't Trust. Verify.** walkthrough for independent security auditing via `codesign`, Activity Monitor, and Console logs.
- Premium web design overhaul for the Security page with high-fidelity glassmorphism, dynamic backgrounds, and custom SVG iconography.
- Integrated a new "Built for Flow. Protected by Kernel." call-to-action with sunset-gradient aesthetics on the security pages.

### Improved
- Toned down marketing hyperbole on the main landing page, adding direct links to the technical security breakdown.
- Refined the global site navigation and section spacing for better readability and a more premium feel.
- Improved the "Summary for Verified Privacy" with 100% visible SVG check icons to avoid font-loading issues.

### Removed
- Removed "Menubar app discovery" from the features list as the feature is currently being refactored for better reliability.

## 0.1.139 - 2026-03-20

### Added
- Created a robust concurrent `PerformanceTracker` to accurately record durations for cycle switches, quick search initialization, slideshow loading, and application target commitments.
- Exposed these runtime metrics in a new `Performance` section inside `SettingsPanelController`, calculating live statistical feedback including min, max, mean, median, and a clustered mode for diagnostic power.

## 0.1.138 - 2026-03-20

### Fixed
- Architectured a massive "0ms" latency hook for `Cmd+W` closure operations by aggressively mapping and optimistically popping the active frontmost tab securely out of the history instantly, completely bypassing the OS-level AppleScript fetch delay window so you can instantly sprint back to `Cmd+Tab` with no ghost items.
- Fixed a silent bug inside the cross-browser target pruning system where a mandatory 1.0s history immunity buffer was incorrectly preserving closed target clones during rapid-fire tab deletion strokes.

## 0.1.136 - 2026-03-20

### Fixed
- Reduced cmd+W refresh debounce window from 140ms to 40ms, drastically cutting the time before a closed tab or window is removed from the LRS history and the Quick Search/HUD lists.

## 0.1.133 - 2026-03-20

### Fixed
- Fixed double entries for Quick Search LRS targets by properly collapsing window/tab pairs for the HUD in target records.

## 0.1.131 - 2026-03-20

### Added
- Added a new Tab Slideshow feature triggered via `Cmd+Tab+T` during an active cycle session.
- Users can select an exported tabs `.txt` backup and automatically rotate through each URL.
- Added a `Slideshow` section in the Settings panel to specify the preferred browser and slide duration.
- Display a floating control panel overlay with a "Stop" and "+5s" button to manage the slideshow in real-time.
## 0.1.130 - 2026-03-20

### Fixed
- Fixed an `AppDelegate` logic bug where `BrowserScriptBridge` legitimately returning 0 windows (e.g. for Chromium PWA apps or specific headless Spaces) was falsely interpreted as an internal AppleScript crash. This eliminates ghost tabs and the erroneous "Chrome tabs temporarily unavailable" warning on the visual HUD.
- Improved visual padding and vertical alignment of the HUD search hint, distancing it appropriately from the icon stack.

## 0.1.122 - 2026-03-18

### Fixed
- Fixed LRS ordering for rapid back-and-forth browser tab cycling (e.g. two Chrome tabs) by marking the selected target in history immediately on cycle commit, before activation begins. Previously the history update relied on `didActivateApplicationNotification`, which never fires when cycling tabs within the same browser because the non-activating HUD keeps the browser frontmost throughout.
- Fixed 3-tab pruning bug where only 2 of 3 recently-seen browser tabs appeared in the Cmd+Tab HUD. Root cause: front-tab sampling captures tabs into LRS history every ~0.28s, but the background tab cache only refreshes every ~2.5s. Tabs seen by sampling but not yet present in the cache were falsely pruned by `pruneUnavailableHistoryTargets`. Fixed by adding a recency cutoff — entries seen after the cache snapshot was built are now protected from pruning.
- Fixed tabs disappearing from Cmd+Tab HUD when opening new browser tabs quickly. When new tabs are opened, existing tabs' indices shift, changing their stableIDs. The cycle pruning path only checked exact stableID matches against the cached tab snapshot, missing the match key fallback (URL/title/window-based). Fixed by passing `openTabMatchKeys` to `pruneUnavailableHistoryTargets` in both `switchTargetsForCycle` and `recentTargetsForMenu`.
- Fixed new browser windows (Cmd+N) disappearing from HUD after a few seconds. Browser window entries are only marked once on first capture and their `lastSeenAt` ages quickly past the recency cutoff. Excluded window entries from cache-based pruning in the cycle/menu path; stale windows are cleaned up by post-commit validation instead.
- Fixed Cmd+Q not removing stale browser tabs from the HUD. When a tab no longer exists and `closeTarget` fails, the entry is now checked for staleness and removed if the tab is confirmed gone.

### Added
- Automated tab LRS regression test (`runTabLRSRegressionCheck`) that opens a Chrome window with 3 test tabs, seeds each into LRS history via front-tab sampling, builds a cycle snapshot, and verifies all 3 tabs appear. Wired into `PerfAutomationRunner` and the release build pipeline via `hud_window_regression.sh`.
- Standalone `scripts/tab_lrs_regression.sh` harness for running the tab LRS regression test independently.
### Lessons Learned
- Non-activating panels mean no app-activation notification: any state that depends on `didActivateApplicationNotification` must have an explicit fallback for intra-browser tab switches.
- Front-tab sampling and background cache refresh operate on different cadences. Pruning logic must account for this timing gap by protecting recently-seen entries from stale-cache eviction.
- Tab stableIDs include the tab index, which shifts when tabs are opened or closed. All pruning paths must use match key fallback (URL/title/window) in addition to exact stableID matching.

## 0.1.121 - 2026-03-18

### Added
- Added a dedicated browser window/tab regression harness (`scripts/hud_window_regression.sh`) that runs through the in-app visual HUD automation path.
- Extended perf smoke automation to support `browserWindowRegression` runs, explicit run-count aware pass/fail gating, and cleaner log-window filtering.
- Wired release builds to run the HUD browser-window regression check by default via `RUN_HUD_WINDOW_REGRESSION_TESTS=true`.

### Improved
- Added deterministic app-targeting in perf smoke (`open -a "$APP_PATH" "wos://..."`) so automation executes against the freshly built release app.

### Lessons Learned
- Keep LRS recency authoritative: regression fallbacks should be ephemeral and must not mutate recency order.
- Window/tab edge cases require release-gated regression coverage; ad-hoc manual checks are not sufficient.
- Log collection windows must use local timestamps consistently; UTC/local mismatches silently invalidate test results.

## 0.1.110 - 2026-03-18

### Fixed
- Fixed browser `Window` activation flow by adding dedicated window activation scripts and a fallback to tab activation when direct window focusing fails.
- Improved Chromium-family window targeting reliability by removing index-only metadata matches that could report false-positive success on the wrong window.
- Changed `Cmd+Q` behavior for browser `Window` targets to close the represented tab (non-destructive) instead of attempting to close the whole browser window.
- Removed the bottom-right `T` marker from tab icons to keep browser icon rendering cleaner.

### Improved
- Added a one-command release workflow (`./releasebuild`) that builds WOS and publishes the latest DMG to the website downloads folder.
- Release versioning is now driven by a dedicated `VERSION` file and automatically bumps on each release build.

## 0.1.109 - 2026-03-17

### Added
- Added first-class browser-window LRS targets (`Window`) so recently seen browser windows can be revisited independently from tabs and apps.

### Improved
- `Cmd+Q` now closes selected browser `Window` targets in the cycle HUD (in addition to existing app/tab close behavior).
- Refined tab icon styling by removing the previous badge plate/stack overlay and replacing it with a subtle bottom-right `T` marker.

## 0.1.108 - 2026-03-17

### Improved
- Improved vertical `Cmd+Tab` HUD sizing on notebooks by reserving top and bottom safe-area margins (at least one row stride each) so the panel no longer fills the full visible height.
- Reworked vertical HUD geometry to use consistent row/spacing constants for both visible-row calculation and panel height, keeping layout stable and professional across screen sizes.

## 0.1.107 - 2026-03-17

### Fixed
- Filtered `UserNotificationCenter` from WOS target discovery so the non-activatable system helper no longer appears in `Cmd+Tab` or `Cmd+Tab+S`.

## 0.1.106 - 2026-03-16

### Improved
- Tuned browser-tab overlay styling to a softer look by reducing WOS stack badge opacity and shadow intensity, so the badge appears more subtle and less dark.

## 0.1.105 - 2026-03-16

### Added
- Added a native `Send Feedback...` menu flow with a dedicated in-app feedback panel (category, description, optional attachments) and asynchronous submission.
- Added secure, no-email feedback delivery options:
  - Website-first feedback portal launch (`FeedbackPortalURL`)
  - Webhook-based backend path for Google Sheets / Apps Script
  - Local queue fallback in Application Support when remote delivery is unavailable

### Improved
- Set default feedback portal URL to `https://wosapp.io/feedback`, with compatibility for legacy key `FeedbackProtalURL`.
- Refined browser tab overlay styling to a larger, plate-free monochrome WOS stack glyph directly over browser icons.
- Redesigned the Settings window with a cleaner, more professional tabbed layout (`Shortcuts`, `Search`, `Visual HUD`, `About`) and updated spacing/visual hierarchy.

## 0.1.104 - 2026-03-15

### Fixed
- Fixed `Cmd+Tab+S` selection visibility so keyboard navigation (`Up`/`Down`) now shows a clear active highlight while moving through results, enabling confident `Enter` activation.
- Fixed vertical `Cmd+Tab` HUD selected-item spacing by increasing row geometry and panel sizing so selected icons now have balanced top/left/bottom breathing room.

## 0.1.103 - 2026-03-15

### Fixed
- Fixed `Cmd+Tab+S` keyboard navigation so pressing `Down` from the search field highlights the first visible result, `Up`/`Down` move through results, and `Enter` activates the selected target consistently.

## 0.1.102 - 2026-03-15

### Added
- Added searchable Google Chrome profile entries to `Cmd+Tab+S`, sourced from Chrome's local profile metadata.

### Improved
- Kept the quick-search UI clean by hiding Chrome profiles from the empty-state view and showing them when the user actively searches.
- Changed profile selection behavior to open Chrome's native profile/settings UI for the selected profile, keeping the UX simpler and more reliable.

## 0.1.101 - 2026-03-15

### Fixed
- Replaced Arc's Chromium-style AppleScript handling with Arc-native tab capture, enumeration, activation, and close logic so Arc tabs can appear as individual LRS targets instead of collapsing to the Arc app.

### Improved
- Added polished Up/Down keyboard navigation in `Cmd+Tab+S`, allowing the cursor keys to move directly between the search field and the result list.

## 0.1.100 - 2026-03-15

### Improved
- Redesigned the `Cmd+Tab+S` panel to follow a more Raycast-like visual style with a flatter command bar, darker chrome, slimmer grouped sections, and cleaner result rows.
- Added right-aligned result type labels and simplified row metadata so the quick-search list reads more like a command palette than a traditional table.
- Refined the selected-row geometry so the active highlight now has balanced top/bottom padding that matches the left-side spacing around the icon.

## 0.1.99 - 2026-03-15

### Improved
- Moved the `Search all open browser tabs` control to the bottom area of the `Cmd+Tab+S` panel.
- Realigned the quick-search layout so the search field and content column align more cleanly for a more professional visual rhythm.
- Kept quick-search launch interactions responsive while tabs continue loading in the background.

## 0.1.98 - 2026-03-15

### Improved
- Updated browser-tab overlay styling to a minimal monochrome stack badge (no blue accent), aligned with the WOS menubar icon visual language.

## 0.1.97 - 2026-03-15

### Improved
- Increased browser-tab icon presence in both `Cmd+Tab` and `Cmd+Tab+S` so browser identity (Safari/Chrome/Brave/etc.) is easier to recognize at a glance.
- Refined browser-tab icon styling with a clearer bottom-right stack badge indicator while keeping the main browser icon dominant.
- Hardened Terminal tab enumeration/front-tab detection paths so Terminal tabs are more reliably captured as tab targets (not only the app target).
- Implemented: Cmd+W now triggers an immediate, debounced, single-browser tab-cache refresh (no full multi-browser scan), so closed tabs drop out of Cmd+Tab and Cmd+Tab+S quickly with minimal overhead.

## 0.1.96 - 2026-03-15

### Improved
- When LRS history is empty or has been fully pruned, the initial `Cmd+Tab` HUD now falls back to currently open regular apps instead of showing an empty switcher.
- The frontmost app is pushed to the end of that startup fallback list so the first `Cmd+Tab` still switches somewhere meaningful, closer to native behavior.
- Simplified the selected-item styling in the `Cmd+Tab` HUD by removing the extra border stroke and relying on softer fill + shadow emphasis for a cleaner Spotlight-like highlight.
- Increased HUD icon sizes in both horizontal and vertical layouts so browser identity is easier to distinguish at a glance.
- Removed the redundant `Open Targets` title row in `Cmd+Tab+S` for a cleaner search UI.
- Added tab-level tracking/activation/enumeration support for both Terminal and iTerm so individual terminal tabs can appear in `Cmd+Tab`.

## 0.1.95 - 2026-03-14

### Improved
- Added clearer performance logs for cycle HUD show timing (`mode=initial|advance`) and app activation latency, making Console timing analysis easier for `Cmd+Tab` interactions.

## 0.1.94 - 2026-03-14

### Improved
- Startup self-check now appears only when Accessibility permission is actually missing or disabled, and it dismisses automatically once trust is restored.

## 0.1.93 - 2026-03-14

### Fixed
- Reworked the internal perf automation runner to drive the real WOS actions directly instead of trying to synthesize raw key events from inside the app, which ensures the cycle HUD, quick search panel, and menu bar search panel visibly appear during automated runs.
- Added live perf step progress logging so `perf_smoke.sh` can print ongoing status instead of appearing idle while the on-screen run is executing.

## 0.1.92 - 2026-03-14

### Improved
- Moved automated performance driving into the WOS process itself via the `wos://perf-smoke` URL so the on-screen perf run uses WOS's own Accessibility permission instead of requiring a separate driver process to be authorized.
- Updated `perf_smoke.sh` to trigger visible in-app automation, print progress, and wait for the `Perf automation completed` marker before collecting logs.
- Registered the `wos` custom URL scheme in the packaged app so the internal perf runner works from installed builds.

## 0.1.91 - 2026-03-14

### Added
- Added a local automated performance harness:
  - `scripts/wos_perf_driver.swift` sends real CGEvent hotkey sequences for cycle, quick search, and dedicated menu bar search
  - `scripts/perf_smoke.sh` runs scenarios, captures unified logs, computes latency summaries, and enforces configurable budgets
- Added new latency markers for:
  - cycle hotkey capture
  - quick search shell show
  - menu bar search shell show
- Added optional release integration via `RUN_PERF_SMOKE_TESTS=true ./scripts/build_release.sh`

## 0.1.90 - 2026-03-14

### Improved
- Moved background open-tab enumeration out of the in-process `NSAppleScript` lane and into isolated `osascript` subprocesses so interactive target activation no longer queues behind multi-second browser scans.
- Preserved the AppleScript crash fix for interactive operations while restoring fast foreground switching when background browser refreshes are in flight.

## 0.1.89 - 2026-03-14

### Improved
- Moved frontmost browser tab sampling off the main thread and onto a dedicated coalesced queue so tab tracking can no longer block the event-tap run loop while AppleScript is busy.
- Prevented repeated foreground sampling requests from piling up by coalescing them to the latest browser bundle ID, reducing missed `Cmd+Tab` HUD appearances after rapid tab changes.

## 0.1.88 - 2026-03-14

### Improved
- Cleared the `Cmd+Tab` HUD and yielded one AppKit run-loop turn before activation begins, so the switcher can visually dismiss before expensive browser work starts.
- Moved browser-tab AppleScript activation onto a dedicated background activation queue to keep the UI thread responsive while the actual tab jump completes.
- Added a `Browser target activation latency` log entry for easier timing verification in Console.

## 0.1.87 - 2026-03-14

### Improved
- Hid the visual HUD before target activation begins, so browser-tab commits no longer leave the HUD onscreen while AppleScript work is queued.
- Deferred background tab-cache refresh briefly around browser-tab activation and paused front-tab sampling during an active cycle to reduce AppleScript contention and restore responsive switching.

## 0.1.86 - 2026-03-14

### Fixed
- Serialized all AppleScript execution through a dedicated queue in `BrowserScriptBridge` to prevent concurrent `NSAppleScript` usage from crashing WOS during background tab refresh and front-tab sampling.

## 0.1.85 - 2026-03-14

### Improved
- Added a delayed inline note to the dedicated menu bar search panel so users see `Accessibility scan is taking longer than usual...` if menu bar enumeration is still in flight after 2 seconds.

## 0.1.84 - 2026-03-14

### Fixed
- Fixed a crash when opening the dedicated menu bar search window by replacing an invalid `NSPanel` collection-behavior combination.
- Made dedicated menu bar search panel loading non-blocking: the window now opens immediately and menu bar enumeration completes asynchronously.

## 0.1.83 - 2026-03-14

### Changed
- Switched the dedicated menu bar search shortcut default from `Cmd+Shift+M` to `Cmd+Opt+R` to avoid common app conflicts.
- Added a configurable Menu Bar Search shortcut in Settings, using the same capture flow as the primary and fallback shortcuts.

## 0.1.82 - 2026-03-14

### Fixed
- Registered `Cmd+Shift+M` as a dedicated global hotkey in addition to the event-tap path, making the new menu bar search window open reliably even when the standalone shortcut is not delivered through the cycle tap.

## 0.1.81 - 2026-03-14

### Added
- Introduced a dedicated Thaw-style menu bar browser window with grouped results, richer rows, and direct activation via the new global shortcut `Cmd+Shift+M`.

### Improved
- Up/down arrow navigation and Enter activation now work directly from the menu bar search field, so the dedicated panel behaves like a native searchable command palette.

## 0.1.58 - 2026-03-10

### Fixed
- Refined `Cmd+Tab+S` menu bar section to filter out submenu/action nodes and keep only real menu bar mini-app candidates (e.g., Dropbox/1Password/ProtonVPN-style extras).

## 0.1.57 - 2026-03-10

### Fixed
- Ensured `Cmd+Tab+S` reliably shows menu bar mini-apps by adding a last-good snapshot fallback when live Accessibility enumeration is temporarily empty or unavailable.
- Added fallback diagnostics in logs for menu-bar item sourcing to support quicker debugging.

## 0.1.56 - 2026-03-10

### Fixed
- Improved menu bar extras discovery to enumerate nested pressable AX descendants instead of only top-level nodes, so all available menu bar mini-apps are listed.
- Added deduplication and extra diagnostics for menu bar enumeration (`topLevel/candidates/items`) to aid reliability debugging.

## 0.1.55 - 2026-03-10

### Fixed
- Improved `Cmd+Tab+B` capture reliability by accepting both keycode and unicode-key paths during active cycle handling.

### Added
- `Cmd+Tab+S` now includes a dedicated `Menu Bar Apps` section (under `Open Apps`) listing menu bar extras.
- Menu bar items in Quick Search are directly actionable (click/Enter triggers the corresponding menu bar extra).

## 0.1.54 - 2026-03-10

### Changed
- Switched menu-bar extras shortcut from `Cmd+Tab+M` to `Cmd+Tab+B` to avoid conflicts and improve reliability.

### Fixed
- Added resilient cycle-session recovery for menu-bar shortcut triggering immediately after cycle state drops.

### Docs
- Updated Help text to document `Cmd+Tab+B` for "Show all menu bar apps".

## 0.1.53 - 2026-03-10

### Added
- Added `Cmd+Tab+M` shortcut to open a searchable "Menu Bar Apps" panel from an active cycle session.
- Added menu bar extras enumeration/activation via macOS Accessibility (`SystemUIServer`) so hidden extras can be found and opened quickly.
- Added OpenAI Atlas browser support (with bundle/app-name alias handling).

### Fixed
- Added robust Chromium app-name alias fallback for Google Chrome Canary and Google Chrome Beta so tab detection/activation/close work reliably.

### Docs
- Added `Cmd+Tab+M` to Help shortcut documentation.

## 0.1.52 - 2026-03-09

### Added
- Added session-level quick-search load metrics (`last/avg/max/samples`) and wired them into Help.
- Added dedicated Console metric logs: `Quick search load metric: ...` for regression tracking.

## 0.1.51 - 2026-03-09

### Added
- `Cmd+Tab+S` now opens search panel immediately with native loading spinner and loading text.
- Added async quick-search bootstrap API (`presentLoading`, `updateEntries`, `setLoading`) for panel-first UX.

### Performance
- Removed synchronous tab enumeration from quick-search panel startup path.
- Added tokenized async loading for quick-search results; stale responses are discarded safely.
- Moved high-tab recommendation evaluation after async load completion to avoid blocking initial panel render.

## 0.1.50 - 2026-03-09

### Performance
- Replaced aggressive fixed tab-cache polling with adaptive refresh scheduling.
- Reduced background contention by running tab-cache refresh work at utility QoS.
- Added dynamic backoff/idle intervals and next-refresh telemetry in logs to reduce menubar/UI jitter under browser AppleScript load.

## 0.1.49 - 2026-03-09

### Reliability
- Added hotkey state watchdog/reconcile flow to guarantee emergency cooldown auto-recovery.
- Menubar now shows live hotkey status (`Enabled`, `Paused`, `Cooldown Ns`) to make interception state explicit.
- Unified effective hotkey state management so menu state and `CGEventTap` enablement stay in sync.

## 0.1.48 - 2026-03-09

### Added
- Added new menubar `Help` item documenting all WOS shortcuts (including dynamic primary/fallback, `Cmd+Tab+S` search behavior, stale delete, cancel, and emergency reset).

### Performance
- Commit path now activates target first and runs stale tab cleanup asynchronously post-commit (no pre-commit stale gate).
- Added browser enumeration safeguards:
  - skip browsers that are not running
  - per-browser backoff after parse/timeouts/repeated failures
  - explicit skip/suspend logs for root-cause diagnosis.

### Reliability
- Added emergency panic shortcut `Cmd+Opt+Ctrl+Esc`:
  - cancels cycle and clears HUD state
  - disables WOS hotkeys for 10 seconds
  - auto re-enables hotkeys after cooldown.

## 0.1.47 - 2026-03-09

### Changed
- Refined visual HUD proportions after removing shortcut footer text:
  - reduced panel height in horizontal and vertical layouts
  - tightened internal top/bottom padding and content spacing
  - subtle typographic tuning for the search hint line.

## 0.1.46 - 2026-03-09

### Changed
- Removed the HUD footer line `Shortcut: <key>` from the visual Cmd+Tab switcher.
- HUD footer now shows only the search hint line.

## 0.1.45 - 2026-03-08

### Changed
- Removed the magnifier icon from the `Cmd+Tab+S` search field to avoid layout/alignment artifacts.
- Kept native text editing behavior and clear (`x`) affordance.

## 0.1.44 - 2026-03-08

### Fixed
- Prevented stale closed tabs from lingering in `Cmd+Tab` HUD (both horizontal and vertical layouts).
- Added background open-tab cache refresh and cache-based tab pruning on the cycle path.

### Performance
- Kept `Cmd+Tab` hot path non-blocking by avoiding synchronous full tab enumeration during cycle.
- Added cache freshness/age and tab-pruning source logs to help debug latency and correctness.

## 0.1.43 - 2026-03-08

### Added
- `Cmd+Q` support during active HUD cycle (`Cmd+Tab` session):
  - closes the currently selected target in the HUD
  - app targets: terminates the app process
  - tab targets: closes the tab via browser-specific AppleScript logic
  - removes closed target from history and refreshes HUD list immediately.

## 0.1.42 - 2026-03-08

### Performance
- Removed full browser tab enumeration from the `Cmd+Tab` cycle path.
- `Cmd+Tab` now uses a fast mode (app-pruning only) and keeps full tab enumeration for `Cmd+Tab+S` search mode where it belongs.
- Reduced HUD startup latency by eliminating synchronous AppleScript tab scans from the cycle critical path.

## 0.1.41 - 2026-03-08

### Added
- Detailed cycle-latency logging for HUD startup path:
  - snapshot build timing
  - browser tab fetch timing
  - history prune timing
  - target build timing
  - HUD render timing
- Per-browser open-tab enumeration timing logs in `BrowserScriptBridge`.

### Analysis
- The dominant startup delay before HUD visibility is usually synchronous open-tab enumeration (`fetchAllOpenBrowserTabs`) now executed before `Cmd+Tab` snapshot build.

## 0.1.40 - 2026-03-08

### Fixed
- Improved tab activation reliability for Firefox, Opera, and Orion:
  - added explicit success-flag AppleScript activation flow (instead of silent `try` no-ops)
  - added metadata fallback by tab URL/title when window-id/index activation fails
  - passed tab title/URL through activation path so fallback can target the right tab.

## 0.1.39 - 2026-03-08

### Changed
- Shifted `Cmd+Tab+S` search text field content further right to remove remaining overlap with the magnifier icon.

## 0.1.38 - 2026-03-08

### Fixed
- Simplified `Cmd+Tab+S` search field layout to avoid baseline drift by using only native draw/edit behavior plus rect overrides.
- Increased magnifier-to-text spacing for a cleaner, consistent margin.

## 0.1.37 - 2026-03-08

### Changed
- Moved `Cmd+Tab+S` magnifier further left to align with the panel’s left content rail/divider start.
- Reduced `Cmd+Tab+S` search text size by about 30% for cleaner visual hierarchy.

## 0.1.36 - 2026-03-08

### Changed
- Moved the `Cmd+Tab+S` magnifier icon further left while keeping text inset unchanged, so icon and text can be tuned independently.

## 0.1.35 - 2026-03-08

### Changed
- Tuned `Cmd+Tab+S` search magnifier placement:
  - moved icon closer to the left bezel
  - vertically aligned icon against the text area for cleaner visual balance.

## 0.1.34 - 2026-03-08

### Fixed
- `Cmd+Tab+S` search field now applies consistent text inset for draw/edit/select paths, preventing magnifier overlap in both placeholder and typed text.
- Added reliable all-tabs toggle shortcut handling directly at panel level for `⌥T` (and compatibility with `⌥⌘T`).

### Changed
- Replaced the all-tabs checkbox with a native macOS-style `NSSwitch` control in the search panel.

## 0.1.33 - 2026-03-08

### Fixed
- High-tab recommendation now uses an uncapped open-tab snapshot so the popup count reflects the actual number of currently open browser tabs.
- Adjusted `Cmd+Tab+S` search text inset to prevent first-character overlap with the magnifier icon.

## 0.1.32 - 2026-03-08

### Fixed
- Restored reliable typing/editing in the `Cmd+Tab+S` search field by preserving editable/selectable behavior in the customized search cell.
- Kept Spotlight-like search styling while maintaining proper first-responder text input behavior.

### Changed
- Redesigned browser tab icon treatment to a cleaner, more professional stacked-tab visual style in HUD/search/menu.

## 0.1.31 - 2026-03-08

### Fixed
- Closed apps are now pruned from LRS history before building `Cmd+Tab` and `Cmd+Tab+S` targets.
- Closed browser tabs are now pruned from LRS history against the current open-tab snapshot.
- Prevents stale/closed targets from appearing in cycle HUD, search panel, and recent targets menu.

## 0.1.30 - 2026-03-08

### Changed
- `Cmd+Tab+S` search field now uses larger Spotlight-like search typography.
- Added left inset so the magnifier has clear padding from the search field border.

## 0.1.29 - 2026-03-08

### Fixed
- Reverted `Cmd+Tab+S` search field to native AppKit layout behavior for Spotlight-like rendering quality.
- Removed custom geometry overrides that caused icon misalignment and text clipping.
- Kept larger magnifier/cancel glyph sizing while preserving native centering and text metrics.

## 0.1.28 - 2026-03-08

### Fixed
- Refined `Cmd+Tab+S` search field layout to be closer to Spotlight:
  - explicitly centered magnifier/cancel icons
  - consistent text insets and vertical alignment
  - removed clipping/truncation from the visible typing area.

## 0.1.27 - 2026-03-08

### Changed
- Increased the magnifier icon size in the `Cmd+Tab+S` search field to better match Spotlight-style visual proportions.

## 0.1.26 - 2026-03-08

### Fixed
- Removed the blue/default focus rectangle from the `Cmd+Tab+S` search field.
- Replaced it with a neutral, professional custom focus state to match the HUD style.

## 0.1.25 - 2026-03-08

### Changed
- Restyled the `Cmd+Tab+S` search input toward Spotlight-like appearance:
  - larger search field height and typography
  - stronger rounded capsule styling with native-like translucent fill and border
  - updated placeholder copy for broader target scope.

## 0.1.24 - 2026-03-08

### Fixed
- `Cmd+Tab+S` window now opens centered and is movable by users (Spotlight-like behavior).
- High-tab-count recommendation is now actionable when Search is open:
  - presented as a sheet over the Search window to avoid being hidden behind it
  - tab backup save panel also opens as a sheet in that context.

## 0.1.23 - 2026-03-08

### Changed
- Hardened DMG layout generation toward deterministic drag-to-install behavior:
  - mount by fixed path (`-mountpoint`) with `-nobrowse`
  - Finder customization targets disk resolved from mount path
  - fixed icon view settings and icon coordinates for `WOS.app` and `Applications`
  - explicit DMG window bounds and background image assignment
  - improved robustness with warning fallback if Finder customization fails.

## 0.1.22 - 2026-03-08

### Added
- Added custom WOS app icon asset (`AppIcon.icns`) and wired it into the app bundle metadata.
- Added DMG artwork assets for drag-install presentation.

### Changed
- Improved DMG build pipeline to produce a polished drag-to-Applications style disk image layout.

## 0.1.21 - 2026-03-08

### Changed
- Release output now defaults to `/Users/Shared/WOS-release` to avoid installers being launched from protected `Documents` paths.
- Replaced installer packaging format from `.pkg` to `.dmg`.
- Release artifacts are now:
  - `WOS.app`
  - `WOS.dmg`

### Website
- Updated download links from `.pkg` to `.dmg`.

## 0.1.20 - 2026-03-08

### Changed
- Release packaging now outputs only:
  - `WOS.app`
  - `WOS-installer.pkg`
- Removed `.zip` artifact generation from release builds.
- Standardized website download links to the unversioned installer path: `dist/WOS-installer.pkg`.

## 0.1.19 - 2026-03-08

### Changed
- Kept `Search across all open browser tabs` disabled by default to protect Cmd+Tab+S performance for new installs.
- Improved Cmd+Tab+S text-field keyboard behavior so `Cmd+A` reliably selects all text (alongside existing Cocoa editing keys like `Ctrl+A`/`Ctrl+E`).

### Added
- Added one-time-per-session recommendation when WOS detects more than 20 open browser tabs:
  - message: `Closing tabs makes your system much faster.`
  - action to export all open tabs to a `.txt` backup before cleanup.
- Added `.txt` open-tab backup export flow with browser tab titles + URLs for safe reference.

## 0.1.18 - 2026-03-08

### Changed
- Refined `Cmd+Tab+S` search panel proportions to feel closer to native Spotlight:
  - adjusted panel width/height and top-center placement
  - refined corner radius, border weight, and interior insets
  - tuned search field and stack spacing for cleaner visual hierarchy
- Improved result-list readability and selection feel:
  - larger icon plates/icons and typography polish in result rows
  - adjusted section-header spacing
  - added rounded native-style selected-row highlight.

## 0.1.17 - 2026-03-08

### Added
- Added a new setting to allow/disallow Search-mode enumeration of all open browser tabs:
  - `Search across all open browser tabs (may increase Cmd+Tab+S latency)`
  - Default is `Off` for performance.
- Added in-Search quick toggle for all-tabs mode (mouse + keyboard shortcut `⌥⌘T`).

### Changed
- Redesigned Settings panel to a cleaner macOS-style card layout with dedicated sections:
  - Shortcuts
  - Search
  - Visual HUD
- Redesigned Search panel result model with clear grouped dividers:
  - `LRS Targets`
  - `Open Apps`
  - `All Open Browser Tabs`
- Search now respects the all-tabs setting and only performs full tab enumeration when enabled.

## 0.1.16 - 2026-03-08

### Added
- Added Orion browser support (`com.kagi.kagimacOS`) for:
  - front-tab tracking
  - tab activation
  - all-open-tabs search enumeration

### Changed
- Implemented Orion script handling with WebKit-style primary commands and Chromium-style fallback commands to maximize compatibility.
- Updated release automation usage text to include Orion support.

## 0.1.15 - 2026-03-08

### Added
- Search mode now enumerates and includes all open browser tabs across supported browsers (Safari, Chrome, Brave, Arc, Opera, Firefox) instead of only recently-seen tab targets.
- Added browser tab snapshot merge in search results so non-recent but currently open tabs are searchable.

### Changed
- Search result metadata now marks enumerated tab snapshots as `open now` when no recency timestamp exists.

## 0.1.14 - 2026-03-07

### Added
- Added Arc browser support (`company.thebrowser.Browser`) for tracking and tab activation.
- Added Opera browser support (`com.operasoftware.Opera`) for tracking and tab activation.
- Added Firefox support (`org.mozilla.firefox`) with best-effort tab tracking/activation plus safe fallback behavior.

### Changed
- Extended browser script bridge to treat Arc/Opera as Chromium-family browsers for tab operations.
- Extended release app privacy usage text to include Arc, Opera, Firefox, and iTerm automation support.

## 0.1.13 - 2026-03-07

### Added
- Added configurable signing support in release build script:
  - `SIGNING_IDENTITY` for app signing
  - `INSTALLER_SIGNING_IDENTITY` for signed installer packages
- Added signing diagnostics in build output, including post-sign verification.

### Changed
- Release build now signs app using configured identity instead of hard-coded ad-hoc-only behavior.
- Added hardened runtime signing option for app builds.

## 0.1.12 - 2026-03-07

### Added
- Added `Include running apps in HUD cycle` setting (default `Off`), appending running apps after LRS recents.
- Added running-app fallback results in Search Targets so open apps can still be found quickly even when not recently seen.

### Changed
- Updated HUD/search target labeling to clearly distinguish types:
  - `App`
  - `Running App`
  - `Tab • <Browser>`
- Added a tab badge overlay on browser-tab icons to visually differentiate tabs from app entries.

### Fixed
- Prevented duplicate browser app fallback entries when tab entries for the same browser are already present.

## 0.1.11 - 2026-03-07

### Improved
- Upgraded Search Targets ranking with stronger autocomplete behavior:
  - exact title-match boost
  - title prefix and word-boundary prefix boost
  - multi-token coverage scoring
  - fuzzy subsequence fallback with gap penalty
- Kept recency as tie-breaker so equally matched targets still favor recently seen items.

## 0.1.10 - 2026-03-07

### Improved
- Reworked Quick Search input to use standard AppKit text-command behavior (`NSControlTextEditingDelegate`) instead of a global key monitor.
- Preserved native macOS text editing shortcuts in search field, including:
  - `Cmd+A` (select all)
  - `Ctrl+A` (move to beginning)
  - `Ctrl+E` (move to end)
  - and other default Cocoa text navigation/editing bindings.

### Fixed
- Improved search-field rendering by removing oversized custom typography and using standard large control sizing to avoid clipped/truncated text appearance.

## 0.1.9 - 2026-03-07

### Fixed
- Quick Search panel now reliably accepts keyboard input.
- Resolved focus/key-window issue by using a key-capable overlay panel for search.
- Adjusted search field sizing/typography to prevent truncated placeholder/editing presentation.

## 0.1.8 - 2026-03-07

### Fixed
- Made cycle continuity session-driven (active modifier hold) instead of timeout-driven.
- Removed timeout-based reset behavior from `CycleEngine`, which could cause apparent left/back jumps during repeated forward cycling.
- Added tests to verify:
  - no reset from elapsed time in same cycle session
  - reset only on explicit session reset

## 0.1.7 - 2026-03-07

### Added
- Introduced a dedicated menu bar icon for WOS.
- Added `Search Targets...` from menu with search/autocomplete UI.
- Added hotkey search mode trigger during cycle (`... + S`, e.g. `Cmd+Tab+S`).
- Added Chrome-style quick search panel with:
  - live filtering and scoring
  - keyboard navigation (`Up/Down`, `Enter`, `Esc`)
  - mouse row selection
- Added unified `Settings...` panel for shortcut and HUD preferences.
- Added configurable HUD layout preference:
  - Horizontal
  - Vertical
- Added configurable auto-switch from horizontal to vertical based on target-count threshold.
- Added in-HUD search hint so users discover `... + S` quickly.
- Updated About menu label to include version (`About WOS <version>`).

### Changed
- Increased sampling responsiveness for frontmost tab tracking.
- Updated status menu structure to center on Search, Settings, About, and recent targets.

## 0.1.6 - 2026-03-07

### Changed
- Renamed app and product from `WOWSwitcher` to `WOS` across the project.
- Renamed executable/packaging outputs to `WOS` naming.
- Updated bundle identifier to `com.agentic.wos`.
- Updated user-facing labels/docs/resources to WOS branding.

## 0.1.5 - 2026-03-07

### Added
- Improved visual HUD styling to better resemble native Cmd+Tab behavior.
- Added click-to-select directly on HUD targets.
- Added left/right arrow-key navigation while cycling.

### Changed
- Reduced cycle/HUD latency by:
  - precomputing display items per cycle session
  - caching application icons
  - increasing tab sampling frequency

## 0.1.4 - 2026-03-07

### Added
- Added `About WOS` menu item.
- Added About dialog showing dynamic version plus legal/license text:
  - `WOS v<version>`
  - copyright notice
  - MPL/open-source notice text

## 0.1.3 - 2026-03-07

### Added
- Added startup self-check panel for readiness and permissions.
- Added menu bar version display (`WOS <version>` at the time of this release).
- Made recent targets in menu clickable for direct activation.

### Fixed
- Removed duplicate browser app entries from switch lists/HUD when tab-level entries for that browser exist (e.g., Brave app + Brave tabs duplication).

## 0.1.2 - 2026-03-07

### Added
- Added iTerm tab/session tracking and activation support.

### Fixed
- Improved cycle reliability by ending cycle only via modifier state changes instead of generic key-up handling, reducing premature cycle termination.

## 0.1.1 - 2026-03-07

### Added
- Added richer visual HUD with multi-item target display (tabs and apps).
- Added true shortcut capture dialogs (press key combo to assign primary/fallback).
- Added explicit `Set Primary to Cmd+Tab` path for cases where capture cannot intercept that combo.
- Added Brave Browser support for tracking/switching.

### Changed
- Implemented window-specific browser tab activation using stored window id + tab index for Safari/Chrome/Brave.

### Fixed
- Improved Chrome/Brave tab switching correctness across multiple browser windows.
- Ensured HUD stays visible while modifier is held and commits on modifier release.

## 0.1.0 - 2026-03-07

### Added
- Initial WOS prototype implementing LRS switching model.
- Core switching engine and history:
  - target model
  - recency ordering
  - cycle engine
- Global hotkey interception for `Cmd+Tab` behavior override.
- App activation observation + browser front-tab sampling.
- Safari and Chrome tab-level tracking and activation via AppleScript.
- Menu bar app with basic controls, recent history display, and quit action.
- Accessibility trust prompt and initial permission flow.
- Console logging baseline for runtime behavior.
- Unit tests for LRS ordering and cycle behavior.
- Packaging pipeline to produce:
  - `.app`
  - `.zip`
  - `.pkg`
