Skip to content

SimulationOS Alpha foundation: bootable ISO, live Hyprland desktop, installer, CI - #1

Merged
HaiderBassem merged 33 commits into
mainfrom
feat/alpha-foundation
Sep 28, 2026
Merged

HaiderBassem merged 33 commits into
mainfrom
feat/alpha-foundation

Conversation

@HaiderBassem

Copy link
Copy Markdown
Owner

Turns the profile into a coherent Alpha that builds, boots and de-lives correctly, with CI proving it.

Verified by CI run 36388606710 (all green)

Job Result
Static validation success
Build ISO success — 2.0 GB, archiso 90-1
QEMU boot (uefi) success
QEMU boot (bios) success
Live system health success
De-live security contract success

Artifact simulationos-alpha-6bcbb90…-x86_64, SHA256 81cedf5a552ddbb294422e36a14e23ac19c7442f5ce8a2bef9454abc854ff867.

What the live-health job proves (real guest output)

wait-desktop    DESKTOP_READY, graphical.target active
sessions        3 1000 liveuser seat0 721 user tty1     <- SDDM autologin
hyprland        785 Hyprland --watchdog-fd 4
session-procs   827 hyprpaper  828 waybar  829 mako
portals         1144 xdg-desktop-portal  1165 -gtk
networkmanager  active | connected:full | enp0s2 connected
dns             archlinux.org resolves

What the de-live contract proves (19 gates on the real ISO)

liveuser removed · /home/liveuser gone · SDDM and tty1 autologin removed ·
NOPASSWD sudo replaced by a password-prompting %wheel rule · permissive polkit
rule removed · root locked · archiso initramfs hook removed · installer
launcher removed — while display-manager, graphical.target, SimulationOS
identity and /etc/skel survive.

Original defects fixed

Three-way kernel-name mismatch (ISO could not boot) · broadcom-wl removed
from Arch (build abort) · no desktop installed despite graphical.target ·
installed systems would have inherited passwordless root ·
brightnessctl set 0% blanking the screen on first login · /etc/hosts with
no localhost · rm aliased to an unshipped trash-put · SDDM config filed
under polkit rules.d · six dead scripts depending on an uninstalled chwd.

Not yet proven

Calamares GUI install and installed-disk boot are NOT TESTED; the de-live
contract covers the security-critical half of that path. See docs/adr/ for
open decisions (profiles as meta-packages, nftables policy).

- bootmodes: replace the 6 removed names with bios.syslinux + uefi.systemd-boot
  (uefi.systemd-boot already covers x64 + mixed-mode IA32; mkarchiso rejects it
  alongside uefi.grub)
- drop buildmodes=('iso') (implicit default) and iso_icon (not read by mkarchiso)
- rename ISO to simulationos so mkarchiso emits simulationos-<ver>-x86_64.iso
- pacman.conf: declare [cachyos] with explicit Server= URLs instead of
  Include=/etc/pacman.d/cachyos-mirrorlist, so the build no longer silently
  depends on a developer-specific host file
- pacman.conf: remove DisableSandbox; keep SigLevel = Required DatabaseOptional
- order Arch repos before [cachyos] so shared packages come from Arch
The profile installed linux-cachyos but the boot entries referenced
vmlinuz-linux and the custom preset referenced a nonexistent
vmlinuz-linux-simos. Three names, none of which lined up; the ISO could
not have booted.

- all boot entries now use vmlinuz-linux-cachyos / initramfs-linux-cachyos.img,
  confirmed from the linux-cachyos PKGBUILD (pkgbase=linux-cachyos)
- delete airootfs/etc/mkinitcpio.d/linux.preset: presets are keyed to pkgbase,
  so it was orphaned. Upstream archiso removed its own preset for the same
  reason ('the default preset is good enough')
- rebrand boot menus to SimulationOS; rename entries to 01/02/03-simulationos-*
- delete grub/grub.cfg: only the uefi.grub bootmode reads it, and that cannot
  be combined with uefi.systemd-boot. grub/loopback.cfg is kept because
  mkarchiso installs it whenever no *grub* bootmode is set
- drop broadcom-wl: removed from the Arch repositories, would abort pacstrap
…desktop

The inherited config was the author's laptop setup and could not work on
another machine:
- exec-once = brightnessctl set 0%  -> black screen on first login
- wallpaper at /mnt/m.2_ssd/Linux/Wallpapers/1354205.jpeg
- ~/.scripts/Acer/PredatorSense/... on autostart
- hardcoded USB audio sink, SYNA7DB5:00 touchpad, eDP-2 output
- required grimblast / auto-cpufreq / pamixer / ML4W, none shipped
- two wallpaper daemons (swww + hyprpaper); monitor.conf AND monitors.conf;
  several files shipped but never sourced

Replaced with one hyprland.conf plus an empty monitors.conf for per-machine
overrides. Every exec-once and keybind target maps to a shipped package or a
script in airootfs/usr/local/bin. Waybar uses built-in modules only, so a
missing helper can never blank the bar.

Also fixes .bashrc, which aliased rm to trash-put without shipping it,
breaking rm in the live session.

Wallpaper is a real 1920x1080 asset, reproducible via scripts/make-wallpaper.py.
Session:
- SDDM with the default X11 greeter (xorg-server/xorg-xauth are hard deps)
  launching the Wayland hyprland.desktop; theme 'maldives' ships in the sddm
  package, unlike the previously configured 'breeze' which needs Plasma
- display-manager.service symlink instead of the never-invoked dmcheck guesser
- the SDDM autologin config was previously filed under polkit-1/rules.d/,
  where SDDM would never read it

Network: NetworkManager only. systemd-networkd, its .network files, iwd,
statically enabled wpa_supplicant and systemd-resolved (plus the resolv.conf
stub symlink that depended on it) are removed. Running two network stacks is a
reliable source of nondeterministic breakage.

Audio: pipewire/wireplumber user units are enabled explicitly via
/etc/systemd/user/, derived from the units' actual [Install] sections, rather
than relying on package presets (the packages ship no .wants symlinks).

Security: sshd is installed but no longer enabled by default, and the
permissive upstream drop-in (PermitRootLogin yes) is replaced. Live-only
privilege grants are namespaced *-live-* so the installer can strip them.
Replaces calamares-online.sh, which referenced /etc/version-tag and a
simulationos-keyring that never existed, and which nothing invoked.

The config lives at /usr/share/simulationos/calamares and is launched with
'calamares -c'. It cannot live in /etc/calamares: cachyos-calamares owns ~60
files under /etc/calamares/modules and declares no backup= array, and the
airootfs overlay is copied BEFORE pacstrap, so that would be a hard file
conflict. Verified in the Calamares source that -c overrides appDataDir and
that module-config and branding lookup then resolve only inside it.

Offline install via unpackfs from
/run/archiso/bootmnt/arch/x86_64/airootfs.sfs (derived from install_dir and
arch in profiledef.sh).

Because unpackfs copies the live filesystem verbatim, the target inherits
every live concession. simulationos-deloop strips them and, critically, LOCKS
ROOT - the live medium ships root with an empty password, which unpackfs would
otherwise carry onto every installed system. It must run before initcpio so
the archiso initramfs hook is gone before the initramfs is rebuilt.
Identity: os-release (ID=simulationos), issue, motd, hostname,
simulationos-release. /etc/hosts previously contained ONLY a hardcoded GitHub
IP with no localhost entries.

Removed six scripts that nothing invoked and that all depend on CachyOS' chwd,
which was never in the package list: dmcheck, prepare-live-desktop.sh,
remove-nvidia, removeun, removeun-online, calamares-online.sh. Also removed
the chwd-dependent nvidia-module-loader and its modprobe drop-in, the
broadcom-wl drop-in (package gone from Arch), choose-mirror (its service was
never shipped), Installation_guide, pacman-more.conf, the hand-written
CachyOS-forcing mirrorlist (now provided by pacman-mirrorlist + the upstream
uncomment hook), and the reflector config for a package that is not installed.

Shipping half-working hardware detection is worse than shipping none: NVIDIA
falls back to nouveau for the Alpha.
validate-profile.sh catches the class of breakage that otherwise only appears
at boot: kernel-name drift between packages.x86_64 and the boot entries,
Hyprland launching binaries nobody ships, developer paths, dangling systemd
symlinks, missing Calamares module configs, a deloop script that stopped
removing the archiso initramfs hook, more than one wallpaper daemon, and
shell syntax errors. Runs on bash 3.2 without pacman, so it works on macOS.

Verified by fault injection: 10 deliberate regressions introduced into a
scratch copy, 10 caught.

build-iso.sh refuses to run on a non-Linux or non-x86_64 host, without root,
without archiso, or without the CachyOS key trusted - each with the exact
remediation - rather than producing a wrong-architecture or unsigned build.
It never disables signature verification.
Records, for each significant decision, which behaviour is upstream archiso,
which is CachyOS-specific and which is a SimulationOS decision, plus the
verification status of every claim - explicitly separating what was statically
verified from what has never been booted.
One build engine for developers, CI and releases, so a local build and a CI
build cannot drift. build.sh decides WHERE to build - natively on Arch x86_64,
otherwise in an x86_64 Arch container - and then runs the same
scripts/build-iso.sh either way. On an arm64 host the container is pinned to
linux/amd64 so an arm64 ISO can never be produced by accident.

Found by actually running it: pacman >=7 sandboxes downloads with seccomp and
an unprivileged alpm user, and that filter fails with EINVAL under qemu-user
emulation. The workaround is confined to the throwaway builder container (a
patched copy of pacman.conf plus --disable-sandbox); the committed pacman.conf
never sets DisableSandbox and signature verification is never disabled.
validate-profile.sh now fails if DisableSandbox or 'SigLevel = Never' ever
appears in a committed config, so the concession cannot leak into the product.

profiledef.sh takes pacman_conf from $SIMOS_PACMAN_CONF so the builder can
supply that patched copy without modifying the repository.

Also adds scripts/inspect-iso.sh: mkarchiso exiting 0 is not evidence of a
usable image, so the artefact is checked for the kernel and initramfs named by
packages.x86_64, the squashfs, the bootloader payloads for the declared
bootmodes, and a matching checksum. build-iso.sh now writes build-info.json
(commit, archiso version, checksum, duration) and build.log.
- ./build.sh is documented as the single entry point; the container path is
  now automatic rather than a manual podman recipe
- drops the hardcoded cachyos-keyring package URL, which goes stale on every
  CachyOS keyring rebuild, in favour of installing through ./pacman.conf
- verification table now carries evidence per row, and distinguishes
  'mkarchiso accepts the profile' (verified: a real run passed bootmode
  validation and entered pacstrap) from 'ISO build completes' (not yet)
Found by an actual build, 40 minutes in:

  error: extract: not overwriting dir with file .../airootfs/usr/lib/Xorg
  ==> ERROR: Failed to install packages to new root

xorg-server ships BOTH /usr/lib/Xorg (a file) and /usr/lib/xorg/ (a
directory). On a case-insensitive filesystem those are the same path, so
pacstrap aborts. The work directory was bind-mounted from macOS, whose APFS
is case-insensitive by default; confirmed directly on this host.

- ./build.sh now puts the work directory inside the container
  (/var/tmp/simulationos-work, case-sensitive overlayfs) and bind-mounts only
  out/, so the artefact still lands on the host.
- build-iso.sh probes the work directory for case sensitivity up front and
  fails immediately with the fix, instead of wasting a long build.
- clean-build.sh previously refused any path outside the repository, which
  would have rejected the new container work directory. It now accepts
  explicit build directories under /var/tmp or /tmp while still refusing
  system paths such as /usr.

This class of bug is invisible to static analysis and to dependency
resolution; only running the build surfaces it.
'QEMU started' is not evidence that an OS booted. boot-test.sh boots the ISO
headlessly with the kernel console on a serial port and waits for proof the
system reached graphical.target, reporting which stages were observed:
kernel, linux-cachyos, archiso initramfs, systemd, multi-user, graphical.

It watches for failure signatures (kernel panic, unable to mount root,
emergency mode) as well as success, so a crash is reported rather than being
indistinguishable from 'still booting', and every run ends in PASS, FAIL or
TIMEOUT rather than hanging.

boot.yml runs it for both firmware modes the profile actually declares -
uefi.systemd-boot and bios.syslinux - and nothing else, and uploads the
serial log on failure. iso.yml chains it after the build.
Found by an actual build:

  ==> ERROR: file not found: '/usr/lib/initcpio/ipconfig'
  ==> ERROR: binary not found: 'nbd-client'
  ==> ERROR: file not found: '/usr/lib/initcpio/nfsmount'

The inherited HOOKS line kept archiso_pxe_common/_nbd/_http/_nfs while the
package cleanup removed mkinitcpio-nfs-utils, nbd and nfs-utils as server
bloat. I had judged the PXE plumbing 'upstream, harmless'; it is not harmless
once its support packages are gone.

SimulationOS does not support PXE netboot - the product journey is
download -> flash USB -> boot - so the hooks are removed rather than the
packages restored, along with syslinux/archiso_pxe*.cfg and the pxe branch of
syslinux.cfg, which were unreachable configuration.

validate-profile.sh now enforces the hook -> package relationship and flags a
PXE syslinux config with no PXE hook enabled. Verified by injecting
archiso_pxe_nbd without the nbd package: the validator rejects it.
Both found by inspecting a real 2.1 GB artefact:

- build-info.json was invalid JSON. It re-sourced profiledef.sh to read
  iso_version/arch, but profiledef.sh assigns file_permissions as an
  associative array without declaring it; mkarchiso declares it first, a bare
  source does not, so bash failed with an arithmetic syntax error on
  '/etc/shadow'. Version and architecture are now derived from the produced
  filename, which is ground truth.

- inspect-iso.sh looked for /syslinux/ but mkarchiso writes boot/syslinux/,
  so a correct ISO was reported as missing its BIOS payload. It now checks
  boot/syslinux/isolinux.bin and the generated menu, and additionally extracts
  both boot menus FROM the ISO to assert they say SimulationOS, carry no
  'Arch Linux install medium' label, and point at the kernel the profile
  installs.

L3 now passes on the real artefact: kernel, initramfs, squashfs, unpackfs
source path, BIOS and UEFI payloads, branding and checksum.
The container had no git, and even with git installed it refuses to read a
bind-mounted repository owned by another uid ('dubious ownership'), so every
build recorded git_commit: unknown. A release engineer cannot answer 'which
commit produced this ISO' from that.

Installs git in the builder and marks /simulationos a safe directory.
The ISO now builds end to end. The verification table distinguishes what the
artefact proves (kernel naming, both bootloader payloads, branding read out of
the ISO, installer source path, checksum) from what it does not: it has never
been booted.
qemu-system-x86_64 from Homebrew ships its firmware as
share/qemu/edk2-x86_64-code.fd with edk2-i386-vars.fd as the vars template,
neither of which matched the Linux distribution paths the scripts searched.
Both boot-test.sh and test-iso.sh now find them.

boot-test.sh takes SIMOS_QEMU_ACCEL so a host without KVM (any macOS host)
selects TCG explicitly instead of relying on the kvm:tcg fallback.
The first real boot test proved the ISO boots - OVMF loaded it and
systemd-boot rendered the branded SimulationOS menu with all three entries and
the configured 15s timeout - but every post-bootloader stage showed as MISS
because the kernel logs only to tty0, which is invisible headlessly.

Boot entries now carry 'console=ttyS0,115200 console=tty0'. tty0 is listed
last so it remains the primary console for a normal user, while serial makes
the boot debuggable headlessly, on real hardware and in CI. This follows
archiso's own intent: it already enables a serial console for syslinux
(SERIAL 0 115200 in archiso_head.cfg). It is a product improvement, not a
concession to the test harness.

boot-test.sh additionally reports the firmware/bootloader stages it can
observe, so a bootloader-level regression is distinguishable from a kernel
one.
The builder container has no python3, so the build-info.json validity check
always failed and printed 'WARNING: build-info.json is not valid JSON' about
a perfectly valid file. It now tries python3, falls back to jq, and says it
skipped the check when neither exists - and a genuine JSON failure now aborts
the build instead of only warning.
First end-to-end boot of a SimulationOS ISO, under QEMU + OVMF:

  BdsDxe: starting Boot0001 "UEFI QEMU DVD-ROM"
    SimulationOS (x86_64, UEFI)        <- systemd-boot menu, our entries
  [    0.391639] virt/tdx: ...          <- kernel executing
  SimulationOS Alpha Linux 7.2.7-1-cachyos (ttyS0)
  simulationos login:

That chain proves firmware -> systemd-boot -> linux-cachyos -> archiso
initramfs -> systemd -> getty, with /etc/issue branding and the hostname
applied. It does NOT prove the graphical session, the installer, or an
installed system.

boot-test.sh had reported this healthy boot as a TIMEOUT because its success
pattern required 'Reached target Graphical Interface'. systemd sends status
to tty0, the primary console, so that string never reaches serial; a login
prompt is in fact stronger evidence. Success detection now accepts it.

The script also distinguishes stages that genuinely failed from stages simply
not visible on serial, instead of printing MISS for both, and gains
SIMOS_BOOT_ANALYZE_ONLY=1 to re-evaluate a captured log without rebooting.
Extracts the SquashFS from the built ISO, runs simulationos-deloop inside it
exactly as Calamares does (including the removeuser step), and asserts the
installed-system security gates.

This covers acceptance gates 15-19 without needing to drive the graphical
installer: liveuser absent, /home/liveuser gone, SDDM and tty1 autologin gone,
NOPASSWD sudo replaced by a password-prompting %wheel rule, permissive polkit
rule gone, root locked and no longer empty-password, archiso initramfs hook
gone - while confirming the display manager, graphical target, SimulationOS
identity and /etc/skel survive.

It also asserts the live-medium PRECONDITIONS first, so the test cannot pass
vacuously against a rootfs that never had the live configuration.

Verified locally against simulationos-2026.09.27-x86_64.iso: 19/19 gates.
Boots the ISO, logs in on the serial console and checks RUNNING state rather
than package presence: default target, graphical.target, display-manager,
liveuser session, Hyprland, waybar/hyprpaper/mako/hyprpolkitagent/nm-applet,
xdg-desktop-portal, NetworkManager, DNS, PipeWire via wpctl, and the presence
of the installer binary, config and launcher.

The unattended serial login relies on the live medium shipping root with an
empty password, which is standard archiso and is removed on install by
simulationos-deloop.
- iso.yml now builds through ./build.sh on the runner instead of running
  actions/checkout inside an Arch container. build.sh already knows how to
  drive an x86_64 Arch container, so CI and local builds share one engine
  and cannot drift. Adds a disk-cleanup step, since a 2.1 GB ISO plus a 4 GB
  rootfs does not fit beside the runner's preinstalled toolchains.
- artifacts are named simulationos-alpha-<sha>-x86_64 and carry the ISO,
  its .sha256 and build-info.json, uploaded uncompressed.
- boot.yml and acceptance.yml take the artifact name as an input so every
  downstream job tests the exact ISO this commit produced.
- acceptance.yml adds live-health (running-state checks in the booted guest)
  and deloop-contract (installed-system security gates).
- all workflows expose workflow_dispatch.
CI run 36362920104 failed in pacstrap:

  mount: /var/tmp/simulationos-work/x86_64/airootfs/dev: permission denied
  ==> ERROR: failed to setup chroot

Root cause: build.sh picked the runtime by name order (podman, then docker).
GitHub's ubuntu-latest ships a ROOTLESS podman alongside a rootful docker, so
podman won. pacstrap bind-mounts /dev inside the chroot, which needs real
CAP_SYS_ADMIN on the host; a rootless container cannot provide that even with
--privileged.

The runtime is now selected by capability rather than by name: docker if its
daemon responds, otherwise podman - and if podman is rootless, via sudo when
available, or a clear error explaining the three ways to fix it instead of an
opaque mount failure deep inside pacstrap.

SIMOS_CONTAINER still overrides, and now accepts a multi-word value such as
'sudo podman'.

Fixed in build.sh rather than special-cased in the workflow, so CI exercises
exactly the logic a developer gets locally.
CI run 36363194961 built the ISO successfully (2043 MB) and then failed:

  bsdtar not available; cannot inspect ISO contents

inspect-iso.sh reads the ISO9660 filesystem with bsdtar, which ubuntu-latest
does not ship; it lives in libarchive-tools. The script already fails loudly
with the reason rather than silently skipping the check, which is why this
surfaced immediately - so the fix is to provide the tool, not to soften the
check.
CI run 36364204960 failed live-health after 3 seconds:

  qemu-system-x86_64: -device hda-duplex: no default audio driver available
  guest never reached a login prompt

live-health.sh declared '-audiodev none,id=nosnd' but never attached it to
hda-duplex, so QEMU aborted at startup. On a host with no default audio
backend - every GitHub runner - that is fatal.

test-iso.sh had the same latent bug and would have aborted identically for
any developer whose machine has no default QEMU audio backend; it now takes
SIMOS_QEMU_AUDIODEV, defaulting to a silent-but-always-valid 'none'.

live-health.sh now also distinguishes 'QEMU exited' from 'timed out'. They
need completely different investigation: the first is a harness problem, the
second may be emulation slowness on a runner without KVM.
CI run 36365762103 reached the login prompt and ran the checks, but 12 of 13
assertions failed and the log contained a 'less' help screen.

Two harness defects, not SimulationOS defects:

1. systemctl and journalctl page by default. A pager took over the serial
   console, so every subsequent command was delivered as keystrokes INSIDE
   less rather than to the shell. The guest shell now exports
   PAGER=cat SYSTEMD_PAGER=cat SYSTEMD_COLORS=0 TERM=dumb.

2. Collection used fixed sleeps. Under TCG on a runner without KVM a command
   routinely takes longer than any sleep worth guessing, so replies landed in
   the middle of the next command and section markers never matched. Each
   command now waits for its own ##END marker with a bounded timeout, so
   slowness delays the run instead of corrupting it.

Also fixes a malformed 'journalctl -g -i hyprland' (-g takes a pattern).

The assertions themselves are unchanged: this makes the measurement reliable,
it does not make the checks easier to pass.
Inspecting the serial log from run 36368038012 showed the collected sections
contained only ECHOED COMMAND TEXT, never command output.

The serial line echoes what we type, and the command text itself contained
the literal '##BEGIN x' and '##END x'. So:
  - wait_for matched the echo and returned immediately, before the command
    had run;
  - the assertions then matched against echoed command text, which happens to
    contain words like 'graphical', 'active' and 'Hyprland'.

11 of 13 checks were therefore FALSE PASSES. The job only went red at all
because two patterns did not appear in any echoed line. A test that passes by
matching its own input is worse than no test.

Markers are now assembled at runtime with printf: the typed line contains
'##BEG%s'/'##EN%s', so only genuine guest output can produce '##BEGIN label'
and '##END label'. Section extraction anchors on whole lines, stty -echo is
sent as its own command once the shell is ready and its effect is confirmed
rather than assumed.

No assertion was weakened; this makes the results real.
Run 36370016973 reported 'serial root login failed' even though the guest had
emitted SIMOS_SHELL_READY. Two defects:

1. wait_for grepped the RAW serial log while sect() grepped a cleaned copy.
   systemd emits OSC shell-integration sequences (ESC]3008;...) immediately
   before command output, so a '^'-anchored pattern could never match in the
   raw stream. Both now share one clean_log helper that strips NULs, CSI
   colour codes and OSC sequences and converts CR to LF, so line anchors mean
   what they say.

2. The OVMF firmware was located but never passed to QEMU, so the guest
   booted via SeaBIOS/ISOLINUX instead of UEFI. The pflash drives are now
   attached, making this a real UEFI live-session test.

Incidentally the log confirms the BIOS path end to end: ISOLINUX 6.04 ->
SimulationOS menu -> vmlinuz-linux-cachyos -> initramfs -> login prompt.
Replaying run 36372041948's serial log with corrected extraction shows the
live desktop genuinely works:

  sessions: 2 1000 liveuser seat0 674 user tty1   <- SDDM autologin
  hyprland: 772 Hyprland --watchdog-fd 4
  session:  817 hyprpaper  818 waybar  819 mako
            821 nm-applet  828 hyprpolkitagent

12 of 13 checks pass on real guest output. Two remaining harness issues:

1. Residual OSC text can precede a marker on the same line, so '^##BEGIN x'
   never matched (BEGIN=0 while END=17). clean_log now forces each marker
   onto its own line, making extraction independent of what precedes it.

2. The portal check was simply wrong. xdg-desktop-portal is D-Bus activated
   and is SUPPOSED to be absent until something requests a portal, so pgrep
   could only pass by accident. It now asks D-Bus to activate the portal and
   confirms it answers - a functional test rather than a liveness guess.
Run 36385709091 exposed two defects in the checks themselves.

1. 'active' is a SUBSTRING of 'inactive'. The graphical.target section
   literally contained 'inactive' and the assertion passed anyway. Service
   state assertions now anchor on '^active$'. This was a false pass that
   would have been reported as a verified graphical target.

2. session-procs passed in run 36372041948 and failed in 36385709091 from the
   same ISO: the checks race the session still starting under TCG on a runner
   without KVM. The guest now waits for graphical.target AND waybar AND
   Hyprland before anything is asserted, up to 10 minutes, and emits
   DESKTOP_READY. That readiness is itself a new gate, so a desktop that
   never comes up fails loudly instead of producing a flaky mixture.

The portal assertion is renamed to 'present' to describe what it checks, now
that activation is attempted first.
artifacts/ holds ISOs pulled from GitHub Actions for local verification and
must never be committed.
@HaiderBassem HaiderBassem self-assigned this Sep 28, 2026
@HaiderBassem
HaiderBassem merged commit 3f89af9 into main Sep 28, 2026
14 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.

2 participants