Skip to content

Commit 378e8ae

Browse files
hyperpolymathclaude
andcommitted
fix(launcher-standard): replace hard-coded /var/mnt/eclipse/... with a search-ladder
The launcher standard previously hard-coded `/var/mnt/eclipse/repos/...` as the sole literal-fallback path for both `launch-scaffolder` finding `launcher-standard.a2ml` and `.desktop` files finding `keepopen.sh` / `verify-desktop-integrity.sh`. On any host that does not happen to use the eclipse-mount layout (e.g. `$HOME/developer/repos`, `$HOME/dev/repos`), the documented default fallback simply pointed at a non-existent path. This blocked anyone outside that one layout from using the standard's defaults — they had to set `--standard <path>` / `$LAUNCH_SCAFFOLDER_STANDARD` on every invocation, and could not rely on the documented `.desktop` templates at all. Changes: - Add `[resolution]` table to `launcher-standard.a2ml` with two search ladders: `desktop-tools-search` (finds `.desktop-tools/`) and `standard-search` (finds `launcher-standard.a2ml`). Each ladder is ordered: explicit env-var override → estate-root → XDG default → three known absolute layouts (eclipse, `$HOME/developer/repos`, `$HOME/dev/repos`). First existing match wins. - Add `launcher/resolve-desktop-tools.sh`: bash reference implementation exposing `hp_resolve_desktop_tools` and `hp_resolve_standard` as sourceable functions, plus CLI behaviour (`--list`, `--standard`, `<tool-name>`). Downstream launchers SHOULD use this rather than re-implement the ladder. - Mark the existing `deployed-symlink` and `fallback-paths` literals DEPRECATED but keep them in place: pre-resolution consumers (`launch-scaffolder` until it's rebuilt against this spec) still read those keys, so removing them would break them. New code MUST use `[resolution]`. - Update `docs/UX-standards/launcher-standard.adoc` §Canonical location to describe the ladder and reference `resolve-desktop-tools.sh`. Also update the `.desktop` Exec= example with an explicit comment that the path is resolved at install time (because the freedesktop spec does not expand env vars in Exec=). - Update `launcher/README.adoc` consumer-resolution-order section to describe the ladder instead of the obsolete single literal. Spec `[spec].version` intentionally NOT bumped here — PR #170 also bumps it (to 0.3.0) and the two PRs are independent off main; whichever merges second will need a trivial conflict-resolve to land both bumps. Verified on this host: $ launcher/resolve-desktop-tools.sh --standard /home/hyperpolymath/developer/repos/standards/launcher/launcher-standard.a2ml (previously: nothing — the only candidate path was the eclipse one) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 431adbb commit 378e8ae

4 files changed

Lines changed: 194 additions & 21 deletions

File tree

docs/UX-standards/launcher-standard.adoc

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,13 @@ feedback.
316316
[Desktop Entry]
317317
Type=Application
318318
Name=Application Name
319+
# The Exec path MUST be the absolute path resolved at install time via
320+
# the [resolution].desktop-tools-search ladder (see §Canonical location).
321+
# The freedesktop spec does NOT expand environment variables in Exec=
322+
# lines, so `--integ` is responsible for picking the host-correct path.
323+
# The /var/mnt/eclipse/... value below is one possible resolution — on
324+
# hosts using $HOME/developer/repos or $HOME/dev/repos the resolved path
325+
# will differ.
319326
Exec=/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh "AppName" "/path/to/repo" "GUI_CMD" "TUI_CMD" "/tmp/app.log"
320327
Terminal=true # keepopen needs a terminal for its loud banners and shell fallback
321328
Icon=/path/to/icon.png
@@ -363,13 +370,28 @@ The single source of truth is:
363370

364371
developer-ecosystem/standards/launcher/keepopen.sh
365372

366-
For desktop files to reference a stable absolute path, a symlink is
367-
deployed at:
373+
For desktop files (which need a stable absolute path) a symlink or copy
374+
is deployed inside a `.desktop-tools/` directory whose location varies by
375+
host. Consumers MUST resolve that location via the search ladder declared
376+
in `launcher/launcher-standard.a2ml` `[resolution].desktop-tools-search`:
368377

369-
/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh → ↑
378+
1. `$HP_DESKTOP_TOOLS` — explicit override
379+
2. `$HP_ESTATE_ROOT/.desktop-tools` — estate-root convention
380+
3. `$XDG_DATA_HOME/hyperpolymath/.desktop-tools` — XDG default
381+
4. `/var/mnt/eclipse/repos/.desktop-tools` — legacy eclipse-mount layout
382+
5. `$HOME/developer/repos/.desktop-tools` — alt: $HOME/developer/repos
383+
6. `$HOME/dev/repos/.desktop-tools` — alt: $HOME/dev/repos
384+
385+
First existing path wins. A reference shell implementation lives at
386+
`launcher/resolve-desktop-tools.sh` (sourceable; provides
387+
`hp_resolve_desktop_tools` and `hp_resolve_standard`). Downstream launchers
388+
SHOULD use the reference impl rather than re-implementing the ladder.
370389

371390
`launch-scaffolder` copies the same script into its baked-in standards
372-
so regenerated launchers stay in sync.
391+
so regenerated launchers stay in sync. The legacy single hard-coded path
392+
(`/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh`) remains in the
393+
a2ml as `deployed-symlink` for compatibility with pre-resolution
394+
consumers; new code MUST use the ladder.
373395

374396
=== Calling convention
375397

launcher/README.adoc

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,33 @@ specification at `docs/UX-standards/launcher-standard.adoc`.
1414

1515
== Consumer: launch-scaffolder
1616

17-
`launch-scaffolder` (Rust/SPARK workspace at
18-
`/var/mnt/eclipse/repos/launch-scaffolder/`) is the minter / provisioner /
19-
configurator for launchers across the estate. It consumes this standard by
20-
reference, in this resolution order:
17+
`launch-scaffolder` is the minter / provisioner / configurator for
18+
launchers across the estate. Its workspace lives in the per-host
19+
standards-monorepo checkout (e.g. `/var/mnt/eclipse/repos/launch-scaffolder/`,
20+
`$HOME/developer/repos/launch-scaffolder/`, etc.; see the resolution ladder
21+
below). It consumes this standard by reference, in this resolution order:
2122

2223
. `--standard <path>` — explicit CLI argument
2324
. `$LAUNCH_SCAFFOLDER_STANDARD` — environment-variable override
24-
. `/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml` — the
25-
hard-coded literal fallback path to this file. This is the last-resort
26-
default and is why this file's location in the standards monorepo is
27-
itself part of the contract: moving or renaming this file without also
28-
updating the fallback path in `launch-scaffolder` will break every
29-
minting operation that relies on the default.
30-
31-
A follow-up change in `launch-scaffolder` will drop its vendored copy under
32-
`launch-scaffolder/standards/` and point its loader at this file via the
33-
three-step resolution above.
25+
. The `[resolution].standard-search` ladder declared in
26+
`launcher-standard.a2ml`, consulted in order, first existing path wins:
27+
+
28+
.. `$HP_ESTATE_ROOT/standards/launcher/launcher-standard.a2ml`
29+
.. `$XDG_DATA_HOME/hyperpolymath/standards/launcher/launcher-standard.a2ml`
30+
.. `/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml`
31+
.. `$HOME/developer/repos/standards/launcher/launcher-standard.a2ml`
32+
.. `$HOME/dev/repos/standards/launcher/launcher-standard.a2ml`
33+
34+
The ladder replaces the previous single hard-coded
35+
`/var/mnt/eclipse/repos/...` literal, which broke the standard on any host
36+
that didn't happen to use the eclipse-mount layout. A bash reference
37+
implementation lives at `launcher/resolve-desktop-tools.sh`
38+
(`hp_resolve_standard` function); `launch-scaffolder` re-implements the
39+
same ladder in Rust.
40+
41+
A follow-up change in `launch-scaffolder` will drop its vendored copy
42+
under `launch-scaffolder/standards/` and point its loader at the ladder
43+
above.
3444

3545
== Sync requirement
3646

launcher/launcher-standard.a2ml

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,38 @@ compliance = [
2424
"fallback-ladder-keepopen",
2525
]
2626

27+
[resolution]
28+
# Path-resolution ladders. Consumers (launch-scaffolder, downstream launchers,
29+
# the integrity verifier) MUST consult these in order; the first existing path
30+
# wins. This replaces single hard-coded /var/mnt/eclipse/... paths that broke
31+
# the standard on hosts that don't use the eclipse-mount layout.
32+
#
33+
# .desktop files MUST embed the RESOLVED ABSOLUTE PATH at install time —
34+
# the freedesktop spec does not expand environment variables in Exec= lines,
35+
# so the launcher's `--integ` mode is responsible for performing the
36+
# resolution and writing the literal path into the .desktop file.
37+
#
38+
# Reference implementation: launcher/resolve-desktop-tools.sh
39+
desktop-tools-search = [
40+
"$HP_DESKTOP_TOOLS", # explicit override
41+
"$HP_ESTATE_ROOT/.desktop-tools", # estate-root convention
42+
"$XDG_DATA_HOME/hyperpolymath/.desktop-tools", # XDG default (XDG_DATA_HOME defaults to $HOME/.local/share)
43+
"/var/mnt/eclipse/repos/.desktop-tools", # legacy eclipse-mount layout
44+
"$HOME/developer/repos/.desktop-tools", # alt: $HOME/developer/repos
45+
"$HOME/dev/repos/.desktop-tools", # alt: $HOME/dev/repos
46+
]
47+
48+
# Same ladder shape for locating the canonical launcher-standard.a2ml itself
49+
# (consumed by launch-scaffolder when neither --standard <path> nor
50+
# $LAUNCH_SCAFFOLDER_STANDARD is set).
51+
standard-search = [
52+
"$HP_ESTATE_ROOT/standards/launcher/launcher-standard.a2ml",
53+
"$XDG_DATA_HOME/hyperpolymath/standards/launcher/launcher-standard.a2ml",
54+
"/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml",
55+
"$HOME/developer/repos/standards/launcher/launcher-standard.a2ml",
56+
"$HOME/dev/repos/standards/launcher/launcher-standard.a2ml",
57+
]
58+
2759
[fallback-ladder]
2860
# keepopen.sh wraps every primary desktop-file Exec line. It turns launcher
2961
# failures from invisible flashes into loud, labelled banners, and lands the
@@ -32,7 +64,8 @@ compliance = [
3264
# See launcher-standard.adoc §Fallback Ladder for the prose version.
3365
wrapper = "keepopen.sh"
3466
canonical-path = "developer-ecosystem/standards/launcher/keepopen.sh"
35-
deployed-symlink = "/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh"
67+
deployed-name = "keepopen.sh" # resolved via [resolution].desktop-tools-search
68+
deployed-symlink = "/var/mnt/eclipse/repos/.desktop-tools/keepopen.sh" # DEPRECATED: legacy literal, kept for pre-resolution consumers. New code MUST use [resolution].
3669
calling-convention = "keepopen.sh APP_NAME REPO_DIR \"GUI_CMD\" \"TUI_CMD\" [LOG_FILE]"
3770
stages = [
3871
{ name = "gui", colour = "yellow", on-failure = "show-banner-then-try-tui" },
@@ -101,10 +134,14 @@ bat-fallback = true
101134

102135
[integrity]
103136
# Per LM-LA-LIFECYCLE §LM/LA-INSTALL: after --integ completes, generate
104-
# integrity hashes via an external tool.
137+
# integrity hashes via an external tool. The tool is located via
138+
# [resolution].desktop-tools-search + the tool-name below — new consumers
139+
# MUST use that ladder; fallback-paths is kept only for pre-resolution
140+
# consumers and is itself a legacy literal.
105141
verification-tool = "verify-desktop-integrity.sh"
142+
tool-name = "verify-desktop-integrity.sh" # resolved via [resolution].desktop-tools-search
106143
fallback-paths = [
107-
"/var/mnt/eclipse/repos/.desktop-tools/verify-desktop-integrity.sh",
144+
"/var/mnt/eclipse/repos/.desktop-tools/verify-desktop-integrity.sh", # DEPRECATED
108145
]
109146
fatal-on-failure = false # missing verifier is a log line, not an error
110147

launcher/resolve-desktop-tools.sh

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: PMPL-1.0-or-later
3+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# resolve-desktop-tools.sh — reference implementation of the path-resolution
6+
# ladders declared in launcher/launcher-standard.a2ml §[resolution].
7+
#
8+
# Downstream launchers SHOULD `source` this script and call
9+
# `hp_resolve_desktop_tools` / `hp_resolve_standard` rather than rolling
10+
# their own path-discovery logic — the standard's ladder will evolve, and
11+
# centralising the implementation here keeps the estate aligned.
12+
#
13+
# Each function:
14+
# - Echoes the first existing matching path to stdout, exits 0.
15+
# - Echoes nothing and exits 1 if no candidate exists. The caller decides
16+
# whether that is fatal (e.g. missing keepopen.sh wrapper) or recoverable
17+
# (e.g. missing optional verify-desktop-integrity.sh).
18+
#
19+
# The ladders mirror [resolution].desktop-tools-search and
20+
# [resolution].standard-search in the a2ml. They MUST stay in sync — see the
21+
# CI gate referenced in launcher/README.adoc §Sync requirement.
22+
23+
# ---------------------------------------------------------------------------
24+
# hp_resolve_desktop_tools [TOOL_NAME]
25+
#
26+
# With no argument: echoes the first existing .desktop-tools/ directory.
27+
# With an argument: echoes the first existing .desktop-tools/<TOOL_NAME>.
28+
# ---------------------------------------------------------------------------
29+
hp_resolve_desktop_tools() {
30+
local tool="${1:-}"
31+
local -a candidates=(
32+
"${HP_DESKTOP_TOOLS:-}"
33+
"${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/.desktop-tools}"
34+
"${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/.desktop-tools"
35+
"/var/mnt/eclipse/repos/.desktop-tools"
36+
"${HOME}/developer/repos/.desktop-tools"
37+
"${HOME}/dev/repos/.desktop-tools"
38+
)
39+
40+
local candidate
41+
for candidate in "${candidates[@]}"; do
42+
[[ -z "${candidate}" ]] && continue
43+
local target="${candidate}${tool:+/${tool}}"
44+
if [[ -e "${target}" ]]; then
45+
echo "${target}"
46+
return 0
47+
fi
48+
done
49+
return 1
50+
}
51+
52+
# ---------------------------------------------------------------------------
53+
# hp_resolve_standard
54+
#
55+
# Echoes the first existing launcher-standard.a2ml found via the
56+
# [resolution].standard-search ladder. Used by launch-scaffolder and any
57+
# other consumer that needs the canonical contract file.
58+
# ---------------------------------------------------------------------------
59+
hp_resolve_standard() {
60+
local -a candidates=(
61+
"${LAUNCH_SCAFFOLDER_STANDARD:-}"
62+
"${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/standards/launcher/launcher-standard.a2ml}"
63+
"${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/standards/launcher/launcher-standard.a2ml"
64+
"/var/mnt/eclipse/repos/standards/launcher/launcher-standard.a2ml"
65+
"${HOME}/developer/repos/standards/launcher/launcher-standard.a2ml"
66+
"${HOME}/dev/repos/standards/launcher/launcher-standard.a2ml"
67+
)
68+
69+
local candidate
70+
for candidate in "${candidates[@]}"; do
71+
[[ -z "${candidate}" ]] && continue
72+
if [[ -f "${candidate}" ]]; then
73+
echo "${candidate}"
74+
return 0
75+
fi
76+
done
77+
return 1
78+
}
79+
80+
# When invoked directly (not sourced) act as a CLI that prints the resolved
81+
# path for the requested tool, or all ladder candidates with --list.
82+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
83+
case "${1:-}" in
84+
--standard)
85+
hp_resolve_standard
86+
;;
87+
--list)
88+
echo "desktop-tools-search:"
89+
printf ' %s\n' \
90+
"${HP_DESKTOP_TOOLS:-<unset>}" \
91+
"${HP_ESTATE_ROOT:+${HP_ESTATE_ROOT}/.desktop-tools}" \
92+
"${XDG_DATA_HOME:-${HOME}/.local/share}/hyperpolymath/.desktop-tools" \
93+
"/var/mnt/eclipse/repos/.desktop-tools" \
94+
"${HOME}/developer/repos/.desktop-tools" \
95+
"${HOME}/dev/repos/.desktop-tools"
96+
;;
97+
"")
98+
hp_resolve_desktop_tools
99+
;;
100+
*)
101+
hp_resolve_desktop_tools "$1"
102+
;;
103+
esac
104+
fi

0 commit comments

Comments
 (0)