Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions docs/UX-standards/launcher-standard.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
38 changes: 24 additions & 14 deletions launcher/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>` — 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

Expand Down
43 changes: 40 additions & 3 deletions launcher/launcher-standard.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path> 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
Expand All @@ -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" },
Expand Down Expand Up @@ -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

Expand Down
104 changes: 104 additions & 0 deletions launcher/resolve-desktop-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: PMPL-1.0-or-later
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
# 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/<TOOL_NAME>.
# ---------------------------------------------------------------------------
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:-<unset>}" \
"${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
Loading