fix(mime): text/plain falls through to Text Editor on live (#119)
All checks were successful
Check / eval (push) Successful in 4m5s

A singleton default naming code.desktop left the live ISO with no text
handler — GIO skips missing .desktop files and offers nothing. Prefer
vscode when present, then org.gnome.TextEditor.desktop. Live also gains
mpv (video defaults had the same hole; free via the template pin).

checks.live-baseline-apps now requires every Default Applications key
to resolve to a present .desktop on live (HM + system path) and on the
default template install. Proved to fail on a vscode-only text/plain.

Verified: V2 (live-baseline-apps green + prove-to-fail); V0 flake check.
This commit is contained in:
2026-07-15 09:24:48 +01:00
parent 6fbad46bca
commit 355d8cb1a4
6 changed files with 125 additions and 48 deletions

View File

@@ -55,31 +55,6 @@ template source. Pass = Chromium launches after installation and HTTP/HTTPS
mime defaults resolve to `chromium-browser.desktop` (and the intended live-ISO mime defaults resolve to `chromium-browser.desktop` (and the intended live-ISO
browser posture is explicit). browser posture is explicit).
### 119. `text/plain` names vscode, which the live ISO does not ship
Found while shipping #103 (2026-07-14), deliberately not fixed there — same
bug class as #94, one mime key over. `modules/home/mime.nix:48` assigns
`text/plain = code.desktop`, and vscode ships only in the **template**, never
on the live ISO. GIO silently skips an entry whose desktop file is absent, so
opening a text file in the live session finds no handler at all — the exact
shape of the no-default-browser bug, and now the only one left of its kind
since #103 put chromium on the live medium.
Two ways it bites, and a fix should settle both: (1) the live session, which
now ships `gnome-text-editor` (`org.gnome.TextEditor.desktop`) — the obvious
live handler; (2) any installed machine where the user takes the template at
its word and deletes the `vscode` line (opt-out = delete the line, per the
option-surface convention), which silently breaks `text/plain` with no warning.
The general trap is worth fixing once rather than per-key: a mime default
naming a package the profile does not carry is invisible until a user
double-clicks a file. `checks.live-baseline-apps` now guards exactly this for
HTTPS (asserts the named .desktop is present, not merely named) — the cheap
version of this item is extending that assertion to every key in
`defaultApplications`, which would have caught both #94 and this. Pass = no
mime default names a desktop entry the profile lacks, on the live ISO and on
a template install, and a guard fails on a regression.
### 95. Live ISO/install: Ghostty does not open ### 95. Live ISO/install: Ghostty does not open
Reproduce from the launcher and a terminal, capture its stderr/journal, and Reproduce from the launcher and a terminal, capture its stderr/journal, and

View File

@@ -421,12 +421,11 @@ the **T14s** (webcam case).
**LibreOffice** (Writer), **Text Editor**, **Amberol**, **Snapshot**. **LibreOffice** (Writer), **Text Editor**, **Amberol**, **Snapshot**.
**Pass** = all five are *listed in the launcher* and each opens a window. **Pass** = all five are *listed in the launcher* and each opens a window.
Also: a `.html` file opens in Chromium (the HTTPS mime default now names Also: a `.html` file opens in Chromium (the HTTPS mime default now names
a browser that is present — #94's exact bug). Firefox is deliberately a browser that is present — #94's exact bug), and a `.txt` file opens in
absent; its absence is correct, not a miss. **Text Editor** (`text/plain` falls through to
**Known-not-covered:** a `.txt` file has no handler on the live ISO — `org.gnome.TextEditor.desktop` after #119; vscode remains preferred on
`text/plain` still names vscode, which the live medium never ships template installs). Firefox is deliberately absent; its absence is
(BACKLOG #119). Text Editor opening from the launcher is the pass here; correct, not a miss.
double-clicking a text file is #119's problem, so don't file it twice.
## AMD dev box only ## AMD dev box only
- [ ] **#118 smartd still runs where drives DO have SMART** (this commit) — the - [ ] **#118 smartd still runs where drives DO have SMART** (this commit) — the

View File

@@ -19,6 +19,26 @@ Template:
--- ---
## 2026-07-15 — #119: mime defaults must name a present .desktop (this commit)
- **Task:** BACKLOG #119`text/plain` named vscode, which the live ISO never
ships (same GIO-skip trap as #94, one key over).
- **Did:** `text/plain` is now an ordered fallback
`code.desktop``org.gnome.TextEditor.desktop` (HM tries the next when the
preferred is missing). Live profile gains `mpv` too — video mime defaults
had the same hole and the package is free via the template pin. Extended
`checks.live-baseline-apps` so every `[Default Applications]` key has at
least one listed `.desktop` on **live** (HM path + system path for thunar)
and on the **default template install**.
- **Verified:** **V2** — check green; mimeapps.list renders
`text/plain=code.desktop;org.gnome.TextEditor.desktop`; **proved to fail**
when `text/plain` is a vscode-only singleton. V0 flake check green.
- **Pending:** V3 launch of a `.txt` on the Acer is covered by the existing
#103 HARDWARE-QUEUE entry (now expects Text Editor for text files). Opting
out of *both* vscode and Text Editor still leaves no handler — that is the
delete-the-line convention, not a silent live-ISO bug.
- **Next suggestion:** #95 (Ghostty does not open) or #94 residual
(test-install chromium launch).
## 2026-07-14 — #118: smartd self-gates; the doctor was right all along (this commit) ## 2026-07-14 — #118: smartd self-gates; the doctor was right all along (this commit)
- **Task:** BACKLOG #118 — Bernardo's live ISO showed a red Waybar health icon - **Task:** BACKLOG #118 — Bernardo's live ISO showed a red Waybar health icon
reporting smartd. reporting smartd.

View File

@@ -380,8 +380,8 @@ Design/decision records and a running log of shipped work (items marked
the thing a user judges the distro by *before* installing, and what they the thing a user judges the distro by *before* installing, and what they
boot to rescue a machine that will not start; it could do neither. The live boot to rescue a machine that will not start; it could do neither. The live
HM user in `flake.nix` now names its own set: **Chromium, libreoffice-fresh, HM user in `flake.nix` now names its own set: **Chromium, libreoffice-fresh,
gnome-text-editor, amberol, snapshot** (Bernardo confirmed the gap on real gnome-text-editor, amberol, mpv (#119), snapshot** (Bernardo confirmed the
hardware 2026-07-14). gap on real hardware 2026-07-14).
**Firefox deliberately excluded** (Bernardo's call): Chromium already owns **Firefox deliberately excluded** (Bernardo's call): Chromium already owns
the HTTP mime default and ships in the installed template, so it is the the HTTP mime default and ships in the installed template, so it is the
browser that matches what a user gets after installing — and it was the one browser that matches what a user gets after installing — and it was the one
@@ -404,6 +404,17 @@ Design/decision records and a running log of shipped work (items marked
decision rather than a drive-by. Proved to fail: dropping snapshot makes it decision rather than a drive-by. Proved to fail: dropping snapshot makes it
name the missing entry. **V3 pending** — that the apps *launch* needs real name the missing entry. **V3 pending** — that the apps *launch* needs real
hardware (HARDWARE-QUEUE, Acer M5-481T). hardware (HARDWARE-QUEUE, Acer M5-481T).
- ✓ **Mime defaults must name a present .desktop (#119):** same bug class as
#94, one key over. `text/plain` pointed at `code.desktop` (vscode), which
the live ISO never ships — GIO skips the missing entry and leaves no
handler, so a double-clicked `.txt` did nothing. Fix: ordered fallback
`code.desktop` → `org.gnome.TextEditor.desktop` (HM/GIO try the next when
preferred is missing); live gains `mpv` too (video defaults had the same
hole, free via the template pin). Guard extended: every
`[Default Applications]` key must have *at least one* listed `.desktop` in
the live HM path **or** system path (thunar is system-side) *and* on the
default template install — proved to fail when `text/plain` is a vscode
singleton. Template still prefers vscode when present.
- ✓ **smartd self-gates on the hardware (#118):** Bernardo booted the live ISO - ✓ **smartd self-gates on the hardware (#118):** Bernardo booted the live ISO
and the Waybar health icon was red. Everything downstream was working and the Waybar health icon was red. Everything downstream was working
correctly, which is what made it worth writing down: smartd's config is correctly, which is what made it worth writing down: smartd's config is

View File

@@ -560,23 +560,37 @@
touch $out touch $out
''; '';
# Live-ISO baseline apps (ROADMAP § live-ISO baseline apps, #103). # Live-ISO baseline apps (ROADMAP § live-ISO baseline apps, #103)
# The live session shipped no # plus the mime-default presence guard (#94 / #119). The live
# browser and no office for as long as it existed — the thing a user # session shipped no browser and no office for as long as it
# judges the distro by before installing, and boots to rescue a dead # existed — the thing a user judges the distro by before installing,
# machine. Nothing in a build fails when an app silently leaves the # and boots to rescue a dead machine. Nothing in a build fails when
# live profile (it just stops being in the launcher, which is # an app silently leaves the live profile (it just stops being in
# invisible until someone boots the ISO on real hardware and looks), # the launcher), and a mime default that names a missing .desktop is
# so assert both halves: on PATH *and* a .desktop the launcher can # silently skipped by GIO (no handler at all) — so assert both:
# see. Same artifacts the ISO ships, no ISO build. # binaries + launcher entries for the baseline suite, and that
# every Default Applications key has at least one listed .desktop
# present in the live profile or system path (thunar is system-side).
# Same artifacts the ISO ships, no ISO build.
live-baseline-apps = live-baseline-apps =
let let
liveHm = liveHm =
self.nixosConfigurations.nomarchy-live.config.home-manager.users.${username}; self.nixosConfigurations.nomarchy-live.config.home-manager.users.${username};
gen = liveHm.home.activationPackage; gen = liveHm.home.activationPackage;
# system.path carries NixOS-side apps (thunar); HM-only
# home-path does not. A mime default that only names a
# system desktop must still resolve.
sysPath =
self.nixosConfigurations.nomarchy-live.config.system.path;
# Template install path: same mime defaults, different package
# set (vscode present, gnome-text-editor not). Guard both so
# a live-only fix cannot re-break the installed machine.
templateGen = downstream.homeConfigurations.me.activationPackage;
templateSys =
downstream.nixosConfigurations.default.config.system.path;
in in
pkgs.runCommand "nomarchy-live-baseline-apps" pkgs.runCommand "nomarchy-live-baseline-apps"
{ nativeBuildInputs = [ pkgs.gnugrep pkgs.findutils ]; } { nativeBuildInputs = [ pkgs.gnugrep pkgs.findutils pkgs.python3 ]; }
'' ''
set -euo pipefail set -euo pipefail
apps=${gen}/home-path/share/applications apps=${gen}/home-path/share/applications
@@ -590,11 +604,12 @@
impress.desktop \ impress.desktop \
org.gnome.TextEditor.desktop \ org.gnome.TextEditor.desktop \
io.bassi.Amberol.desktop \ io.bassi.Amberol.desktop \
mpv.desktop \
org.gnome.Snapshot.desktop; do org.gnome.Snapshot.desktop; do
test -f "$apps/$d" || { echo "live-baseline-apps: MISSING launcher entry $d"; exit 1; } test -f "$apps/$d" || { echo "live-baseline-apps: MISSING launcher entry $d"; exit 1; }
done done
for b in chromium libreoffice gnome-text-editor amberol snapshot; do for b in chromium libreoffice gnome-text-editor amberol mpv snapshot; do
test -x "$bins/$b" || { echo "live-baseline-apps: MISSING binary $b"; exit 1; } test -x "$bins/$b" || { echo "live-baseline-apps: MISSING binary $b"; exit 1; }
done done
@@ -603,16 +618,67 @@
# so GIO silently skipped the entry and the live session had # so GIO silently skipped the entry and the live session had
# no default browser at all). # no default browser at all).
mimes=${gen}/home-files/.config/mimeapps.list mimes=${gen}/home-files/.config/mimeapps.list
grep -q '^x-scheme-handler/https=chromium-browser.desktop' "$mimes" grep -q '^x-scheme-handler/https=chromium-browser.desktop' "$mimes" \
|| { echo "live-baseline-apps: https mime must name chromium-browser.desktop (#94)"; exit 1; }
test -f "$apps/chromium-browser.desktop" test -f "$apps/chromium-browser.desktop"
# text/plain must prefer vscode but fall through to the live
# editor (#119) never a singleton that only names code.
grep -qE '^text/plain=.*org\.gnome\.TextEditor\.desktop' "$mimes" \
|| { echo "live-baseline-apps: text/plain must fall through to org.gnome.TextEditor.desktop (#119)"; exit 1; }
test -f "$apps/org.gnome.TextEditor.desktop"
# Every Default Applications key: at least one listed
# .desktop must exist in home-path OR system path. A key
# whose every entry is missing is the #94/#119 bug class.
# Covers live *and* the default template install.
python3 - "$mimes" "$apps" "${sysPath}/share/applications" \
"${templateGen}/home-files/.config/mimeapps.list" \
"${templateGen}/home-path/share/applications" \
"${templateSys}/share/applications" <<'PY'
import sys
from pathlib import Path
def check(label, mimes, *app_dirs):
text = Path(mimes).read_text()
in_defaults = False
bad = []
for line in text.splitlines():
if line.startswith("["):
in_defaults = line.strip() == "[Default Applications]"
continue
if not in_defaults or not line.strip() or line.startswith("#"):
continue
if "=" not in line:
continue
key, val = line.split("=", 1)
desktops = [d for d in val.split(";") if d.strip()]
if not desktops:
bad.append(f"{key}= (empty)")
continue
if not any(
any((Path(d) / desk).is_file() for d in app_dirs)
for desk in desktops
):
bad.append(f"{key}={';'.join(desktops)} (none present)")
if bad:
print(f"live-baseline-apps: mime defaults missing on {label}:")
for b in bad:
print(f" {b}")
sys.exit(1)
print(f"live-baseline-apps: mime defaults ok on {label}")
check("live", sys.argv[1], sys.argv[2], sys.argv[3])
check("template", sys.argv[4], sys.argv[5], sys.argv[6])
PY
# Firefox is deliberately absent a second browser is the one # Firefox is deliberately absent a second browser is the one
# item in #103's list that costs a real closure, and chromium # item in #103's list that costs a real closure, and chromium
# owns the mime default (Bernardo 2026-07-14). If it ever # owns the mime default (Bernardo 2026-07-14). If it ever
# ships here, that is a size decision, not a drive-by. # ships here, that is a size decision, not a drive-by.
test ! -f "$apps/firefox.desktop" || { echo "live-baseline-apps: firefox reappeared size decision, see #103"; exit 1; } test ! -f "$apps/firefox.desktop" || { echo "live-baseline-apps: firefox reappeared size decision, see #103"; exit 1; }
echo "live-baseline-apps: ok browser, office, editor, music, camera on PATH + in the launcher" echo "live-baseline-apps: ok browser, office, editor, music, video, camera + mime defaults resolve"
touch $out touch $out
''; '';
@@ -1919,6 +1985,8 @@
libreoffice-fresh # documents/sheets/slides off a dying disk libreoffice-fresh # documents/sheets/slides off a dying disk
gnome-text-editor # the maintained gedit successor gnome-text-editor # the maintained gedit successor
amberol # music player (also in the template) amberol # music player (also in the template)
mpv # video (mime defaults → mpv.desktop; also
# template — free on the ISO via the pin)
snapshot # camera — the maintained Cheese successor snapshot # camera — the maintained Cheese successor
]; ];
}; };

View File

@@ -44,8 +44,12 @@ lib.mkIf config.nomarchy.mime.enable {
"audio/x-m4a" = "io.bassi.Amberol.desktop"; "audio/x-m4a" = "io.bassi.Amberol.desktop";
"audio/aac" = "io.bassi.Amberol.desktop"; "audio/aac" = "io.bassi.Amberol.desktop";
# The template's active editor; degrades if you drop vscode. # Prefer the template's vscode; fall through to gnome-text-editor
"text/plain" = "code.desktop"; # (ships on the live ISO after #103, and is the only editor there).
# A singleton that names an absent .desktop is silently skipped by
# GIO and leaves no handler at all — the #94 / #119 trap. HM tries
# the next entry when the preferred one is missing.
"text/plain" = [ "code.desktop" "org.gnome.TextEditor.desktop" ];
# The system-side Thunar (nomarchy.system.fileManager). # The system-side Thunar (nomarchy.system.fileManager).
"inode/directory" = "thunar.desktop"; "inode/directory" = "thunar.desktop";