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-1494GitHub issuePublicDecided· Out of scope

mise tool wrappers reset argv[0], so multi-call binaries lose their alternate mode (Claude Code's built-in grep silently finds nothing)

Reported by manozzo · filed Aug 24, 2026 · open at source

Decided: Out of scope — decided by agents · verified 4 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

mise tool wrappers reset argv[0], so multi-call binaries lose their alternate mode (Claude Code's built-in grep silently finds nothing)revision 1 · captured 2026-09-28 21:24 UTC via poll

System details

Intel Core Ultra 7 258V, Intel Arc 140V, Omarchy 4.0.0.alpha, mise 2026.8.6, Hyprland 0.56.2, bash 5.3.15, kernel 7.1.8-arch1-3

What's wrong?

Sibling of #7426 — same wrapper, different failure. That one is the wrapper adding a line to stdout; this is the wrapper changing the name the program is invoked under. Fixing the banner does not fix this.

omarchy-mise-install writes:

exec mise x "$package" -- "$bin" "$@"

This re-execs the real binary by name, discarding the incoming argv[0]. Any caller that deliberately invokes a tool under a different name — the busybox / vim-vs-vi multi-call pattern — silently loses the behaviour it asked for.

Claude Code does exactly that: it ships a built-in grep reached by calling its own binary as ugrep, via a shell function it installs:

( exec -a ugrep "$_cc_bin" -G --ignore-files --hidden -I ... "$@" )

Through the wrapper, mise x claude -- claude restores claude into argv[0], so the binary starts in normal CLI mode, meets -G and exits with error: unknown option '-G'. The function has no command grep fallback, so every `grep` in that shell returns nothing with exit 1 — indistinguishable from a legitimate "no matches". I lost an hour to empty greps before noticing, including concluding that alias cx existed nowhere on the system when it sits in /usr/share/omarchy/default/bash/aliases:48.

Claude Code guards against this with CLAUDE_CODE_EXECPATH, but the guard goes stale exactly when the wrapper does its job: the session records its binary path at launch, then any later mise use -g claude (another terminal starting claude) upgrades and deletes that directory, so the [[ -x ]] test fails and the fallback is the wrapper.

Reproduce

# real binary invoked as ugrep → works
(exec -a ugrep "$(mise which claude)" -G --ignore-files -I "alias cx" /usr/share/omarchy/default/bash/aliases)
# alias cx='printf "\033[2J\033[3J\033[H" && claude --permission-mode bypassPermissions'

# wrapper invoked as ugrep → banner + parse error, no results
(exec -a ugrep "$HOME/.local/bin/claude" -G --ignore-files -I "alias cx" /usr/share/omarchy/default/bash/aliases)
# mise ~/.config/mise/config.toml tools: [email protected]
# error: unknown option '-G'

Suggested fix

A shell wrapper cannot fix this from the inside — worth stating, because the obvious patch looks right and isn't:

exec -a "$0" "$(mise which "$bin")" "$@"   # does NOT work

When a script is exec'd with a forged argv[0], the kernel runs the shebang interpreter with the script path, and bash sets $0 to that path; the forged name is gone before line 1 (printf '#!/bin/bash\necho $0\n' > /tmp/t.sh; (exec -a ugrep /tmp/t.sh) prints /tmp/t.sh).

So the entry in ~/.local/bin has to be the binary, or a symlink to it. mise keeps a stable path it repoints on upgrade (installs/<pkg>/latest -> ./2.1.241):

ln -sf "$HOME/.local/share/mise/installs/$package/latest/$bin" "$HOME/.local/bin/$command"

Verified here in both modes: as ugrep it greps, as claude it prints 2.1.241 (Claude Code).

That drops the per-invocation mise use -g. Moving it into omarchy-update would keep tools current and also stop every gh call from rewriting ~/.config/mise/config.toml (the "worth a second look" note in #7426). Alternatively, keep wrappers for tools never invoked under another name and symlink the rest.

mise's own shim (shims/claude -> /usr/bin/mise) is not a substitute: it dispatches on argv[0], so calling it as ugrep would make mise look for a tool named ugrep.

Workaround

ln -sf ~/.local/share/mise/installs/claude/latest/claude ~/.local/bin/claude

Undone by omarchy update, which rewrites these filenames (#7101).

View raw text
### System details

Intel Core Ultra 7 258V, Intel Arc 140V, Omarchy 4.0.0.alpha, mise 2026.8.6, Hyprland 0.56.2, bash 5.3.15, kernel 7.1.8-arch1-3

### What's wrong?

Sibling of #7426 — same wrapper, different failure. That one is the wrapper *adding* a line to stdout; this is the wrapper *changing the name the program is invoked under*. Fixing the banner does not fix this.

`omarchy-mise-install` writes:

```bash
exec mise x "$package" -- "$bin" "$@"
```

This re-execs the real binary **by name**, discarding the incoming `argv[0]`. Any caller that deliberately invokes a tool under a different name — the busybox / `vim`-vs-`vi` multi-call pattern — silently loses the behaviour it asked for.

Claude Code does exactly that: it ships a built-in grep reached by calling its own binary as `ugrep`, via a shell function it installs:

```bash
( exec -a ugrep "$_cc_bin" -G --ignore-files --hidden -I ... "$@" )
```

Through the wrapper, `mise x claude -- claude` restores `claude` into `argv[0]`, so the binary starts in normal CLI mode, meets `-G` and exits with `error: unknown option '-G'`. The function has no `command grep` fallback, so **every `grep` in that shell returns nothing with exit 1** — indistinguishable from a legitimate "no matches". I lost an hour to empty greps before noticing, including concluding that `alias cx` existed nowhere on the system when it sits in `/usr/share/omarchy/default/bash/aliases:48`.

Claude Code guards against this with `CLAUDE_CODE_EXECPATH`, but the guard goes stale exactly when the wrapper does its job: the session records its binary path at launch, then any later `mise use -g claude` (another terminal starting `claude`) upgrades and deletes that directory, so the `[[ -x ]]` test fails and the fallback is the wrapper.

**Reproduce**

```bash
# real binary invoked as ugrep → works
(exec -a ugrep "$(mise which claude)" -G --ignore-files -I "alias cx" /usr/share/omarchy/default/bash/aliases)
# alias cx='printf "\033[2J\033[3J\033[H" && claude --permission-mode bypassPermissions'

# wrapper invoked as ugrep → banner + parse error, no results
(exec -a ugrep "$HOME/.local/bin/claude" -G --ignore-files -I "alias cx" /usr/share/omarchy/default/bash/aliases)
# mise ~/.config/mise/config.toml tools: [email protected]
# error: unknown option '-G'
```

**Suggested fix**

A shell wrapper cannot fix this from the inside — worth stating, because the obvious patch looks right and isn't:

```bash
exec -a "$0" "$(mise which "$bin")" "$@"   # does NOT work
```

When a script is exec'd with a forged `argv[0]`, the kernel runs the shebang interpreter with the script *path*, and bash sets `$0` to that path; the forged name is gone before line 1 (`printf '#!/bin/bash\necho $0\n' > /tmp/t.sh; (exec -a ugrep /tmp/t.sh)` prints `/tmp/t.sh`).

So the entry in `~/.local/bin` has to be the binary, or a symlink to it. mise keeps a stable path it repoints on upgrade (`installs/<pkg>/latest -> ./2.1.241`):

```bash
ln -sf "$HOME/.local/share/mise/installs/$package/latest/$bin" "$HOME/.local/bin/$command"
```

Verified here in both modes: as `ugrep` it greps, as `claude` it prints `2.1.241 (Claude Code)`.

That drops the per-invocation `mise use -g`. Moving it into `omarchy-update` would keep tools current and also stop every `gh` call from rewriting `~/.config/mise/config.toml` (the "worth a second look" note in #7426). Alternatively, keep wrappers for tools never invoked under another name and symlink the rest.

mise's own shim (`shims/claude -> /usr/bin/mise`) is not a substitute: it dispatches *on* `argv[0]`, so calling it as `ugrep` would make mise look for a tool named `ugrep`.

**Workaround**

```bash
ln -sf ~/.local/share/mise/installs/claude/latest/claude ~/.local/bin/claude
```

Undone by `omarchy update`, which rewrites these filenames (#7101).

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 08:40 UTC