diff --git a/docs/UX-standards/launcher-standard.adoc b/docs/UX-standards/launcher-standard.adoc index 8e4e701..e23e918 100644 --- a/docs/UX-standards/launcher-standard.adoc +++ b/docs/UX-standards/launcher-standard.adoc @@ -430,6 +430,13 @@ feedback. [Desktop Entry] Type=Application Name=Application Name +# The Exec path MUST be the absolute path resolved at install time via +# the [resolution].desktop-tools-search ladder (see §Canonical location). +# The freedesktop spec does NOT expand environment variables in Exec= +# lines, so `--integ` is responsible for picking the host-correct path. +# The /var/mnt/eclipse/... value below is one possible resolution — on +# hosts using $HOME/developer/repos or $HOME/dev/repos the resolved path +# will differ. Exec=/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh "AppName" "/path/to/repo" "GUI_CMD" "TUI_CMD" "$HOME/.local/state/app/server.log" Terminal=true # keepopen needs a terminal for its loud banners and shell fallback Icon=/path/to/icon.png @@ -477,13 +484,28 @@ The single source of truth is: developer-ecosystem/standards/launcher/keepopen.sh -For desktop files to reference a stable absolute path, a symlink is -deployed at: +For desktop files (which need a stable absolute path) a symlink or copy +is deployed inside a `.desktop-tools/` directory whose location varies by +host. Consumers MUST resolve that location via the search ladder declared +in `launcher/launcher-standard.a2ml` `[resolution].desktop-tools-search`: - /var/mnt/eclipse/repos/.desktop-tools/keepopen.sh → ↑ +1. `$HP_DESKTOP_TOOLS` — explicit override +2. `$HP_ESTATE_ROOT/.desktop-tools` — estate-root convention +3. `$XDG_DATA_HOME/hyperpolymath/.desktop-tools` — XDG default +4. `/var/mnt/eclipse/repos/.desktop-tools` — legacy eclipse-mount layout +5. `$HOME/developer/repos/.desktop-tools` — alt: $HOME/developer/repos +6. `$HOME/dev/repos/.desktop-tools` — alt: $HOME/dev/repos + +First existing path wins. A reference shell implementation lives at +`launcher/resolve-desktop-tools.sh` (sourceable; provides +`hp_resolve_desktop_tools` and `hp_resolve_standard`). Downstream launchers +SHOULD use the reference impl rather than re-implementing the ladder. `launch-scaffolder` copies the same script into its baked-in standards -so regenerated launchers stay in sync. +so regenerated launchers stay in sync. The legacy single hard-coded path +(`/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh`) remains in the +a2ml as `deployed-symlink` for compatibility with pre-resolution +consumers; new code MUST use the ladder. === Calling convention diff --git a/launcher/README.adoc b/launcher/README.adoc index f2db14f..f3f033e 100644 --- a/launcher/README.adoc +++ b/launcher/README.adoc @@ -14,23 +14,33 @@ specification at `docs/UX-standards/launcher-standard.adoc`. == Consumer: launch-scaffolder -`launch-scaffolder` (Rust/SPARK workspace at -`/var/mnt/eclipse/repos/launch-scaffolder/`) is the minter / provisioner / -configurator for launchers across the estate. It consumes this standard by -reference, in this resolution order: +`launch-scaffolder` is the minter / provisioner / configurator for +launchers across the estate. Its workspace lives in the per-host +standards-monorepo checkout (e.g. `/var/mnt/eclipse/repos/launch-scaffolder/`, +`$HOME/developer/repos/launch-scaffolder/`, etc.; see the resolution ladder +below). It consumes this standard by reference, in this resolution order: . `--standard ` — explicit CLI argument . `$LAUNCH_SCAFFOLDER_STANDARD` — environment-variable override -. `/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml` — the - hard-coded literal fallback path to this file. This is the last-resort - default and is why this file's location in the standards monorepo is - itself part of the contract: moving or renaming this file without also - updating the fallback path in `launch-scaffolder` will break every - minting operation that relies on the default. - -A follow-up change in `launch-scaffolder` will drop its vendored copy under -`launch-scaffolder/standards/` and point its loader at this file via the -three-step resolution above. +. The `[resolution].standard-search` ladder declared in + `launcher-standard.a2ml`, consulted in order, first existing path wins: ++ +.. `$HP_ESTATE_ROOT/standards/launcher/launcher-standard.a2ml` +.. `$XDG_DATA_HOME/hyperpolymath/standards/launcher/launcher-standard.a2ml` +.. `/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml` +.. `$HOME/developer/repos/standards/launcher/launcher-standard.a2ml` +.. `$HOME/dev/repos/standards/launcher/launcher-standard.a2ml` + +The ladder replaces the previous single hard-coded +`/var/mnt/eclipse/repos/...` literal, which broke the standard on any host +that didn't happen to use the eclipse-mount layout. A bash reference +implementation lives at `launcher/resolve-desktop-tools.sh` +(`hp_resolve_standard` function); `launch-scaffolder` re-implements the +same ladder in Rust. + +A follow-up change in `launch-scaffolder` will drop its vendored copy +under `launch-scaffolder/standards/` and point its loader at the ladder +above. == Sync requirement diff --git a/launcher/launcher-standard.a2ml b/launcher/launcher-standard.a2ml index 233cc2f..4f6c8ba 100644 --- a/launcher/launcher-standard.a2ml +++ b/launcher/launcher-standard.a2ml @@ -24,6 +24,38 @@ compliance = [ "fallback-ladder-keepopen", ] +[resolution] +# Path-resolution ladders. Consumers (launch-scaffolder, downstream launchers, +# the integrity verifier) MUST consult these in order; the first existing path +# wins. This replaces single hard-coded /var/mnt/eclipse/... paths that broke +# the standard on hosts that don't use the eclipse-mount layout. +# +# .desktop files MUST embed the RESOLVED ABSOLUTE PATH at install time — +# the freedesktop spec does not expand environment variables in Exec= lines, +# so the launcher's `--integ` mode is responsible for performing the +# resolution and writing the literal path into the .desktop file. +# +# Reference implementation: launcher/resolve-desktop-tools.sh +desktop-tools-search = [ + "$HP_DESKTOP_TOOLS", # explicit override + "$HP_ESTATE_ROOT/.desktop-tools", # estate-root convention + "$XDG_DATA_HOME/hyperpolymath/.desktop-tools", # XDG default (XDG_DATA_HOME defaults to $HOME/.local/share) + "/var/mnt/eclipse/repos/.desktop-tools", # legacy eclipse-mount layout + "$HOME/developer/repos/.desktop-tools", # alt: $HOME/developer/repos + "$HOME/dev/repos/.desktop-tools", # alt: $HOME/dev/repos +] + +# Same ladder shape for locating the canonical launcher-standard.a2ml itself +# (consumed by launch-scaffolder when neither --standard nor +# $LAUNCH_SCAFFOLDER_STANDARD is set). +standard-search = [ + "$HP_ESTATE_ROOT/standards/launcher/launcher-standard.a2ml", + "$XDG_DATA_HOME/hyperpolymath/standards/launcher/launcher-standard.a2ml", + "/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml", + "$HOME/developer/repos/standards/launcher/launcher-standard.a2ml", + "$HOME/dev/repos/standards/launcher/launcher-standard.a2ml", +] + [fallback-ladder] # keepopen.sh wraps every primary desktop-file Exec line. It turns launcher # failures from invisible flashes into loud, labelled banners, and lands the @@ -32,7 +64,8 @@ compliance = [ # See launcher-standard.adoc §Fallback Ladder for the prose version. wrapper = "keepopen.sh" canonical-path = "developer-ecosystem/standards/launcher/keepopen.sh" -deployed-symlink = "/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh" +deployed-name = "keepopen.sh" # resolved via [resolution].desktop-tools-search +deployed-symlink = "/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh" # DEPRECATED: legacy literal, kept for pre-resolution consumers. New code MUST use [resolution]. calling-convention = "keepopen.sh APP_NAME REPO_DIR \"GUI_CMD\" \"TUI_CMD\" [LOG_FILE]" stages = [ { name = "gui", colour = "yellow", on-failure = "show-banner-then-try-tui" }, @@ -167,10 +200,14 @@ bat-fallback = true [integrity] # Per LM-LA-LIFECYCLE §LM/LA-INSTALL: after --integ completes, generate -# integrity hashes via an external tool. +# integrity hashes via an external tool. The tool is located via +# [resolution].desktop-tools-search + the tool-name below — new consumers +# MUST use that ladder; fallback-paths is kept only for pre-resolution +# consumers and is itself a legacy literal. verification-tool = "verify-desktop-integrity.sh" +tool-name = "verify-desktop-integrity.sh" # resolved via [resolution].desktop-tools-search fallback-paths = [ - "/var/mnt/eclipse/repos/.desktop-tools/verify-desktop-integrity.sh", + "/var/mnt/eclipse/repos/.desktop-tools/verify-desktop-integrity.sh", # DEPRECATED ] fatal-on-failure = false # missing verifier is a log line, not an error diff --git a/launcher/resolve-desktop-tools.sh b/launcher/resolve-desktop-tools.sh new file mode 100755 index 0000000..2dcfcd0 --- /dev/null +++ b/launcher/resolve-desktop-tools.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: PMPL-1.0-or-later +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# resolve-desktop-tools.sh — reference implementation of the path-resolution +# ladders declared in launcher/launcher-standard.a2ml §[resolution]. +# +# Downstream launchers SHOULD `source` this script and call +# `hp_resolve_desktop_tools` / `hp_resolve_standard` rather than rolling +# their own path-discovery logic — the standard's ladder will evolve, and +# centralising the implementation here keeps the estate aligned. +# +# Each function: +# - Echoes the first existing matching path to stdout, exits 0. +# - Echoes nothing and exits 1 if no candidate exists. The caller decides +# whether that is fatal (e.g. missing keepopen.sh wrapper) or recoverable +# (e.g. missing optional verify-desktop-integrity.sh). +# +# The ladders mirror [resolution].desktop-tools-search and +# [resolution].standard-search in the a2ml. They MUST stay in sync — see the +# CI gate referenced in launcher/README.adoc §Sync requirement. + +# --------------------------------------------------------------------------- +# hp_resolve_desktop_tools [TOOL_NAME] +# +# With no argument: echoes the first existing .desktop-tools/ directory. +# With an argument: echoes the first existing .desktop-tools/. +# --------------------------------------------------------------------------- +hp_resolve_desktop_tools() { + local tool="${1:-}" + local -a candidates=( + "${HP_DESKTOP_TOOLS:-}" + "${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/.desktop-tools}" + "${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/.desktop-tools" + "/var/mnt/eclipse/repos/.desktop-tools" + "${HOME}/developer/repos/.desktop-tools" + "${HOME}/dev/repos/.desktop-tools" + ) + + local candidate + for candidate in "${candidates[@]}"; do + [[ -z "${candidate}" ]] && continue + local target="${candidate}${tool:+/${tool}}" + if [[ -e "${target}" ]]; then + echo "${target}" + return 0 + fi + done + return 1 +} + +# --------------------------------------------------------------------------- +# hp_resolve_standard +# +# Echoes the first existing launcher-standard.a2ml found via the +# [resolution].standard-search ladder. Used by launch-scaffolder and any +# other consumer that needs the canonical contract file. +# --------------------------------------------------------------------------- +hp_resolve_standard() { + local -a candidates=( + "${LAUNCH_SCAFFOLDER_STANDARD:-}" + "${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/standards/launcher/launcher-standard.a2ml}" + "${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/standards/launcher/launcher-standard.a2ml" + "/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml" + "${HOME}/developer/repos/standards/launcher/launcher-standard.a2ml" + "${HOME}/dev/repos/standards/launcher/launcher-standard.a2ml" + ) + + local candidate + for candidate in "${candidates[@]}"; do + [[ -z "${candidate}" ]] && continue + if [[ -f "${candidate}" ]]; then + echo "${candidate}" + return 0 + fi + done + return 1 +} + +# When invoked directly (not sourced) act as a CLI that prints the resolved +# path for the requested tool, or all ladder candidates with --list. +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + case "${1:-}" in + --standard) + hp_resolve_standard + ;; + --list) + echo "desktop-tools-search:" + printf ' %s\n' \ + "${HP_DESKTOP_TOOLS:-}" \ + "${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/.desktop-tools}" \ + "${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/.desktop-tools" \ + "/var/mnt/eclipse/repos/.desktop-tools" \ + "${HOME}/developer/repos/.desktop-tools" \ + "${HOME}/dev/repos/.desktop-tools" + ;; + "") + hp_resolve_desktop_tools + ;; + *) + hp_resolve_desktop_tools "$1" + ;; + esac +fi