Skip to content

Sync upstream quattro through #18674 - #131

Merged
mark-groves merged 20 commits into
quattrofrom
cursor/upstream-changes-pr-ac6c
Nov 13, 2026
Merged

mark-groves merged 20 commits into
quattrofrom
cursor/upstream-changes-pr-ac6c

Conversation

@cursor

@cursor cursor Bot commented Nov 12, 2026

Copy link
Copy Markdown

What changes

Upstream quattro moved past omacom#18544 (e6554e02d), which is what #130 merged. Twenty commits replayed onto current quattro, through ac6cb9c71 (the dock stays up through a theme switch, omacom#18674). No merge commits. Nothing skipped. No conflicts. Fork delta vs ac6cb9c71 is the same 256 paths as quattro vs e6554e02d. On the 202 paths upstream did not touch, the added and removed lines are unchanged. The other 54 auto-merged, and a three-way merge of each of them matches this branch. Face, Grok Bot, Cursor, linux / linux-headers, and the dropped translation-sync workflow stay.

Verification

  • Cherry-pick of the 20 non-merge commits from 36b70f1bc through ac6cb9c71 applied onto quattro 5a00ceb67. git diff --name-only of HEAD vs upstream ac6cb9c71 is the same 256 paths as quattro vs e6554e02d. On the 202 paths upstream did not touch, the added and removed line counts match. A three-way merge of the other 54 against e6554e02d matches HEAD with no conflicts. linux and linux-headers are still in the other-packages list. Face, Grok Bot, and Cursor are still in the menu. The translation-sync workflow is still absent. The agents panel still walks shownProviders. Power profiles still write through busctl. cursor-agent is still absent from the mise tool list.
  • bash test/shell.d/agents-panel-test.sh passed (100), including pairing from the panel.
  • bash test/shell.d/tailscale-test.sh passed (54), including a rename whose switch-back fails.
  • bash test/shell.d/dock-test.sh passed (42), including a pinned Browser claiming the browser it opened.
  • bash test/shell.d/bar-test.sh passed (233).
  • bash test/shell.d/chromium-ytdlp-test.sh passed (27), including video and audio as one bar that only moves forward.
  • bash test/shell.d/theme-templates-test.sh passed (9), including app icon colors per palette.
  • bash test/shell.d/theme-set-retint-order-test.sh passed (11).
  • bash test/shell.d/hyprland-floating-workspaces-test.sh passed (44).
  • bash test/shell.d/browser-link-handoff-test.sh passed (5).
  • bash test/shell.d/test-runner-test.sh passed (8), including the failed-CI log printout.
  • bash test/shell.d/i18n-test.sh passed (401).
  • ./test/cli passed (133). One find warned that ~/.config/omarchy/themes/ is missing; no assertion failed.
  • This checkout has no Omarchy session, so the dock, the launcher, the themed icons, and the agents panel were not drawn on a machine. No other sync was open.

Screenshots

No visible change: this checkout has no Omarchy session, so nothing was drawn here. Themed app icons, the new dock pins, Terminal and Browser in the launcher, and pairing a network model from the agents panel are visible on a machine; the tests above cover the behavior that can run here.

Open in Web View Automation 

emirb and others added 20 commits November 12, 2026 23:04
* Keep a GitHub artifact outage from failing tests

The Tests job uploaded out/ on every run. On 11 November, during a
GitHub Actions incident, run 43094754025 passed every test, then the
upload got GitHub's HTML error page on five attempts and a 404. The
step took 12 minutes and turned a green run red. Nothing reads the
artifact automatically, and 40 of 49 runs that day passed, so most
uploads were never read.

The upload now runs only when the job fails, may not change the
verdict, and has two minutes. In exchange, test/ci-vm prints a failed
run's evidence into the job log itself, in folded groups: the
test/cli output, each shell test file that failed or did not finish
(its last 64 KiB), and the end of run.log. It does this on the
timeout path too, where a hung file has no exit status yet.

Some tests start test/shell runs of their own, so out/shell-logs held
six log directories. test/shell now names its directory as it starts,
and ci-vm reads that one for the printout and the progress lines.

* Test which logs a failed CI run prints

test/ci-vm picks the run's own log directory from run.log and prints
only the files that failed or never finished. Nothing checked that,
so a wrong directory or a missed hung file would pass CI unnoticed.

The printer moves into test/ci-failure-logs, which test/ci-vm sources.
test-runner-test.sh sources it too, on the fixture suite's real output.
It checks four things: the CLI output prints, a failed file prints with
its exit code, an unfinished file prints, and neither a passing file
nor a nested test/shell run's logs print.

The new check fails when shell_log_dir takes the last announced
directory instead of the first.
…account (omacom#18614)

* Label Tailscale connections by tailnet when the nickname mirrors the account

Two profiles created from the same login (e.g. dhh.github and Omacom,
both authorized as dhh@github) report nickname === account, so the
connections list showed two identical entries. Treat a nickname that
merely mirrors the login name as a non-label and fall through to the
tailnet name; custom nicknames still win.

* Drop the no-tailnet assertion that guards nothing

The no-tailnet case returns the account under both the old and new
code, so the assertion stayed green without the fix and violated the
AGENTS.md rule that an added test must fail without the change.
Greptile flagged it in review.
* Rename Tailscale connections from the panel

A pen glyph appears at the right edge of a connection row on hover or
keyboard cursor; clicking it (or pressing r while the row is picked)
swaps the row for an inline input. Enter commits, Escape or clicking
away cancels — the same contract as the agent panel's account rename.

tailscale set --nickname only reaches the selected profile, so renaming
any other connection switches to it, renames it, and switches back; the
switch back runs even when the rename fails, so the daemon never ends
up stranded on another tailnet.

* Fix the connection rename pen: literal glyph and a stable row layout

QML string escapes are four hex digits, so "\uf03eb" rendered codepoint
f03e plus a stray 'b' — embed the literal md-pencil glyph instead, as the
rest of the panel's icons do.

Reveal the pen with opacity rather than visibility so its space is
always reserved in the row: showing it on hover no longer squeezes the
connection name sideways. While hidden the button is disabled, so
clicks fall through to the row.

* Fix the pen click being swallowed by the row MouseArea

The row's full-size MouseArea was declared after the row content, so it
stacked on top of the pen button and clicking the pen switched accounts
instead of opening the rename field. Declare it before the content like
PeerRow does, leaving the buttons on top.

* Harden connection rename: failure status, race guards, draft survival

Codex review findings, all verified:

- The switch-rename-switch script returned the restoration's exit code,
  so a successful switch-back masked a failed rename. Track the first
  failure in $r and exit with it; restoration failures are reported
  when nothing else failed. Behavioral tests now run the generated
  scripts against a stubbed tailscale binary and assert the status
  contract.
- switchAccount and renameAccount could race: a switch landing between
  the rename's switch and its set --nickname would rename the wrong
  profile. The service now serializes them, rows ignore clicks while a
  rename or switch is in flight, and a successful switch optimistically
  updates selectedAccountId so a follow-up rename takes the in-place
  path.
- An accounts poll reassigned the array every minute, rebuilding rows
  and destroying an open rename draft. parseAccounts now publishes only
  on real content change, the draft lives at panel level, a rebuilt
  editor restores it, and editing state is reconciled when the edited
  connection disappears.

Codex also flagged keyCatcher/accountRepeater as out of scope in the
inline component; disproven empirically with a minimal QML case —
inline components resolve document ids from nested subtrees.

* Block exit-node changes during a rename's temporary profile switch

Greptile's remaining valid finding: setExitNode only guarded against
itself, so an exit-node click inside a rename's switch-rename-restore
window would apply to the wrong profile. Rename and switch now block it
too.

* Fix the three real findings from the second Codex review

- The switch-back's failure was swallowed when nothing else failed:
  '[ $r -eq 0 ] && r=$?' captured the test's status, not the
  restoration's. Capture the restoration status into $s first, then
  fold it into $r. The stub-binary tests now cover all four
  prior-failure x switch-back-failure combinations and assert the
  restoration call actually happened (54 assertions).
- switchAccount and renameAccount could start under an in-flight
  exit-node change, moving its target profile out from under it. Both
  now wait out exitNodeProcess, making the serialization symmetric.
- Reopening the rename editor after cancelling without typing restored
  an empty draft on a rebuilt row, because setting the field to the
  unchanged label fired no textChanged. Seed editingDraft with the
  label in beginRename, and restore the draft even when the user had
  cleared the field.

Codex re-raised the keyCatcher/accountRepeater scope claim; still
disproven by the same minimal QML case as before.

* Fix the switch-back test to actually guard the fix, and use (( )) arithmetic

Greptile P2s, both valid:

- renamePlan('backfail', ...) failed on the first switch, so the old
  broken restoration-status code exited 9 there too and the test
  passed either way. Rename dd30 while restoring to the failing
  backfail target instead: verified this scenario exits 0 against the
  old code and 9 with the fix, so the test guards it.
- The generated script used [ $r -eq 0 ]; AGENTS.md and the rest of
  bin/ use (( )) for numeric tests. Converted both checks; all four
  prior-failure x restoration-failure combinations re-verified through
  the stub.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Teach the app skill what the nine Omarchy apps taught

Bringing Omacut, Monologue, Hype, Signed, Folio, Plain, Omawrite, Omacalc and Omasnap to one bar found the same template bugs in several of them independently: a Translator with no lock, the wrong plural rules for an English fallback, and a missed first language pick; a Theme that hard-coded a dark window and polled hyprctl every second; Material.roundedScale advice that cannot work; sh scripts against Omarchy's bash style; and packaging in each repository that omarchy-pkgs already owns. The templates now carry the shared files every app uses byte for byte, a Theme that follows the whole palette and asks Hyprland again only on configreloaded, install rules for trying an app before it is packaged, and tests for the catalogs and the real window. The skill gains the conventions and traps the apps paid for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Resolve the theme like Omarchy does and test catalogs token by token

The template Theme now takes the older palette names (bg, fg, colorN) and mode before theme_type, as omarchy-theme-color does, reconnects to Hyprland's event socket and asks again on every connection, and no longer queries hyprctl on unrelated file changes. The highlighted button's caption ignored Material.foreground, so it now carries its own. The catalog test reads the sources token by token, so comments, spaced calls, single quotes, and adjacent literals no longer fool it, and checks every plural form a language needs with the English placeholders. The skill gains text size, access keys, window sizing and highlighter traps, and words the getter and shortcut advice as precisely as Qt behaves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>

* Decode hex escapes and require million-only plural forms in catalog tests

A translated text written with … was read as u2026, so a correct en.json failed, and French, Portuguese, Italian, Catalan and Spanish reach their many form only at a million, which counts up to 1000 never sampled, so a catalog missing it passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let template CI read only, and unregister the translator only while there is an application

The same two changes every app took: CodeQL flags a workflow with no permissions block, and removeTranslator() warns (aborting under fatal warnings) for a Translator that outlives QCoreApplication.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>

* Fail the template tests when ctest finds none

ctest succeeds on an empty build directory, so a test target that stopped being registered would turn CI green without running anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Omarchybot <317366263+omarchybot@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codex <noreply@openai.com>
* Show yt-dlp downloads as one steady progress bar

YouTube's video arrives over HLS, whose first progress report reads 100%
before any chunk lands, and yt-dlp then downloads the audio as a second
stream that runs 0-100% again. The OSD showed each of those as-is.

Fold the streams into one bar (video fills 90%, audio the rest), drop the
bogus 100% while still downloading, and never move the bar backwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep a stalled yt-dlp bar on screen and test the new templates

A stretch that doesn't advance the bar, such as a slow audio stream that
moves it a point per 10%, now redraws every few seconds so the OSD's 8s
timeout doesn't hide it. Throttle before folding the percent so a long
HLS download doesn't fork per report, and assert the host asks yt-dlp
for the stream and progress records the stub replays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Show the focused app's version beneath its name in the app launcher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Version only the launcher entry that wins each id, nested ones included

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Move the launcher's version lookup to omarchy-pkg-versions for the CLI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Name the version lookup omarchy-launch-versions, after the launcher it serves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep versions for apps Omarchy overrides with its own launcher entries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…8524)

* Reveal hidden indicators only when hovering their own block

Hovering anywhere on the bar left of the clock used to peek the inactive
indicators. Now only the spot where they would appear does: the widget
carries an invisible hover zone the size of its hidden icons, on the side
they grow toward. The bar-wide peek and its plumbing are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show indicators outright in the tray's drawer

There an idle widget with its icons hidden takes no room, so its hover
zone is clipped away and nothing could reveal them. The drawer already
hides them until hovered, so the bar now tells widgets they sit in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
)

* Draw Omarchy's own app icons in the current theme's colors

The icons for Files, Omacut, Omasnap, Hype, Omawrite, Omacalc, Gliff,
Plain, Monologue and Signed become templates that render into the theme
and are linked over the packaged icons, so they follow every switch.

They draw from roles derived from the palette rather than raw keys, since
magenta is Tokyo Night's violet but Gruvbox's and Nord's pink, and light
themes turn the neutral ramp around. A theme can set any role itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep themed app icons safe on odd palettes, data dirs and removed themes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let a theme's chosen icon highlight stand in for its red and orange

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw the themed app icons after the switch instead of holding it up

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Replace dangling icon links and keep CLI tests out of the real data dir

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Publish each app icon once when a user template shadows a built-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw cliamp's and disktree's icons in the current theme's colors too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw Monologue's icon as its original button and ring, in theme colors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw Gliff like its own icon and cliamp as a terminal equalizer

Gliff keeps its pale screen with a dark pointer on it, through a new
icon_contrast role that is dark on the paper in light themes too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw cliamp's icon as just its terminal equalizer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Lay disktree's treemap on an even grid

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give Catppuccin's icons its mauve and cliamp's equalizer two clean tiers

Catppuccin's palette has no key for its mauve, so its magenta (the pink)
fell through to the yellow; both flavors now name the mauve themselves.
Mixing the main color into the highlight muddied cliamp's middle bars
wherever the two hues sit far apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give any theme's app icons a best guess

Colors written as rgb(), rgba(), 0xAARRGGBB or #rrggbbaa are read, neutrals
a palette leaves out are mixed from its background and foreground, and a
palette with no hues of its own gets its accent turned for the highlight.
Stock themes draw exactly as before. Also apply a theme's own neutrals
before the roles derived from them, and queue an icon rescan that arrives
while one is running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tell a bare palette from a monochrome one by the keys the theme wrote

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Don't count neutral ANSI slots as a palette's own hues

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…macom#18641)

* Pair with a network model from the agents panel without a terminal

Pairing ran in a floating terminal so SSH could ask about a new host key.
omarchy-agent-local pair now trusts a host met for the first time and asks
nothing when it has no terminal, so the panel runs it in the background and
stays open. A failure shows its reason's first line in red on the model's
line, which opens everything pairing printed in a terminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let the agents panel step aside for the SSH agent's prompt while pairing

The open panel is a full-screen overlay that holds the keyboard and closes
on a click outside its card, so 1Password's prompt to approve a key came up
unfocused, and clicking it closed the panel. A keyboard panel can now yield:
it stays on show, but gives up the keyboard, takes clicks on its card alone,
and doesn't close on one elsewhere. The agents panel yields while it pairs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Land on a newly paired model's terminal, so Return starts an agent

Once a model paired from the agents panel shows up paired and running, the
keyboard cursor moves to its terminal button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep waiting to land on a paired model's terminal until it runs

A model paired from the agents panel can show up paired while it is still
starting on its host, with no terminal yet. The cursor gave up then; it now
waits for the terminal while the panel stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add Terminal and Browser to the app launcher, drawn in the theme's colors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Draw the launcher's Browser as just a globe, and let the link entry show in the launcher in its test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let a pinned Terminal or Browser claim the windows of the terminal or browser it opened

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add only Terminal and Browser in the migration, keeping apps the user removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Draw dock icons solid and file default terminal and browser windows under their pinned stand-ins

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read a terminal named with an action, and rerun a stand-in read requested mid-read

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… workspaces (omacom#18663)

* Give Chromium-based browsers their own frame on floating workspaces

They skip the titlebar and keep their tab strip as the header, with their
own maximize and close buttons. Hyprland only hears their maximize button
when maximize requests aren't suppressed for them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give Disks and Document Viewer their own frame on floating workspaces too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Match web app windows too, and keep a button layout the user chose

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep maximize requests suppressed and the close-only button layout

Hyprland reports every app as maximized and takes any maximize request as
a toggle, so Chrome asking to return to maximized after leaving fullscreen
maximized a normal window. The apps keep their own header without a
titlebar, and maximize through Super + Alt + F.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep titlebars on web apps, which have no tab strip to drag by

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Pin Omarchy's own apps in the default dock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Leave Omacut out of the default dock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Keep the dock on screen through a Hyprland config reload

A theme switch reloads the config, and the dock dropped every cached
workspace layout on reload, hiding itself until the layouts were read again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Change the dock's icons with the theme palette

Draw Omarchy's app icons alongside the terminal restarts rather than in the
slow retint batch, and fade the dock's icons and apps button over the same
250ms as the bar instead of blanking while they reload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Order icon redraws and keep the shown icon through a second swap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@mark-groves
mark-groves merged commit ab8aa87 into quattro Nov 13, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants