Cursor Skill

auv-wayland-rendering

Prepare, diagnose, and validate Linux GPU and Wayland environments for AUV rendering and capture through XDG Desktop Portal and PipeWire. Use when installing AUV desktop runtime dependencies, connecting from another machine over SSH, using an existing compositor or a headless wlr

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download moeru-ai-auv-.agents_skills_auv-wayland-rendering-372a07b.zip · 12 KB
moeru-ai/auv 51 6 forks Apache-2.0 Updated 21h ago
Part of moeru-ai/auv — 28 skills

Install

skills CLI npx skills add https://github.com/moeru-ai/auv/tree/main/.agents/skills/auv-wayland-rendering
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install moeru-ai-auv@llmmart
Git git clone https://github.com/moeru-ai/auv.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole moeru-ai/auv collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

AUV Wayland Rendering

Build a reproducible Linux desktop session in which AUV can observe a Wayland display and persist capture artifacts. Treat environment validation as evidence, not as a change to AUV's public support claim.

Keep ownership explicit: the compositor renders application clients; AUV requests frames through the portal and records them. Do not imply that AUV ships the compositor or a Linux overlay renderer.

Read the relevant references

  • Read setup.md before installing packages, creating a compositor session, or configuring remote viewing.
  • Read troubleshooting.md when a probe or capture fails, the image is blank, or hardware rendering is uncertain.
  • Check AUV's current support matrix before describing a result as supported. A successful live probe is live-validated evidence for the named environment only.

Follow the workflow

1. Inspect before changing the host

Run the read-only probe in the target shell:

.agents/skills/auv-wayland-rendering/scripts/probe-wayland.sh

Add --require-gpu to make missing GPU prerequisites fatal when hardware rendering is required. The flag does not replace compositor-log evidence. Record:

  • distribution and package manager;
  • existing compositor and whether the session is local, nested, or headless;
  • WAYLAND_DISPLAY, XDG_RUNTIME_DIR, and the session D-Bus;
  • accessible /dev/dri/renderD* nodes and the renderer reported by compositor logs or eglinfo/vulkaninfo;
  • ScreenCast portal and PipeWire readiness.

Do not infer GPU rendering from the presence of a GPU or render node. Require a renderer log or renderer-tool result. Label pixman, llvmpipe, and similar paths as software rendering.

2. Choose the session shape

Prefer the existing logged-in Wayland compositor when one is available. It already owns the seat, GPU, D-Bus activation environment, and user consent UI.

Use headless Sway when the host has no desktop session or the test must be isolated. Start with wlroots renderer auto-selection. Force pixman only as an explicit software fallback; doing so gives deterministic rendering but does not validate the GPU path.

Use a nested compositor when isolation is needed inside an existing Wayland desktop and an extra window is acceptable. Do not point AUV at one compositor while its portal backend is attached to another.

3. Install the smallest capability set

Install or verify:

  • a Wayland compositor such as Sway;
  • xdg-desktop-portal and the backend for that compositor;
  • PipeWire and WirePlumber;
  • wayland-info and grim for independent diagnostics;
  • WayVNC only when a human needs to view the wlroots session remotely.

Request authorization before changing system packages, user services, group membership, login linger, or firewall state. Prefer distribution packages. Use a source build or patch only after reproducing a version-specific defect and recording the exact upstream revision and reason.

4. Keep one coherent user session

Run the compositor, portal backend, PipeWire, and AUV as the same unprivileged user. They must agree on XDG_RUNTIME_DIR, WAYLAND_DISPLAY, and the session bus. For a compositor started by a user service, import its generated variables into the systemd user manager and retrieve them in SSH shells.

Use the helper to run a command with either the current environment or the variables published by the user manager:

.agents/skills/auv-wayland-rendering/scripts/with-wayland-session.sh \
  auv invoke display.list --json

Do not run the compositor or AUV as root to work around device permissions. Fix seat, logind, container-device, or render-node access instead.

5. Validate from dependencies to AUV artifacts

Validate in this order so each failure has one clear owner:

  1. Confirm the Wayland socket and compositor output with wayland-info or the compositor IPC.

  2. Confirm PipeWire and the org.freedesktop.portal.ScreenCast interface.

  3. Run the AUV Linux driver probe:

    cargo run -p auv-driver-linux --example validate -- permissions displays
    
  4. Run repeated product-surface captures:

    .agents/skills/auv-wayland-rendering/scripts/verify-auv-capture.sh \
      --auv target/debug/auv --repeat 3
    
  5. Open at least one emitted PNG. Verify that it contains the expected client, has the expected dimensions, and is not a blank or stale frame.

Repeated captures establish startup and first-frame reliability. To test freshness, visibly change a client between two capture passes and compare their artifacts; identical static frames do not prove that updates propagate.

The verification script checks AUV's JSON result, artifact purpose, PNG signature, and file existence. It does not prove semantic correctness of the rendered application; visual inspection or an app-specific assertion remains separate.

6. Report the boundary precisely

Report all of the following:

  • OS, compositor, portal backend, output name and dimensions;
  • renderer and whether it is hardware or software;
  • portal frame transport when known (dma-buf, shared memory, or fallback);
  • AUV revision and exact validation commands;
  • successful run IDs and artifact paths;
  • whether remote viewing was enabled and how it was secured;
  • known warnings, fallbacks, and missing capabilities.

Do not turn compilation, a portal introspection result, or a single screenshot into a blanket Linux support claim.

Keep GPU compositor rendering, GPU-buffer transport, and correct AUV artifact output as three separate claims. None implies the other two.

Apply safety rules

  • Bind WayVNC to loopback and use an SSH tunnel unless authenticated transport was explicitly configured.
  • Do not expose an unauthenticated VNC port on 0.0.0.0.
  • Do not overwrite an existing desktop's portal selection without checking its current backend and other consumers.
  • Do not persist services or enable login linger unless the user requested a durable session.
  • Keep capture and input evidence separate. A rendered frame does not prove that input was delivered or that an application completed an operation.
Files (auv)
  • agents
    • openai.yaml 256 B
      interface:
        display_name: "AUV Wayland Rendering"
        short_description: "Run and validate AUV capture on GPU Wayland hosts"
        default_prompt: "Use $auv-wayland-rendering to prepare and validate AUV rendering and capture in this Linux Wayland environment."
      
  • references
    • setup.md 5.4 KB
      # Setup recipes
      
      Use this reference to select dependencies and session topology. Prefer the
      host's existing Wayland desktop. Use a headless compositor only when no desktop
      exists or isolation is required.
      
      ## Capability map
      
      | Capability | Typical component | Required for AUV capture |
      | --- | --- | --- |
      | Wayland compositor | Sway or the host desktop | Yes |
      | Portal frontend | `xdg-desktop-portal` | Yes |
      | wlroots portal backend | `xdg-desktop-portal-wlr` | For Sway/wlroots |
      | Frame transport | PipeWire | Yes for ScreenCast |
      | Session policy | WirePlumber | Normally yes |
      | Output inspection | `wayland-info`, compositor IPC | Diagnostic |
      | Independent screenshot | `grim` | Diagnostic on wlroots |
      | Remote human view | WayVNC | Optional on wlroots |
      | Test client | Foot or another Wayland app | Recommended |
      
      Do not install every portal backend. Match the backend to the compositor and
      inspect the current portal selection before changing it.
      
      ## Distribution examples
      
      Package names change. Confirm them with the distribution package index before
      installing.
      
      Debian or Ubuntu family:
      
      ```bash
      sudo apt install \
        sway xdg-desktop-portal xdg-desktop-portal-wlr \
        pipewire wireplumber wayland-utils grim foot
      sudo apt install wayvnc  # optional
      ```
      
      Arch Linux family:
      
      ```bash
      sudo pacman -S \
        sway xdg-desktop-portal xdg-desktop-portal-wlr \
        pipewire wireplumber wayland-utils grim foot
      sudo pacman -S wayvnc  # optional
      ```
      
      For AUV build dependencies, prefer the repository's `nix develop` shell. If
      using native packages, derive the current library set from `flake.nix` and CI
      rather than copying an old list.
      
      ## Existing desktop session
      
      Run AUV inside the session that already owns the Wayland socket and D-Bus. Over
      SSH, recover the compositor variables only if the desktop imported them into
      the systemd user manager:
      
      ```bash
      .agents/skills/auv-wayland-rendering/scripts/with-wayland-session.sh \
        target/debug/auv invoke display.capture
      ```
      
      If the helper cannot find a live socket, publish the variables from a process
      started by the compositor. For Sway, a configuration command can do this:
      
      ```text
      exec systemctl --user import-environment WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
      ```
      
      Restart the selected portal services after publishing a changed compositor
      environment. Avoid doing this when another live desktop session depends on the
      same user services.
      
      ## Headless Sway session
      
      Use a user service or another supervisor so the compositor and its children
      share a lifetime. Start with:
      
      ```text
      XDG_CURRENT_DESKTOP=sway
      XDG_SESSION_TYPE=wayland
      WLR_BACKENDS=headless
      WLR_HEADLESS_OUTPUTS=1
      WLR_LIBINPUT_NO_DEVICES=1
      ```
      
      Do not set `WLR_RENDERER` for the first attempt. Inspect Sway's debug or journal
      logs to learn what wlroots selected.
      
      To require the GLES2 path, set `WLR_RENDERER=gles2` and do not set
      `WLR_RENDERER_ALLOW_SOFTWARE=1`. Treat a failure as missing GPU/driver/device
      support, not as permission to silently relabel software rendering as GPU.
      
      For a deterministic software fallback, use:
      
      ```text
      WLR_RENDERER=pixman
      ```
      
      The fallback is useful for capture correctness tests but is not GPU evidence.
      
      A minimal Sway configuration can define the output and publish the generated
      session variables:
      
      ```text
      output HEADLESS-1 mode 1280x720
      output * bg #1f2430 solid_color
      exec sh -lc 'systemctl --user import-environment WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP XDG_SESSION_TYPE && systemctl --user restart xdg-desktop-portal-wlr.service xdg-desktop-portal.service'
      exec foot --title AUV-Wayland-Validation
      ```
      
      Confirm the real output name with Sway IPC before putting it in portal
      configuration.
      
      For unattended wlroots capture, use an xdpw configuration such as:
      
      ```ini
      [screencast]
      output_name=HEADLESS-1
      max_fps=30
      chooser_type=none
      ```
      
      `chooser_type=none` bypasses interactive consent for the selected output. Use
      it only in an isolated session whose owner requested unattended capture.
      
      ## GPU access
      
      Check all layers instead of stopping at PCI discovery:
      
      1. The host driver binds the device and exposes `/dev/dri/renderD*`.
      2. The session user can open the chosen render node.
      3. A container, if used, receives the render node and matching user/group IDs.
      4. EGL or Vulkan reports the intended device and driver.
      5. The compositor log reports the hardware renderer rather than `pixman`,
         `llvmpipe`, or another software implementation.
      6. Portal capture succeeds without an implicit software-only claim.
      
      For NVIDIA, AMD, Intel, virtual GPUs, and containers, follow the compositor and
      driver documentation for that exact stack. Do not encode vendor workarounds as
      universal defaults.
      
      ## Remote viewing
      
      Remote viewing is optional and separate from AUV capture. For wlroots, keep
      WayVNC on loopback:
      
      ```bash
      wayvnc 127.0.0.1 5900
      ```
      
      Forward it from the client:
      
      ```bash
      ssh -N -L 5900:127.0.0.1:5900 user@host
      ```
      
      Then connect a VNC viewer to `127.0.0.1:5900`. If loopback plus SSH tunneling
      is unsuitable, configure WayVNC authentication and TLS before changing the
      listen address.
      
      ## Primary upstream references
      
      - [wlroots environment variables](https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/docs/env_vars.md)
      - [xdg-desktop-portal-wlr configuration](https://github.com/emersion/xdg-desktop-portal-wlr/blob/master/xdg-desktop-portal-wlr.5.scd)
      - [xdg-desktop-portal-wlr portal selection](https://github.com/emersion/xdg-desktop-portal-wlr)
      - [WayVNC remote-access guidance](https://github.com/any1/wayvnc/blob/master/README.md)
      
    • troubleshooting.md 5 KB
      # Troubleshooting
      
      Diagnose one boundary at a time. Preserve the failing command, exit status,
      relevant environment, and service logs before changing configuration.
      
      ## Failure map
      
      | Symptom | Likely boundary | Check first |
      | --- | --- | --- |
      | `WAYLAND_DISPLAY` unset over SSH | Session environment | `systemctl --user show-environment` and the live socket |
      | Wayland socket exists but clients fail | User/runtime mismatch | socket owner, `XDG_RUNTIME_DIR`, client UID |
      | Portal name missing on D-Bus | Portal frontend | `xdg-desktop-portal` user service and session bus |
      | ScreenCast interface missing | Wrong portal backend selection | `*-portals.conf`, compositor name, xdpw service |
      | Portal can select an output but no frame arrives | screencopy/buffer negotiation | xdpw and compositor versions, DMA-BUF versus SHM logs |
      | PipeWire remote or node fails | PipeWire session | PipeWire/WirePlumber services and runtime socket |
      | Capture is black or stale | Renderer or screencopy path | compositor renderer logs and an independent `grim` capture |
      | Headless compositor starts only with `pixman` | GPU initialization | render-node access, EGL/Vulkan driver, compositor logs |
      | WayVNC reports no output | Wrong compositor environment | `WAYLAND_DISPLAY`, `WAYVNC_OUTPUT`, compositor IPC |
      | First capture fails but later captures work | Initial-frame protocol defect | reproduce several times and test a maintained xdpw release |
      
      ## Session environment
      
      Compare the compositor, portal backend, and AUV processes:
      
      ```bash
      tr '\0' '\n' </proc/COMPOSITOR_PID/environ | \
        grep -E '^(XDG_RUNTIME_DIR|WAYLAND_DISPLAY|SWAYSOCK|DBUS_SESSION_BUS_ADDRESS|XDG_CURRENT_DESKTOP)='
      ```
      
      Repeat for the portal process. Do not copy environment values blindly: stale
      Wayland and Sway sockets can remain named after an earlier session. Verify that
      the current process can use them.
      
      ## Portal selection and introspection
      
      Check the public portal interface:
      
      ```bash
      busctl --user introspect \
        org.freedesktop.portal.Desktop \
        /org/freedesktop/portal/desktop \
        org.freedesktop.portal.ScreenCast
      ```
      
      For Sway, confirm that the preferred portal configuration selects `wlr` for
      ScreenCast. A generic portal frontend being active does not prove that the
      correct backend owns the implementation.
      
      Inspect logs together:
      
      ```bash
      journalctl --user \
        -u xdg-desktop-portal.service \
        -u xdg-desktop-portal-wlr.service \
        -u pipewire.service \
        -u wireplumber.service \
        --since '10 minutes ago'
      ```
      
      ## No valid screencopy format
      
      If xdpw selects the output but reports that it cannot receive a valid format
      from wlroots screencopy:
      
      1. Capture the complete xdpw and compositor versions.
      2. Check whether the compositor advertises DMA-BUF, SHM, or both.
      3. Reproduce with the current maintained distribution package or upstream
         release.
      4. Compare an independent `grim` capture.
      5. Use a source patch only when an upstream issue or exact code inspection
         demonstrates the missing protocol path.
      
      Do not distribute a locally copied binary or undocumented patch. Record source
      URL, revision, checksum, build recipe, patch, and the failing/passing probes.
      
      ## GPU versus software rendering
      
      Use at least two compatible evidence points when possible:
      
      - compositor debug or journal renderer initialization;
      - `eglinfo -B` or `vulkaninfo --summary`;
      - render-node file descriptor or device identity;
      - vendor monitoring tools while a changing scene renders.
      
      The following are not sufficient by themselves:
      
      - `lspci` or a vendor utility listing the GPU;
      - `/dev/dri/renderD*` existing;
      - Sway running with `WLR_BACKENDS=headless`;
      - a successful PNG capture.
      
      If `pixman` was forced, report software rendering even when a GPU is present.
      
      ## AUV capture diagnosis
      
      Run the driver probe before the product command:
      
      ```bash
      cargo run -p auv-driver-linux --example validate -- permissions displays capture-screen
      ```
      
      Then run:
      
      ```bash
      target/debug/auv invoke display.capture --json
      ```
      
      Interpret the layers separately:
      
      - Build success proves compilation only.
      - `permissions` proves AUV can inspect the session and portal interfaces.
      - `displays` proves output discovery and geometry.
      - `capture-screen` exercises the Linux driver directly.
      - `display.capture` proves the product invoke path and durable artifact
        projection.
      - Opening the PNG proves that the expected visual content arrived.
      
      When fallback occurs, preserve AUV's `fallback_reason`. A successful fallback
      must not be reported as success for the primary backend.
      
      ## Containers
      
      Treat a container as another session boundary. Confirm:
      
      - the matching Wayland socket is mounted;
      - `XDG_RUNTIME_DIR` inside the container resolves that mount;
      - the session D-Bus and PipeWire sockets are reachable;
      - the render device is passed through with usable ownership;
      - host and container graphics libraries are compatible.
      
      Prefer a compositor and portal entirely inside the container or entirely on
      the host. Splitting ownership across both requires deliberate socket and D-Bus
      plumbing and should be documented as a distinct topology.
      
  • scripts
    • probe-wayland.sh 6.1 KB
      #!/usr/bin/env bash
      set -uo pipefail
      
      require_gpu=false
      case "${1:-}" in
        "") ;;
        --require-gpu) require_gpu=true ;;
        -h|--help)
          printf 'usage: %s [--require-gpu]\n' "$0"
          exit 0
          ;;
        *)
          printf 'unknown argument: %s\n' "$1" >&2
          exit 2
          ;;
      esac
      
      failures=0
      
      pass() {
        printf '[ok]   %s\n' "$1"
      }
      
      warn() {
        printf '[warn] %s\n' "$1"
      }
      
      fail() {
        printf '[fail] %s\n' "$1"
        failures=$((failures + 1))
      }
      
      value() {
        local name=$1
        local current=${!name-}
        if [[ -n "$current" ]]; then
          printf '[info] %s=%s\n' "$name" "$current"
        else
          printf '[info] %s is unset\n' "$name"
        fi
      }
      
      printf 'AUV Wayland environment probe\n'
      printf '[info] kernel=%s\n' "$(uname -srm 2>/dev/null || printf unknown)"
      
      if [[ $(uname -s 2>/dev/null) == Linux ]]; then
        pass 'Linux host detected'
      else
        fail 'this workflow requires Linux'
      fi
      
      for name in XDG_SESSION_TYPE XDG_CURRENT_DESKTOP XDG_RUNTIME_DIR WAYLAND_DISPLAY SWAYSOCK DBUS_SESSION_BUS_ADDRESS; do
        value "$name"
      done
      
      runtime_dir=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
      if [[ -d "$runtime_dir" ]]; then
        pass "runtime directory exists: $runtime_dir"
      else
        fail "runtime directory is missing: $runtime_dir"
      fi
      
      if [[ -n ${WAYLAND_DISPLAY:-} ]]; then
        if [[ ${WAYLAND_DISPLAY} = /* ]]; then
          wayland_socket=$WAYLAND_DISPLAY
        else
          wayland_socket=$runtime_dir/$WAYLAND_DISPLAY
        fi
        if [[ -S "$wayland_socket" ]]; then
          pass "Wayland socket is reachable: $wayland_socket"
        else
          fail "Wayland socket is missing: $wayland_socket"
        fi
      else
        fail 'WAYLAND_DISPLAY is unset'
      fi
      
      if [[ -S "$runtime_dir/bus" ]]; then
        pass "session D-Bus socket exists: $runtime_dir/bus"
      else
        fail "session D-Bus socket is missing: $runtime_dir/bus"
      fi
      
      if command -v busctl >/dev/null 2>&1; then
        if busctl --user --no-pager introspect \
          org.freedesktop.portal.Desktop \
          /org/freedesktop/portal/desktop \
          org.freedesktop.portal.ScreenCast >/dev/null 2>&1; then
          pass 'ScreenCast portal interface is available'
        else
          fail 'ScreenCast portal interface is unavailable on the session bus'
        fi
      else
        warn 'busctl is unavailable; portal introspection was skipped'
      fi
      
      if command -v pw-cli >/dev/null 2>&1; then
        if pw-cli info 0 >/dev/null 2>&1; then
          pass 'PipeWire core is reachable'
        else
          fail 'pw-cli cannot reach the PipeWire core'
        fi
      else
        warn 'pw-cli is unavailable; PipeWire probing was skipped'
      fi
      
      render_nodes=()
      while IFS= read -r node; do
        render_nodes+=("$node")
      done < <(compgen -G '/dev/dri/renderD*' || true)
      
      if (( ${#render_nodes[@]} == 0 )); then
        if $require_gpu; then
          fail 'no DRM render node was found'
        else
          warn 'no DRM render node was found; only software rendering may be available'
        fi
      else
        for node in "${render_nodes[@]}"; do
          if [[ -r "$node" && -w "$node" ]]; then
            pass "DRM render node is accessible: $node"
          elif $require_gpu; then
            fail "DRM render node is not readable and writable: $node"
          else
            warn "DRM render node is not readable and writable: $node"
          fi
        done
      fi
      
      renderer_evidence=false
      renderer_summary=
      if command -v eglinfo >/dev/null 2>&1; then
        renderer_output=$(eglinfo -B 2>/dev/null || true)
        renderer_summary=$(printf '%s\n' "$renderer_output" | sed -n '/EGL vendor string/p;/OpenGL.*renderer:/p;/Device platform:/p;/Device:/p')
        if [[ -n "$renderer_summary" ]]; then
          printf '[info] eglinfo renderer summary:\n'
          printf '%s\n' "$renderer_summary" | sed 's/^/       /'
          renderer_evidence=true
        else
          warn 'eglinfo is installed but did not report a renderer'
        fi
      fi
      
      if ! $renderer_evidence && command -v vulkaninfo >/dev/null 2>&1; then
        renderer_output=$(vulkaninfo --summary 2>/dev/null || true)
        renderer_summary=$(printf '%s\n' "$renderer_output" | sed -n '/deviceName/p;/driverName/p;/driverInfo/p')
        if [[ -n "$renderer_summary" ]]; then
          printf '[info] vulkaninfo summary:\n'
          printf '%s\n' "$renderer_summary" | sed 's/^/       /'
          renderer_evidence=true
        else
          warn 'vulkaninfo is installed but did not report a renderer'
        fi
      elif ! command -v eglinfo >/dev/null 2>&1 && ! command -v vulkaninfo >/dev/null 2>&1; then
        warn 'neither eglinfo nor vulkaninfo is installed; inspect compositor logs for renderer evidence'
      fi
      
      if $require_gpu && ! $renderer_evidence; then
        fail 'GPU prerequisites were required but no renderer tool produced evidence'
      elif $require_gpu && ! printf '%s\n' "$renderer_summary" | grep -Ei 'renderer|deviceName' | grep -Eivq 'llvmpipe|softpipe|swrast|pixman'; then
        fail 'GPU prerequisites were required but renderer tools reported only software candidates'
      elif $renderer_evidence && printf '%s\n' "$renderer_summary" | grep -Eiq 'llvmpipe|softpipe|swrast|pixman'; then
        warn 'renderer output includes a software implementation; correlate the relevant platform with compositor logs'
      fi
      
      if command -v swaymsg >/dev/null 2>&1 && [[ -n ${SWAYSOCK:-} ]]; then
        if swaymsg -t get_outputs -r >/dev/null 2>&1; then
          pass 'Sway IPC reports at least an output response'
        else
          fail 'Sway IPC is configured but get_outputs failed'
        fi
      elif command -v wayland-info >/dev/null 2>&1 && [[ -n ${WAYLAND_DISPLAY:-} ]]; then
        if wayland-info >/dev/null 2>&1; then
          pass 'wayland-info connected to the compositor'
        else
          fail 'wayland-info could not connect to the compositor'
        fi
      else
        warn 'no compositor connection probe was available'
      fi
      
      if command -v systemctl >/dev/null 2>&1; then
        for service_name in xdg-desktop-portal.service xdg-desktop-portal-wlr.service pipewire.service wireplumber.service; do
          service_state=$(systemctl --user is-active "$service_name" 2>/dev/null || true)
          printf '[info] %-35s %s\n' "$service_name" "${service_state:-unknown}"
        done
      fi
      
      for command_name in sway swaymsg wayland-info grim wayvnc auv; do
        if command -v "$command_name" >/dev/null 2>&1; then
          printf '[info] %-27s %s\n' "$command_name" "$(command -v "$command_name")"
        else
          printf '[info] %-27s not found\n' "$command_name"
        fi
      done
      
      if (( failures > 0 )); then
        printf '[fail] probe completed with %d required check(s) failing\n' "$failures"
        exit 1
      fi
      
      pass 'probe completed without required-check failures'
      
    • verify-auv-capture.sh 2.9 KB
      #!/usr/bin/env bash
      set -euo pipefail
      
      auv_bin=auv
      repeat=3
      store_root=${AUV_STORE_ROOT:-$PWD/.auv/wayland-validation}
      
      while (( $# > 0 )); do
        case "$1" in
          --auv)
            auv_bin=${2:?--auv requires a path}
            shift 2
            ;;
          --repeat)
            repeat=${2:?--repeat requires a count}
            shift 2
            ;;
          --store-root)
            store_root=${2:?--store-root requires a path}
            shift 2
            ;;
          -h|--help)
            printf 'usage: %s [--auv PATH] [--repeat N] [--store-root PATH]\n' "$0"
            exit 0
            ;;
          *)
            printf 'unknown argument: %s\n' "$1" >&2
            exit 2
            ;;
        esac
      done
      
      if [[ ! "$repeat" =~ ^[1-9][0-9]*$ ]]; then
        printf -- '--repeat must be a positive integer\n' >&2
        exit 2
      fi
      
      if [[ "$auv_bin" == */* ]]; then
        [[ -x "$auv_bin" ]] || {
          printf 'AUV executable is not executable: %s\n' "$auv_bin" >&2
          exit 1
        }
      elif ! command -v "$auv_bin" >/dev/null 2>&1; then
        printf 'AUV executable was not found: %s\n' "$auv_bin" >&2
        exit 1
      fi
      
      command -v python3 >/dev/null 2>&1 || {
        printf 'python3 is required to validate AUV JSON output\n' >&2
        exit 1
      }
      
      mkdir -p "$store_root"
      work_dir=$(mktemp -d "${TMPDIR:-/tmp}/auv-wayland-capture.XXXXXX")
      trap 'find "$work_dir" -type f -delete 2>/dev/null || true; rmdir "$work_dir" 2>/dev/null || true' EXIT
      
      for ((attempt = 1; attempt <= repeat; attempt++)); do
        result_file=$work_dir/result-$attempt.json
        "$auv_bin" invoke display.capture --store-root "$store_root" --json >"$result_file"
      
        python3 - "$result_file" "$attempt" <<'PY'
      import json
      import pathlib
      import sys
      
      result_path = pathlib.Path(sys.argv[1])
      attempt = sys.argv[2]
      result = json.loads(result_path.read_text())
      
      if result.get("status") != "completed":
          raise SystemExit(f"capture {attempt} did not complete: {result}")
      if result.get("command_id") != "display.capture":
          raise SystemExit(f"capture {attempt} returned unexpected command_id: {result.get('command_id')}")
      
      artifacts = [
          artifact
          for artifact in result.get("artifacts", [])
          if artifact.get("purpose") == "auv.driver.display_capture"
      ]
      if len(artifacts) != 1:
          raise SystemExit(f"capture {attempt} returned {len(artifacts)} display artifacts")
      
      artifact = artifacts[0]
      if artifact.get("content_type") != "image/png":
          raise SystemExit(f"capture {attempt} returned unexpected content type: {artifact.get('content_type')}")
      
      file_path = artifact.get("file_path")
      if not file_path:
          raise SystemExit(f"capture {attempt} did not expose a file_path")
      
      png = pathlib.Path(file_path)
      if not png.is_file():
          raise SystemExit(f"capture {attempt} artifact does not exist: {png}")
      with png.open("rb") as stream:
          signature = stream.read(8)
      if signature != b"\x89PNG\r\n\x1a\n":
          raise SystemExit(f"capture {attempt} artifact is not a PNG: {png}")
      
      print(f"capture {attempt}: run={result.get('run_id')} artifact={png}")
      PY
      done
      
      printf 'validated %d AUV display capture(s); store=%s\n' "$repeat" "$store_root"
      
    • with-wayland-session.sh 1.1 KB
      #!/usr/bin/env bash
      set -euo pipefail
      
      if (( $# == 0 )); then
        printf 'usage: %s <command> [args...]\n' "$0" >&2
        exit 2
      fi
      
      runtime_dir=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
      export XDG_RUNTIME_DIR=$runtime_dir
      
      wayland_socket_is_live() {
        local display=${WAYLAND_DISPLAY:-}
        [[ -n "$display" ]] || return 1
        if [[ "$display" = /* ]]; then
          [[ -S "$display" ]]
        else
          [[ -S "$XDG_RUNTIME_DIR/$display" ]]
        fi
      }
      
      manager_value() {
        local key=$1
        systemctl --user show-environment 2>/dev/null | sed -n "s/^${key}=//p" | tail -n 1
      }
      
      if ! wayland_socket_is_live && command -v systemctl >/dev/null 2>&1; then
        for key in WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP XDG_SESSION_TYPE; do
          current=$(manager_value "$key")
          if [[ -n "$current" ]]; then
            printf -v "$key" '%s' "$current"
            export "$key"
          fi
        done
      fi
      
      if ! wayland_socket_is_live; then
        printf 'no live Wayland socket was found in the shell or systemd user environment\n' >&2
        exit 1
      fi
      
      if [[ -z ${DBUS_SESSION_BUS_ADDRESS:-} && -S "$XDG_RUNTIME_DIR/bus" ]]; then
        export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
      fi
      
      exec "$@"
      
  • SKILL.md 6.7 KB
    ---
    name: auv-wayland-rendering
    description: Prepare, diagnose, and validate Linux GPU and Wayland environments for AUV rendering and capture through XDG Desktop Portal and PipeWire. Use when installing AUV desktop runtime dependencies, connecting from another machine over SSH, using an existing compositor or a headless wlroots/Sway session, checking hardware versus software rendering, running display.capture or Linux driver probes, exposing optional WayVNC safely, or troubleshooting blank frames and Wayland, portal, D-Bus, PipeWire, or session-environment failures.
    ---
    
    # AUV Wayland Rendering
    
    Build a reproducible Linux desktop session in which AUV can observe a Wayland
    display and persist capture artifacts. Treat environment validation as evidence,
    not as a change to AUV's public support claim.
    
    Keep ownership explicit: the compositor renders application clients; AUV
    requests frames through the portal and records them. Do not imply that AUV
    ships the compositor or a Linux overlay renderer.
    
    ## Read the relevant references
    
    - Read [setup.md](references/setup.md) before installing packages, creating a
      compositor session, or configuring remote viewing.
    - Read [troubleshooting.md](references/troubleshooting.md) when a probe or
      capture fails, the image is blank, or hardware rendering is uncertain.
    - Check AUV's current [support matrix](../../../docs/SUPPORT_MATRIX.md) before
      describing a result as supported. A successful live probe is
      `live-validated` evidence for the named environment only.
    
    ## Follow the workflow
    
    ### 1. Inspect before changing the host
    
    Run the read-only probe in the target shell:
    
    ```bash
    .agents/skills/auv-wayland-rendering/scripts/probe-wayland.sh
    ```
    
    Add `--require-gpu` to make missing GPU prerequisites fatal when hardware
    rendering is required. The flag does not replace compositor-log evidence.
    Record:
    
    - distribution and package manager;
    - existing compositor and whether the session is local, nested, or headless;
    - `WAYLAND_DISPLAY`, `XDG_RUNTIME_DIR`, and the session D-Bus;
    - accessible `/dev/dri/renderD*` nodes and the renderer reported by compositor
      logs or `eglinfo`/`vulkaninfo`;
    - ScreenCast portal and PipeWire readiness.
    
    Do not infer GPU rendering from the presence of a GPU or render node. Require a
    renderer log or renderer-tool result. Label `pixman`, `llvmpipe`, and similar
    paths as software rendering.
    
    ### 2. Choose the session shape
    
    Prefer the existing logged-in Wayland compositor when one is available. It
    already owns the seat, GPU, D-Bus activation environment, and user consent UI.
    
    Use headless Sway when the host has no desktop session or the test must be
    isolated. Start with wlroots renderer auto-selection. Force `pixman` only as an
    explicit software fallback; doing so gives deterministic rendering but does
    not validate the GPU path.
    
    Use a nested compositor when isolation is needed inside an existing Wayland
    desktop and an extra window is acceptable. Do not point AUV at one compositor
    while its portal backend is attached to another.
    
    ### 3. Install the smallest capability set
    
    Install or verify:
    
    - a Wayland compositor such as Sway;
    - `xdg-desktop-portal` and the backend for that compositor;
    - PipeWire and WirePlumber;
    - `wayland-info` and `grim` for independent diagnostics;
    - WayVNC only when a human needs to view the wlroots session remotely.
    
    Request authorization before changing system packages, user services, group
    membership, login linger, or firewall state. Prefer distribution packages.
    Use a source build or patch only after reproducing a version-specific defect
    and recording the exact upstream revision and reason.
    
    ### 4. Keep one coherent user session
    
    Run the compositor, portal backend, PipeWire, and AUV as the same unprivileged
    user. They must agree on `XDG_RUNTIME_DIR`, `WAYLAND_DISPLAY`, and the session
    bus. For a compositor started by a user service, import its generated variables
    into the systemd user manager and retrieve them in SSH shells.
    
    Use the helper to run a command with either the current environment or the
    variables published by the user manager:
    
    ```bash
    .agents/skills/auv-wayland-rendering/scripts/with-wayland-session.sh \
      auv invoke display.list --json
    ```
    
    Do not run the compositor or AUV as root to work around device permissions.
    Fix seat, logind, container-device, or render-node access instead.
    
    ### 5. Validate from dependencies to AUV artifacts
    
    Validate in this order so each failure has one clear owner:
    
    1. Confirm the Wayland socket and compositor output with `wayland-info` or the
       compositor IPC.
    2. Confirm PipeWire and the `org.freedesktop.portal.ScreenCast` interface.
    3. Run the AUV Linux driver probe:
    
       ```bash
       cargo run -p auv-driver-linux --example validate -- permissions displays
       ```
    
    4. Run repeated product-surface captures:
    
       ```bash
       .agents/skills/auv-wayland-rendering/scripts/verify-auv-capture.sh \
         --auv target/debug/auv --repeat 3
       ```
    
    5. Open at least one emitted PNG. Verify that it contains the expected client,
       has the expected dimensions, and is not a blank or stale frame.
    
    Repeated captures establish startup and first-frame reliability. To test
    freshness, visibly change a client between two capture passes and compare their
    artifacts; identical static frames do not prove that updates propagate.
    
    The verification script checks AUV's JSON result, artifact purpose, PNG
    signature, and file existence. It does not prove semantic correctness of the
    rendered application; visual inspection or an app-specific assertion remains
    separate.
    
    ### 6. Report the boundary precisely
    
    Report all of the following:
    
    - OS, compositor, portal backend, output name and dimensions;
    - renderer and whether it is hardware or software;
    - portal frame transport when known (`dma-buf`, shared memory, or fallback);
    - AUV revision and exact validation commands;
    - successful run IDs and artifact paths;
    - whether remote viewing was enabled and how it was secured;
    - known warnings, fallbacks, and missing capabilities.
    
    Do not turn compilation, a portal introspection result, or a single screenshot
    into a blanket Linux support claim.
    
    Keep GPU compositor rendering, GPU-buffer transport, and correct AUV artifact
    output as three separate claims. None implies the other two.
    
    ## Apply safety rules
    
    - Bind WayVNC to loopback and use an SSH tunnel unless authenticated transport
      was explicitly configured.
    - Do not expose an unauthenticated VNC port on `0.0.0.0`.
    - Do not overwrite an existing desktop's portal selection without checking its
      current backend and other consumers.
    - Do not persist services or enable login linger unless the user requested a
      durable session.
    - Keep capture and input evidence separate. A rendered frame does not prove
      that input was delivered or that an application completed an operation.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related