launch-scaffolder is a single Rust binary that generates, installs, and
maintains cross-platform desktop launcher scripts for any hyperpolymath
project (or any other project that adopts the hyperpolymath launcher
standard). It turns launcher scripts into generated artefacts, not
hand-edited files.
Inputs:
-
launcher-standard.a2mlβ a declarative description of what a compliant launcher looks like (modes, file paths, permissions, integrity requirements, per-platform behaviour). The canonical copy lives in thestandardsmonorepo at/var/mnt/eclipse/repos/developer-ecosystem/standards/launcher/launcher-standard.a2ml. At runtime the loader resolves in this order:-
--standard <file>CLI flag -
$LAUNCH_SCAFFOLDER_STANDARDenvironment variable -
The canonical path above
-
A baked-in copy compiled into the binary at build time (fallback only)
-
<app>.launcher.a2mlβ per-app config (name, display, URL, command, repo, icon, and an optional[exceptions]block).
-
-
Output:
-
<app>-launcher.shβ a fully spec-compliant cross-platform launcher (Linux / macOS / Windows-via-Git-Bash).
Without this tool, every hyperpolymath project maintains its own
hand-written ~600-line bash launcher script that has to be kept in sync
with the current launcher standard by hand. In practice, drift is
guaranteed. The launcher standard evolves; the launchers donβt. Fixing a
spec issue means editing 11+ files and hoping you caught them all.
With this tool:
-
The standard is a single file. Changing it is a one-line edit.
-
Every launcher is regenerated from the same standard + its own config.
-
Bulk realignment after a spec change is one command:
launch-scaffolder realign *.launcher.a2ml. -
Launchers become reproducible, auditable artefacts β not hand-edited drift.
-
"Not wasting tokens in future" β future AI sessions read one spec file, not 11 near-identical launcher scripts.
All four working subcommands, in the order youβre likely to use them:
# βββ mint ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Generate a launcher script from a config. Writes
# <config-parent>/<app-name>-launcher.sh by default.
launch-scaffolder mint examples/stapeln.launcher.fixture.a2ml
launch-scaffolder mint /path/to/burble.launcher.a2ml -o /tmp/out.sh
launch-scaffolder mint /path/to/burble.launcher.a2ml --stdout
# βββ realign βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Bulk re-mint: walk the estate (default = /var/mnt/eclipse/repos) and
# re-render every live `<app>.launcher.a2ml` against the current standard
# + template. Idempotent β unchanged scripts stay unchanged.
launch-scaffolder realign # walk estate root
launch-scaffolder realign --search-root ~/my-projects # narrow walk
launch-scaffolder realign /path/to/one.launcher.a2ml # explicit configs
launch-scaffolder realign --dry-run # preview only
launch-scaffolder realign --check # CI mode: exit 1 on any diff
launch-scaffolder realign --keep-going # don't stop on errors
# βββ provision βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Install (--integ) or uninstall (--disinteg) a launcher's desktop entry,
# icon, and launcher binary on the current system. Writes .desktop files
# directly from the Rust binary rather than going via the generated shell
# script. Bulk runs (--all) prompt before touching $HOME.
launch-scaffolder provision --integ /path/to/burble.launcher.a2ml
launch-scaffolder provision --disinteg /path/to/burble.launcher.a2ml
launch-scaffolder provision --integ --all # everything in the estate
launch-scaffolder provision --integ --all --no-confirm --force
launch-scaffolder provision --integ --all --dry-run # preview only
# βββ config ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Inspect or edit the `@a2ml-metadata` block embedded at the top of any
# generated launcher script.
launch-scaffolder config get ./stapeln-launcher.sh version
launch-scaffolder config get ./stapeln-launcher.sh standards-compliance
launch-scaffolder config validate ./stapeln-launcher.sh
launch-scaffolder config set ./stapeln-launcher.sh version 0.2.0
# NOTE: `config set` rewrites the generated script in place and warns
# that `realign` will overwrite the change. The durable fix is to edit
# the source <app>.launcher.a2ml and re-mint.Generated <app>-launcher.sh scripts carry their own --integ /
--disinteg arms (the original, pure-bash implementation). They now
also embed the absolute path of their source config as CONFIG_FILE=β¦β
and, on invocation, fast-path back to
launch-scaffolder provision --integ "$CONFIG_FILE" when the binary is
on PATH. If the binary isnβt present, the in-script shell fallback
runs instead. This means: the Rust binary is authoritative when
available, without making itself a hard dependency for integrated
launchers.
launch-scaffolder/
βββ Cargo.toml # Workspace root
βββ crates/
β βββ launcher-common/ # Shared library β all real logic
β β βββ src/
β β βββ lib.rs # Public API
β β βββ standard.rs # Parse launcher-standard.a2ml
β β β # + LauncherStandard::resolve (shared
β β β # 3-step precedence: flag β canonical
β β β # β baked fallback)
β β βββ config.rs # Parse <app>.launcher.a2ml
β β βββ template.rs # Render via Tera (embeds CONFIG_FILE)
β β βββ discovery.rs # Walk + prune + fixture-suffix filter
β β β # (shared by realign + provision)
β β βββ integration.rs # Native Rust .desktop writer,
β β β # icon/launcher install, gio,
β β β # update-desktop-database
β β βββ metadata_block.rs # Parser + in-place rewriter for the
β β β # embedded @a2ml-metadata block
β β βββ platform.rs # Linux/macOS/Windows dispatch (stub)
β β βββ integrity.rs # SHA-256 manifests (stub)
β β βββ exceptions.rs # Standard + config + exception merge (stub)
β βββ launcher/ # Thin CLI binary
β βββ src/
β βββ main.rs # clap dispatch
β βββ cmd_mint.rs # β working
β βββ cmd_realign.rs # β working
β βββ cmd_provision.rs # β working (native Rust; option b)
β βββ cmd_config.rs # β working
β βββ cmd_standard.rs # stub
βββ standards/
β βββ launcher-standard.a2ml # Canonical standard (baked into binary)
βββ templates/
β βββ launcher.sh.tera # Bash launcher template rendered by Tera
βββ examples/
β βββ README.md # Fixture-vs-live naming convention
β βββ stapeln.launcher.fixture.a2ml # Worked example (fixture suffix!)
βββ docs/
β βββ launcher-exceptions-2026-04-10.md
β βββ compliance-audit-2026-04-10.md
β βββ branch-protection-remediation-2026-04-10.md
β βββ ruleset-audit-2026-04-10/ # Audit artefacts (read-only record)
βββ tests/
βββ regression/ # Golden-file regression fixtures (planned)To distinguish test fixtures from live, estate-owned launcher configs, file-name suffixes carry the distinction β directory names do not:
| Purpose | File-name suffix | Picked up by estate walks? |
|---|---|---|
Live per-app config |
|
Yes |
Fixture / worked example |
|
No |
The discovery code in launch-scaffolder-common::discovery::is_live_config
enforces this rule. Fixture files can live anywhere β including inside
a consumer repoβs examples/ directory β without being swept up by
realign or provision --all. See examples/README.md for the full
contributor-facing version of the rule.
Per the hyperpolymath language policy (see standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc):
-
Rust is the preferred language for CLI tools β zero-dep binary, fast cold start, strong types, excellent ecosystem (clap, tera, sha2, anyhow).
-
"Rust" always means "Rust with SPARK integration as the default stance" β this tool is Rust-primary now, with SPARK/Ada hooks planned for the correctness-critical
integrity.rspath (called via Zig FFI per the hyperpolymath ABI/FFI standard).
-
A2ML is the hyperpolymath standard format for machine-readable config and metadata, and every launcher already carries an A2ML metadata block in its header. Using A2ML end-to-end means a launcher script can be parsed by this tool to extract its config and re-minted.
-
v0.1 uses TOML as the concrete syntax (A2ML is "TOML-like" per the standards
.claude/CLAUDE.md). v0.2 switches to proper A2ML once thea2ml-rsparser reaches feature parity.
Alpha, ~65% complete. Four of five subcommands fully wired end-to-end;
one remains a stub (standard). Last updated 2026-04-10 (phase
phase-4-config-inspector).
Implemented:
-
β Cargo workspace layout (two crates:
launch-scaffolder-common+launch-scaffolder) -
β
standardmodule β parseslauncher-standard.a2ml;LauncherStandard::resolvehosts the shared 3-step precedence (flag β canonical β baked) -
β
configmodule β parses<app>.launcher.a2ml(TOML) with runtime-kind validation -
β
templatemodule β Tera renderer with full context; embedsCONFIG_FILEfor in-script delegation -
β
discoverymodule β shared walk + prune + fixture-suffix filter -
β
integrationmodule β native Rust.desktopwriter, icon/launcher install, best-effortgio+update-desktop-database(Linux only; macOS/Windows returnIntegError::UnsupportedPlatform) -
β
metadata_blockmodule β hand-rolled parser and in-place rewriter for the embedded@a2ml-metadatablock -
β
templates/launcher.sh.teraβ parameterised overruntime_kind β {server-url, process, remote};--integ/--disintegfast-path tolaunch-scaffolder provisionwhen on PATH -
β
mintsubcommand β positional config,-o/--out,--stdout,--no-chmod -
β
realignsubcommand β estate walk with--search-rootoverride,--dry-run,--check(CI),--keep-going, walk-error tolerant -
β
provisionsubcommand β--integ/--disintegwith--all,--force,--no-confirm,--dry-run; bulk mode prompts before touching$HOME -
β
configsubcommand βget/set/validate;setpreserves column alignment in the embedded metadata block -
β 17 unit tests passing across both crates
-
β 7 launchers fully managed: aerie, burble, game-server-admin, nqc, panll, project-wharf, stapeln
-
β 5 declared exceptions documented in
docs/launcher-exceptions-2026-04-10.md -
β Fixture-vs-live file-naming convention documented and enforced
-
β All 7 managed launchers regenerated with template delegation arms (2026-04-10)
Remaining work (ordered by current priority in STATE.a2ml):
-
β Golden-file regression tests pinning mint output for the 7 managed launchers
-
β macOS integration backend in
launch-scaffolder-common::integration -
β SPARK integration hook for
integrity.rsvia Zig FFI -
β
standardsubcommand β still a scaffold stub -
β
platformmodule β still a stub (runtime dispatch handled inside the generated script today) -
β
integritymodule β still a stub (pending SHA-256 manifest generator) -
β
exceptionsmodule β still a stub (per-app override merge) -
β Cross-platform CI (Linux/macOS/Windows matrix)
-
β Migration of the 5 declared exceptions once the template grows custom-mode hooks
# Standard cargo workflow
cargo build # debug build
cargo build --release # optimized, stripped, single-file binary
cargo test # run tests
cargo run -- --help # invoke the binary
# Or via Justfile
just build
just test
just install # cargo install --path crates/launcherPMPL-1.0-or-later. See LICENSE.
All original hyperpolymath code is PMPL-1.0-or-later. Third-party
dependencies retain their original licenses; see cargo about output
(when added) for the full dependency license audit.
Jonathan D.A. Jewell (hyperpolymath)
j.d.a.jewell@open.ac.uk
Each consumer repo owns its own <app>.launcher.a2ml config at its
repository root. launch-scaffolder mint writes the generated
<app>-launcher.sh next to it. The pre-2026-04-10 pattern of pooling
launchers under /var/mnt/eclipse/repos/.desktop-tools/ is deprecated;
~/Desktop/Shortcuts/*.desktop files have been repointed at the new
per-repo paths.
Scaffolder-managed consumers (as of 2026-04-10):
-
stapeln β Visual Container Stack Designer (server-url, port 4010)
-
burble β WebRTC voice + control plane (server-url, port 4020)
-
aerie β network diagnostic suite (process)
-
game-server-admin β multi-game server orchestration (process)
-
nqc β NextGen Query Client, inside
nextgen-databases/(process) -
panll β panels framework (server-url, port 8000)
-
project-wharf β container/workload staging (process)
Declared exceptions (still hand-written; see
docs/launcher-exceptions-2026-04-10.md for reasoning and migration triggers):
-
hypatia,invariant-path,opsm,ambientops,idaptik
The bulk-realignment goal: once realign is implemented, one command will
re-mint every managed launcher in the estate against the current standard.