Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-2106GitHub issuePublicDecided· Out of scope

Greek (polytonic) layout: stacked dead keys only work in one fixed order (e.g. psili + perispomeni + ypogegrammeni gives "ʼ~ᾳ" instead of "ᾆ")

Reported by Nonrefertquissim · filed Sep 29, 2026 · open at source

Decided: Out of scope — decided by agents · verified 3 days ago.

At a glance

1 · What is claimed?
Only the report text below — no validator has restated it as checkable claims yet.
2 · What does it affect?
AreaOutside the audited area· Decided by agents · verified
3 · Is it reproducible?
No validation has been run yet.
4 · What remains unclear?
  • Not validated yet: no claims, no reproducer.
5 · What happens next?
Decided by agents and verified by a second, independent agent — see the decision.

Original report

Greek (polytonic) layout: stacked dead keys only work in one fixed order (e.g. psili + perispomeni + ypogegrammeni gives "ʼ~ᾳ" instead of "ᾆ")revision 1 · captured 2026-09-29 20:02 UTC via poll

System details

Omarchy 4.0.4

What's wrong?

Context: Greek Polytonic is the default keyboard for anyone who wants to write either Ancient/Medieval Greek, or Modern Greek in the polytonic system, which was officially used until 1982, but is still widely used today, so having a broken layout without an easy workaround is a big problem. I've been typing in polytonic for ages on Linux, and this is a first for me.
I wasted an unhealthy amount of time trying to make it work until I spawned an Opus 5.5 agent, which gave me a solution and then described the problem from its perspective.
Note: If this is, as I suspect, an upstream issue, pray report it to the appropriate repo.

Summary

With the Greek polytonic layout (kb_layout = "us,gr", kb_variant = ",polytonic"), a letter with one diacritic types fine (οὐ, ἐ, ῶ), but a letter with two or more stacked diacritics (extremely usual) only works if the dead keys are pressed in one specific unexpected order. Pressed in any other order (included the expected/normal), the sequence falls apart and the diacritics come out as separate spacing characters.

For example, I type psili (Shift+;), perispomeni ([), ypogegrammeni (]), then α:

  • Expected: ᾆ
  • Actual: ʼ~ᾳ

Similarly, psili, perispomeni, υ gives "ʼῦ" instead of ὖ. A normal sentence like
"Πάντες οὖν, ὡς ἔπος εἰπεῖν, ᾆσμα πειρῶνται ᾇσαι…"
came out as "Πάντες, οʼῦν, ὡς ʼέπος εἰπεῖν, ʼ~ᾳσμα…".

On ALL other distros and desktop environments, the same layout accepts stacked dead keys in any order, and typing polytonic Greek just works. This is just broken.

Steps to reproduce

  1. Add the polytonic layout in ~/.config/hypr/input.lua:
   hl.config({ input = { kb_layout = "us,gr", kb_variant = ",polytonic", kb_options = "grp:alt_shift_toggle" } })
  1. Switch to Greek and, in any program (libreoffice, foot...), press: Shift+; (dead_psili), [ (dead_tilde / perispomeni), ] (dead_iota), a (α).
  2. Result: ʼ~ᾳ. Expected: ᾆ.

(Pressing ] [ Shift+; a, the one order listed in the Compose table, does give ᾆ. Nobody types in that order; it's unnatural)

Cause

Omarchy runs fcitx5 all the time (omarchy-fcitx5.service, XMODIFIERS=@im=fcitx, QT_IM_MODULE=fcitx, SDL_IM_MODULE=fcitx), so fcitx5 turns every dead key into a letter. It does this with the xkbcommon Compose table: ~/.XCompose → Omarchy's default/xcompose → include "%L" → /usr/share/X11/locale/en_US.UTF-8/Compose.

That table lists each stacked-diacritic character under exactly one key order, e.g.:

<dead_iota> <dead_tilde> <dead_psili> <Greek_alpha> : "ᾆ" U1F86

fcitx5 only matches exact sequences. Any other order is a miss: the dead keys already pressed are dropped as spacing characters, and the remaining keys compose on their own (here ] + α → ᾳ).

As far as I know, IBus and GTK's built-in input method also handle stacked dead keys algorithmically (combine the marks and normalize, in any order), which is why the same layout works on GNOME, Plasma and similar setups.

To be fair, this probably isn't only Omarchy's problem. I believe I hit the same thing on a plain Arch + Hyprland setup on bare metal and not on a VM in the past (I can't confirm that 100%). That would fit: without an input method, most apps on Wayland use the same strict xkbcommon Compose table directly. But Omarchy sends every app through fcitx5 by default, so no app gets the lenient handling, and a stock layout choice ends up broken out of the box.

Workaround (confirmed working after Opus's 5.5 magic)

Generate the missing key orders and include them from ~/.XCompose:

  1. Save the script below as ~/.config/xcompose/greek-polytonic-gen.py and run:
   python3 ~/.config/xcompose/greek-polytonic-gen.py > ~/.config/xcompose/greek-polytonic.compose
  1. Add to ~/.XCompose:
   include "%H/.config/xcompose/greek-polytonic.compose"
  1. omarchy-restart-xcompose, then reopen apps.

This adds ~3,100 sequences (every order of breathing / accent / diaeresis / iota subscript / macron / breve on α ε η ι ο υ ω ρ, upper and lower case), with the output NFC-normalized. It skips any sequence already in the locale table, and none of the generated ones conflict with an existing entry.

Suggested fix

  • Short term: ship these any-order sequences (or a generated file like this) in Omarchy's default/xcompose, which already includes %L. Then choosing the polytonic variant just works.
  • Longer term: fcitx5 could combine dead keys in any order, as IBus does (I'm filing a feature request there too).

System

  • Omarchy 4.0.4
  • Hyprland 0.56.2
  • fcitx5 5.1.22
  • libxkbcommon 1.13.2
  • xkeyboard-config 2.48
  • libx11 1.8.13 (source of the Compose table)
  • Locale: en_US.UTF-8
  • Tested in a VM (QEMU)
greek-polytonic-gen.py
import itertools, re, unicodedata, sys
C="/usr/share/X11/locale/en_US.UTF-8/Compose"
existing={}
for l in open(C, encoding="utf-8"):
    m=re.match(r'^((?:<\w+>\s*)+):\s*"([^"]*)"', l)
    if m: existing[tuple(re.findall(r'<(\w+)>', m.group(1)))]=m.group(2)

# dead key -> (group, combining char). Groups are stacked in this visual order.
DEAD={"dead_macron":("len","̄"),"dead_breve":("len","̆"),
      "dead_diaeresis":("dia","̈"),
      "dead_psili":("brth","̓"),"dead_dasia":("brth","̔"),
      "dead_acute":("acc","́"),"dead_grave":("acc","̀"),"dead_tilde":("acc","͂"),
      "dead_iota":("iota","ͅ")}
ORDER=["len","dia","brth","acc","iota"]
V={"alpha":"α","epsilon":"ε","eta":"η","iota":"ι","omicron":"ο","upsilon":"υ","omega":"ω","rho":"ρ"}
ALLOW={"len":"αιυ","dia":"ιυ","brth":"αεηιουωρ","acc":"αεηιουω","iota":"αηω"}
TILDE_OK="αηιυω"

out=[]; conflicts=0
groups={}
for k,(g,_) in DEAD.items(): groups.setdefault(g,[]).append(k)
for n in range(2,6):
    for gs in itertools.combinations(ORDER,n):
        for keys in itertools.product(*(groups[g] for g in gs)):
            for name,ch in V.items():
                if any(ch not in ALLOW[DEAD[k][0]] for k in keys): continue
                if "dead_tilde" in keys and ch not in TILDE_OK: continue
                if ch=="ρ" and len(keys)>1: continue
                for upper in (False,True):
                    base=ch.upper() if upper else ch
                    ks="Greek_"+(name.upper() if upper else name)
                    s=unicodedata.normalize("NFC", base+"".join(DEAD[k][1] for k in keys))
                    for perm in itertools.permutations(keys):
                        seq=perm+(ks,)
                        if seq in existing:
                            if unicodedata.normalize("NFD",existing[seq])!=unicodedata.normalize("NFD",s): conflicts+=1
                            continue
                        out.append("".join(f"<{k}> " for k in seq)+f': "{s}"')
print("# Polytonic Greek: accept stacked dead keys in any order (like IBus/GTK do).")
print("# Generated; the locale Compose file only accepts one fixed order.")
print("\n".join(out))
print(f"lines={len(out)} conflicts={conflicts}", file=sys.stderr)

Note: The fix above immediately gave me a functional polytonic keyboard, but I haven’t examined it in depth. I didn’t encounter any issues, although the code may still miss some edge cases.

View raw text
### System details

Omarchy 4.0.4

### What's wrong?

Context: Greek Polytonic is the default keyboard for anyone who wants to write either Ancient/Medieval Greek, or Modern Greek in the polytonic system, which was officially used until 1982, but is still widely used today, so having a _broken_ layout without an easy workaround is a big problem. I've been typing in polytonic for ages on Linux, and this is a first for me.
I wasted an unhealthy amount of time trying to make it work until I spawned an Opus 5.5 agent, which gave me a solution and then described the problem from its perspective.
Note: _If_ this is, as I suspect, an upstream issue, pray report it to the appropriate repo.
## Summary

With the Greek polytonic layout (`kb_layout = "us,gr"`, `kb_variant = ",polytonic"`), a letter with one diacritic types fine (οὐ, ἐ, ῶ), but a letter with two or more stacked diacritics (extremely usual) only works if the dead keys are pressed in one specific unexpected order. Pressed in any other order (included the expected/normal), the sequence falls apart and the diacritics come out as separate spacing characters.

For example, I type psili (Shift+;), perispomeni ([), ypogegrammeni (]), then α:

- Expected: ᾆ
- Actual: ʼ~ᾳ

Similarly, psili, perispomeni, υ gives "ʼῦ" instead of ὖ. A normal sentence like
"Πάντες οὖν, ὡς ἔπος εἰπεῖν, ᾆσμα πειρῶνται ᾇσαι…"
came out as "Πάντες, οʼῦν, ὡς ʼέπος εἰπεῖν, ʼ~ᾳσμα…".

On ALL other distros and desktop environments, the same layout accepts stacked dead keys in any order, and typing polytonic Greek just works. This is just broken.

## Steps to reproduce

1. Add the polytonic layout in `~/.config/hypr/input.lua`:
   ```lua
   hl.config({ input = { kb_layout = "us,gr", kb_variant = ",polytonic", kb_options = "grp:alt_shift_toggle" } })
   ```
2. Switch to Greek and, in any program (libreoffice, foot...), press: Shift+; (dead_psili), [ (dead_tilde / perispomeni), ] (dead_iota), a (α).
3. Result: `ʼ~ᾳ`. Expected: `ᾆ`.

(Pressing ] [ Shift+; a, the one order listed in the Compose table, does give ᾆ. Nobody types in that order; it's unnatural)

## Cause

Omarchy runs fcitx5 all the time (`omarchy-fcitx5.service`, `XMODIFIERS=@im=fcitx`, `QT_IM_MODULE=fcitx`, `SDL_IM_MODULE=fcitx`), so fcitx5 turns every dead key into a letter. It does this with the xkbcommon Compose table: `~/.XCompose` → Omarchy's `default/xcompose` → `include "%L"` → `/usr/share/X11/locale/en_US.UTF-8/Compose`.

That table lists each stacked-diacritic character under exactly one key order, e.g.:

```
<dead_iota> <dead_tilde> <dead_psili> <Greek_alpha> : "ᾆ" U1F86
```

fcitx5 only matches exact sequences. Any other order is a miss: the dead keys already pressed are dropped as spacing characters, and the remaining keys compose on their own (here ] + α → ᾳ).

As far as I know, IBus and GTK's built-in input method also handle stacked dead keys algorithmically (combine the marks and normalize, in any order), which is why the same layout works on GNOME, Plasma and similar setups.

To be fair, this probably isn't only Omarchy's problem. I believe I hit the same thing on a plain Arch + Hyprland setup on bare metal and not on a VM in the past (I can't confirm that 100%). That would fit: without an input method, most apps on Wayland use the same strict xkbcommon Compose table directly. But Omarchy sends every app through fcitx5 by default, so no app gets the lenient handling, and a stock layout choice ends up broken out of the box.

## Workaround (confirmed working after Opus's 5.5 magic)

Generate the missing key orders and include them from `~/.XCompose`:

1. Save the script below as `~/.config/xcompose/greek-polytonic-gen.py` and run:
   ```bash
   python3 ~/.config/xcompose/greek-polytonic-gen.py > ~/.config/xcompose/greek-polytonic.compose
   ```
2. Add to `~/.XCompose`:
   ```
   include "%H/.config/xcompose/greek-polytonic.compose"
   ```
3. `omarchy-restart-xcompose`, then reopen apps.

This adds ~3,100 sequences (every order of breathing / accent / diaeresis / iota subscript / macron / breve on α ε η ι ο υ ω ρ, upper and lower case), with the output NFC-normalized. It skips any sequence already in the locale table, and none of the generated ones conflict with an existing entry.

## Suggested fix

- Short term: ship these any-order sequences (or a generated file like this) in Omarchy's `default/xcompose`, which already includes `%L`. Then choosing the polytonic variant just works.
- Longer term: fcitx5 could combine dead keys in any order, as IBus does (I'm filing a feature request there too).

## System

- Omarchy 4.0.4
- Hyprland 0.56.2
- fcitx5 5.1.22
- libxkbcommon 1.13.2
- xkeyboard-config 2.48
- libx11 1.8.13 (source of the Compose table)
- Locale: en_US.UTF-8
- Tested in a VM (QEMU)

<details>
<summary>greek-polytonic-gen.py</summary>

```python
import itertools, re, unicodedata, sys
C="/usr/share/X11/locale/en_US.UTF-8/Compose"
existing={}
for l in open(C, encoding="utf-8"):
    m=re.match(r'^((?:<\w+>\s*)+):\s*"([^"]*)"', l)
    if m: existing[tuple(re.findall(r'<(\w+)>', m.group(1)))]=m.group(2)

# dead key -> (group, combining char). Groups are stacked in this visual order.
DEAD={"dead_macron":("len","̄"),"dead_breve":("len","̆"),
      "dead_diaeresis":("dia","̈"),
      "dead_psili":("brth","̓"),"dead_dasia":("brth","̔"),
      "dead_acute":("acc","́"),"dead_grave":("acc","̀"),"dead_tilde":("acc","͂"),
      "dead_iota":("iota","ͅ")}
ORDER=["len","dia","brth","acc","iota"]
V={"alpha":"α","epsilon":"ε","eta":"η","iota":"ι","omicron":"ο","upsilon":"υ","omega":"ω","rho":"ρ"}
ALLOW={"len":"αιυ","dia":"ιυ","brth":"αεηιουωρ","acc":"αεηιουω","iota":"αηω"}
TILDE_OK="αηιυω"

out=[]; conflicts=0
groups={}
for k,(g,_) in DEAD.items(): groups.setdefault(g,[]).append(k)
for n in range(2,6):
    for gs in itertools.combinations(ORDER,n):
        for keys in itertools.product(*(groups[g] for g in gs)):
            for name,ch in V.items():
                if any(ch not in ALLOW[DEAD[k][0]] for k in keys): continue
                if "dead_tilde" in keys and ch not in TILDE_OK: continue
                if ch=="ρ" and len(keys)>1: continue
                for upper in (False,True):
                    base=ch.upper() if upper else ch
                    ks="Greek_"+(name.upper() if upper else name)
                    s=unicodedata.normalize("NFC", base+"".join(DEAD[k][1] for k in keys))
                    for perm in itertools.permutations(keys):
                        seq=perm+(ks,)
                        if seq in existing:
                            if unicodedata.normalize("NFD",existing[seq])!=unicodedata.normalize("NFD",s): conflicts+=1
                            continue
                        out.append("".join(f"<{k}> " for k in seq)+f': "{s}"')
print("# Polytonic Greek: accept stacked dead keys in any order (like IBus/GTK do).")
print("# Generated; the locale Compose file only accepts one fixed order.")
print("\n".join(out))
print(f"lines={len(out)} conflicts={conflicts}", file=sys.stderr)
```

</details>

Note: The fix above immediately gave me a functional polytonic keyboard, but I haven’t examined it in depth. I didn’t encounter any issues, although the code may still miss some edge cases.

Validation

Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.

Linked

Not linked to any known issue, other report or change yet.

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Out of scopeDecided by agents · verified · 2026-10-09 20:13 UTC