|
| 1 | +# SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | +# |
| 4 | +# launcher-standard.a2ml — canonical declarative description of a |
| 5 | +# hyperpolymath-compliant desktop launcher. |
| 6 | +# |
| 7 | +# This file is the source of truth for what a compliant launcher looks like. |
| 8 | +# It is baked into the launch-scaffolder binary at build time via |
| 9 | +# include_str!(), and can be overridden at runtime via --standard <file> or |
| 10 | +# $LAUNCH_SCAFFOLDER_STANDARD. |
| 11 | +# |
| 12 | +# See also: |
| 13 | +# - standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc |
| 14 | +# - standards/docs/UX-standards/launcher-standard.adoc (prose version) |
| 15 | +# - standards/docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc (install/uninstall) |
| 16 | + |
| 17 | +[spec] |
| 18 | +version = "0.1.0" |
| 19 | +date = "2026-04-10" |
| 20 | +compliance = [ |
| 21 | + "launcher-standard.adoc", |
| 22 | + "LM-LA-LIFECYCLE-STANDARD.adoc", |
| 23 | + "cross-platform-system-integration-modes", |
| 24 | +] |
| 25 | + |
| 26 | +[required-modes] |
| 27 | +# These are the modes every compliant launcher MUST implement. |
| 28 | +runtime = ["--start", "--stop", "--status", "--auto", "--browser"] |
| 29 | +integration = ["--integ", "--disinteg"] |
| 30 | +meta = ["--help"] |
| 31 | + |
| 32 | +[optional-modes] |
| 33 | +# Modes launchers MAY implement for extra functionality. |
| 34 | +developer = ["--debug", "--logs", "--tail"] |
| 35 | + |
| 36 | +[default-mode] |
| 37 | +# Mode selected when the launcher is invoked with no arguments. |
| 38 | +mode = "--auto" |
| 39 | + |
| 40 | +[runtime] |
| 41 | +# Required runtime behaviour. |
| 42 | +background = "nohup" |
| 43 | +pid-file-pattern = "/tmp/{app-name}-server.pid" |
| 44 | +log-file-pattern = "/tmp/{app-name}-server.log" |
| 45 | +wait-for-url-timeout-seconds = 15 |
| 46 | +startup-command-search = [ |
| 47 | + "{repo-dir}/scripts/run.sh", |
| 48 | + "{repo-dir}/dev.sh", |
| 49 | +] |
| 50 | + |
| 51 | +[error-visibility] |
| 52 | +# When the launcher runs in a GUI context (no tty + DISPLAY/WAYLAND_DISPLAY set), |
| 53 | +# errors must be visible to the user via a GUI dialog, not only to stderr. |
| 54 | +gui-dialog-chain = ["kdialog", "zenity", "notify-send", "xmessage"] |
| 55 | +always-also-to-stderr = true |
| 56 | + |
| 57 | +[integration.linux] |
| 58 | +apps-dir = "$HOME/.local/share/applications" |
| 59 | +icon-dir = "$HOME/.local/share/icons/hicolor/256x256/apps" |
| 60 | +desktop-shortcut-dir = "$HOME/Desktop" |
| 61 | +bin-dir = "$HOME/.local/bin" |
| 62 | +desktop-file-permissions = 444 |
| 63 | +# If no custom icon is available, fall back to this freedesktop named icon. |
| 64 | +icon-fallback = "package-x-generic" |
| 65 | +refresh-command = "update-desktop-database" |
| 66 | +trust-command = "gio set {file} metadata::trusted true" |
| 67 | + |
| 68 | +[integration.macos] |
| 69 | +apps-dir = "$HOME/Applications" |
| 70 | +desktop-shortcut-dir = "$HOME/Desktop" |
| 71 | +bin-dir = "$HOME/.local/bin" |
| 72 | +bundle-pattern = "{app-display}.app" |
| 73 | +shortcut-pattern = "{app-display}.command" |
| 74 | + |
| 75 | +[integration.windows] |
| 76 | +start-menu-dir = "$APPDATA/Microsoft/Windows/Start Menu/Programs" |
| 77 | +desktop-shortcut-dir = "$HOME/Desktop" |
| 78 | +bin-dir = "$HOME/.local/bin" |
| 79 | +shortcut-pattern = "{app-display}.lnk" |
| 80 | +# Fallback when PowerShell is not reachable under Git Bash / WSL. |
| 81 | +bat-fallback = true |
| 82 | + |
| 83 | +[integrity] |
| 84 | +# Per LM-LA-LIFECYCLE §LM/LA-INSTALL: after --integ completes, generate |
| 85 | +# integrity hashes via an external tool. |
| 86 | +verification-tool = "verify-desktop-integrity.sh" |
| 87 | +fallback-paths = [ |
| 88 | + "/var/mnt/eclipse/repos/.desktop-tools/verify-desktop-integrity.sh", |
| 89 | +] |
| 90 | +fatal-on-failure = false # missing verifier is a log line, not an error |
| 91 | + |
| 92 | +[disinteg] |
| 93 | +# What --disinteg removes and what it deliberately leaves alone. |
| 94 | +remove = [ |
| 95 | + "desktop-file", |
| 96 | + "desktop-shortcut", |
| 97 | + "icon", |
| 98 | + "launcher-binary", |
| 99 | + "start-menu-entry", |
| 100 | + "bat-fallback-shortcuts", |
| 101 | + "pid-file", |
| 102 | +] |
| 103 | +preserve = [ |
| 104 | + "$HOME/.config/{app-name}/", |
| 105 | + "/tmp/{app-name}-server.log", |
| 106 | + "{repo-dir}", |
| 107 | +] |
| 108 | + |
| 109 | +[soft-attach] |
| 110 | +# Optional integrations — called if present, silently skipped if absent. |
| 111 | +tools = [ |
| 112 | + { name = "feedback-o-tron", event-on-failure = "launcher:start_failed" }, |
| 113 | + { name = "hypatia", command = "hypatia diagnose --app {app-name} --log {log-file}" }, |
| 114 | + { name = "panic-attack", command = "panic-attack assail {repo-dir}" }, |
| 115 | +] |
| 116 | + |
| 117 | +[a2ml-metadata-block] |
| 118 | +# Every generated launcher must carry this metadata block in its header so it |
| 119 | +# can be re-parsed by `launch-scaffolder config` and `realign`. |
| 120 | +required-fields = [ |
| 121 | + "id", |
| 122 | + "type", |
| 123 | + "version", |
| 124 | + "app-name", |
| 125 | + "app-display", |
| 126 | + "app-url", |
| 127 | + "standards-compliance", |
| 128 | + "modes", |
| 129 | + "platforms", |
| 130 | + "lifecycle-phases-covered", |
| 131 | + "lifecycle-phases-deferred", |
| 132 | +] |
0 commit comments