|
| 1 | +# SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +# Launcher Standard Review — 2026-05-26 |
| 3 | +# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) |
| 4 | +# |
| 5 | +# Machine-readable manifest of the launcher-standard review campaign run |
| 6 | +# on 2026-05-26. Prose companion: launcher-standard-review-2026-05-26.adoc. |
| 7 | +# |
| 8 | +# Purpose: give tooling (PR-batching bots, change-impact analyzers, |
| 9 | +# launch-scaffolder regenerators) a single document to enumerate the |
| 10 | +# 8-PR set, their classes, their file footprints, and their merge |
| 11 | +# coordination constraints, without re-parsing 8 separate PR bodies. |
| 12 | +# |
| 13 | +# When all 8 PRs have merged this file becomes a historical record; |
| 14 | +# its `status = "open"` fields can be flipped to "merged" with the |
| 15 | +# merge SHA recorded. |
| 16 | + |
| 17 | +@abstract: |
| 18 | +8-PR campaign reviewing and hardening the launcher standard. 3 |
| 19 | +contradictions removed, 1 blocker fixed (hard-coded eclipse-mount |
| 20 | +literal replaced with a search ladder), 1 security issue closed |
| 21 | +(`/tmp/` symlink-attack on PID/log), 1 forcing-function CI gate added, |
| 22 | +2 new required behaviours specified (`--version`, NO_COLOR), 2 new |
| 23 | +contract areas given reference implementations (soft-attach + |
| 24 | +gui-error). All PRs independent off main, GPG-signed. |
| 25 | +@end |
| 26 | + |
| 27 | +[campaign] |
| 28 | +id = "launcher-standard-review-2026-05-26" |
| 29 | +filed-date = "2026-05-26" |
| 30 | +scope = "launcher/ + docs/UX-standards/launcher-standard.adoc + LM-LA-LIFECYCLE-STANDARD.adoc" |
| 31 | +pr-count = 8 |
| 32 | +status = "all-open-awaiting-owner-merge" |
| 33 | +prose-companion = "docs/audits/launcher-standard-review-2026-05-26.adoc" |
| 34 | + |
| 35 | +[[pr]] |
| 36 | +number = 170 |
| 37 | +branch = "fix/launcher-standard-contradictions" |
| 38 | +title = "fix(launcher-standard): resolve 3 cross-doc contradictions" |
| 39 | +class = "contradictions" |
| 40 | +files = [ |
| 41 | + "launcher/launcher-standard.a2ml", |
| 42 | + "docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc", |
| 43 | +] |
| 44 | +touches-spec-version = true |
| 45 | +spec-version-target = "0.3.0" |
| 46 | +addresses = [ |
| 47 | + "browser-alias-listed-as-required-mode", |
| 48 | + "integ-disinteg-supersedence-unmirrored", |
| 49 | + "sudo-vs-no-elevated-privileges-conflict", |
| 50 | +] |
| 51 | +status = "open" |
| 52 | + |
| 53 | +[[pr]] |
| 54 | +number = 171 |
| 55 | +branch = "fix/launcher-standard-path-resolution" |
| 56 | +title = "fix(launcher-standard): replace hard-coded /var/mnt/eclipse/... with a search-ladder" |
| 57 | +class = "blocker-path-resolution" |
| 58 | +files = [ |
| 59 | + "launcher/launcher-standard.a2ml", |
| 60 | + "docs/UX-standards/launcher-standard.adoc", |
| 61 | + "launcher/README.adoc", |
| 62 | + "launcher/resolve-desktop-tools.sh", # new |
| 63 | +] |
| 64 | +touches-spec-version = false |
| 65 | +addresses = [ |
| 66 | + "var-mnt-eclipse-literal-broken-on-other-hosts", |
| 67 | + "no-bash-reference-impl-for-resolution", |
| 68 | +] |
| 69 | +new-files = ["launcher/resolve-desktop-tools.sh"] |
| 70 | +status = "open" |
| 71 | + |
| 72 | +[[pr]] |
| 73 | +number = 172 |
| 74 | +branch = "ci/launcher-standard-lockstep" |
| 75 | +title = "ci: add launcher-standard prose↔a2ml lock-step gate" |
| 76 | +class = "ci-forcing-function" |
| 77 | +files = [ |
| 78 | + ".github/workflows/launcher-standard-lockstep.yml", # new |
| 79 | +] |
| 80 | +touches-spec-version = false |
| 81 | +addresses = [ |
| 82 | + "prose-a2ml-drift-not-enforced", |
| 83 | +] |
| 84 | +new-files = [".github/workflows/launcher-standard-lockstep.yml"] |
| 85 | +fires-on = [ |
| 86 | + "launcher/launcher-standard.a2ml", |
| 87 | + "docs/UX-standards/launcher-standard.adoc", |
| 88 | +] |
| 89 | +status = "open" |
| 90 | + |
| 91 | +[[pr]] |
| 92 | +number = 173 |
| 93 | +branch = "feat/launcher-version-mode" |
| 94 | +title = "feat(launcher-standard): require --version mode with machine-greppable format" |
| 95 | +class = "missing-mode" |
| 96 | +files = [ |
| 97 | + "launcher/launcher-standard.a2ml", |
| 98 | + "docs/UX-standards/launcher-standard.adoc", |
| 99 | +] |
| 100 | +touches-spec-version = false |
| 101 | +addresses = [ |
| 102 | + "no-version-mode-required", |
| 103 | +] |
| 104 | +required-format = "{app-name} {version} ({build-sha-short}) [{platform}]" |
| 105 | +status = "open" |
| 106 | + |
| 107 | +[[pr]] |
| 108 | +number = 175 |
| 109 | +branch = "fix/launcher-xdg-pid-log-paths" |
| 110 | +title = "fix(launcher-standard): move PID/log to XDG dirs (security: symlink-attack hardening)" |
| 111 | +class = "security-xdg-hardening" |
| 112 | +files = [ |
| 113 | + "launcher/launcher-standard.a2ml", |
| 114 | + "docs/UX-standards/launcher-standard.adoc", |
| 115 | +] |
| 116 | +touches-spec-version = true |
| 117 | +spec-version-target = "0.4.0" # if landing after #170; else 0.3.0 and #170 bumps to 0.4.0 |
| 118 | +addresses = [ |
| 119 | + "tmp-pid-symlink-attack-vector", |
| 120 | + "tmp-log-symlink-attack-vector", |
| 121 | +] |
| 122 | +recovery-note = "PR #175 was re-pushed force-with-lease after a parallel-session amend incident contaminated the original commit with 327 lines of unrelated WIP. The final pushed commit (signed GPG) contains only the intended 2-file XDG change." |
| 123 | +status = "open" |
| 124 | + |
| 125 | +[[pr]] |
| 126 | +number = 176 |
| 127 | +branch = "fix/keepopen-honour-no-color" |
| 128 | +title = "fix(keepopen): honour NO_COLOR and auto-strip ANSI for non-TTY stdout" |
| 129 | +class = "ux-no-color" |
| 130 | +files = [ |
| 131 | + "launcher/keepopen.sh", |
| 132 | +] |
| 133 | +touches-spec-version = false |
| 134 | +addresses = [ |
| 135 | + "keepopen-emits-ansi-when-piped", |
| 136 | + "no-color-env-not-honoured", |
| 137 | +] |
| 138 | +triggers-lockstep-gate = false # not in the 2-file lock-step group |
| 139 | +status = "open" |
| 140 | + |
| 141 | +[[pr]] |
| 142 | +number = 177 |
| 143 | +branch = "feat/launcher-browser-and-wait-timing" |
| 144 | +title = "feat(launcher-standard): cross-platform browser launch + env-overridable wait timing" |
| 145 | +class = "cross-platform-and-tunable" |
| 146 | +files = [ |
| 147 | + "launcher/launcher-standard.a2ml", |
| 148 | + "docs/UX-standards/launcher-standard.adoc", |
| 149 | +] |
| 150 | +touches-spec-version = false |
| 151 | +addresses = [ |
| 152 | + "open-browser-linux-only-despite-stated-cross-platform", |
| 153 | + "no-BROWSER-env-honoured", |
| 154 | + "wait-for-url-timeout-constant-unread-by-template", |
| 155 | + "no-per-request-curl-timeout", |
| 156 | +] |
| 157 | +new-a2ml-tables = ["browser-launch"] |
| 158 | +new-a2ml-keys-in-runtime = [ |
| 159 | + "wait-for-url-timeout-env-var", |
| 160 | + "wait-for-url-poll-interval-seconds", |
| 161 | + "wait-for-url-poll-interval-env-var", |
| 162 | + "wait-for-url-per-request-timeout-seconds", |
| 163 | +] |
| 164 | +status = "open" |
| 165 | + |
| 166 | +[[pr]] |
| 167 | +number = 179 |
| 168 | +branch = "feat/launcher-soft-attach-and-gui-error" |
| 169 | +title = "feat(launcher-standard): reference impls for soft-attach + gui-dialog-chain" |
| 170 | +class = "reference-impls" |
| 171 | +files = [ |
| 172 | + "launcher/launcher-standard.a2ml", |
| 173 | + "docs/UX-standards/launcher-standard.adoc", |
| 174 | + "launcher/gui-error.sh", # new |
| 175 | + "launcher/soft-attach.sh", # new |
| 176 | +] |
| 177 | +touches-spec-version = false |
| 178 | +addresses = [ |
| 179 | + "soft-attach-no-reference-impl-drift", |
| 180 | + "gui-dialog-chain-no-reference-impl-drift", |
| 181 | + "soft-attach-when-trigger-underspecified", |
| 182 | +] |
| 183 | +new-files = ["launcher/gui-error.sh", "launcher/soft-attach.sh"] |
| 184 | +new-a2ml-keys-in-error-visibility = ["reference-impl", "suppress-env-var"] |
| 185 | +new-a2ml-keys-in-soft-attach = ["reference-impl"] |
| 186 | +soft-attach-tools-extended-with = ["style", "trigger"] |
| 187 | +status = "open" |
| 188 | + |
| 189 | +[coordination] |
| 190 | +# Notes the merger needs to follow. |
| 191 | +spec-version-conflict-resolution = """ |
| 192 | +PRs #170 and #175 both bump [spec].version. They were filed with the |
| 193 | +same target (0.3.0); whichever lands second must conflict-resolve to |
| 194 | +the next bump (e.g. #175 → 0.4.0 if landing after #170). All other PRs |
| 195 | +deliberately did not touch [spec].version to leave the merger free to |
| 196 | +sequence. |
| 197 | +""" |
| 198 | +lockstep-gate-trigger = """ |
| 199 | +Once #172 merges, PRs #170, #171, #173, #175, #177, #179 will all be |
| 200 | +checked by the gate on next push to their branches. All six touch both |
| 201 | +launcher-standard.a2ml AND docs/UX-standards/launcher-standard.adoc, so |
| 202 | +they pass. #176 (keepopen.sh only) and #172 itself touch neither group |
| 203 | +member, so the gate doesn't fire there. |
| 204 | +""" |
| 205 | + |
| 206 | +[deferred-followups] |
| 207 | +# Items deliberately left out of the 8-PR campaign because they depend |
| 208 | +# on merges or live in other repos. |
| 209 | + |
| 210 | +[[deferred]] |
| 211 | +id = "extend-lockstep-gate-to-helpers" |
| 212 | +description = "Add launcher/resolve-desktop-tools.sh, launcher/gui-error.sh, launcher/soft-attach.sh to the lock-step group in .github/workflows/launcher-standard-lockstep.yml" |
| 213 | +gated-on-merges = [171, 179] |
| 214 | +estimated-effort = "~10 lines workflow edit" |
| 215 | + |
| 216 | +[[deferred]] |
| 217 | +id = "launch-scaffolder-loader-update" |
| 218 | +description = "Update the launch-scaffolder Rust loader to consume the new [resolution] / [browser-launch] / [aliases] / [soft-attach].style+trigger / [error-visibility].suppress-env-var tables. Until this lands, this campaign is documentation-and-helper only for the launch-scaffolder consumer — but it already unblocks any new launcher that sources resolve-desktop-tools.sh / gui-error.sh / soft-attach.sh directly." |
| 219 | +gated-on-merges = "all-8-merging-or-at-minimum-#171" |
| 220 | +repo = "launch-scaffolder" |
| 221 | +estimated-effort = "non-trivial — replace hard-coded fallback in Rust loader; respect aliases in mode dispatch; emit style/trigger-aware soft-attach call sites; thread NO_GUI_ERROR into error paths" |
| 222 | + |
| 223 | +[session-lessons-captured] |
| 224 | +# Memory entries written during this campaign for future reference. |
| 225 | +files = [ |
| 226 | + "~/.claude/projects/-home-hyperpolymath/memory/feedback_git_amend_swallows_parallel_staged.md", |
| 227 | + "~/.claude/projects/-home-hyperpolymath/memory/project_standards_launcher_campaign_2026_05_26.md", |
| 228 | +] |
| 229 | +key-lessons = [ |
| 230 | + "git commit --amend without --only sweeps the entire index, including files staged by parallel sessions sharing the checkout. Always use --only <paths> when amending in a multi-session checkout.", |
| 231 | + "When force-pushing to recover from a parallel-session amend incident, use --force-with-lease=branch:explicit-remote-sha; plain --force-with-lease can fail with 'stale info' if local tracking is out of date.", |
| 232 | +] |
| 233 | + |
| 234 | +[provenance] |
| 235 | +review-method = "manual top-to-bottom read of launcher/ + docs/UX-standards/launcher-standard.adoc + LM-LA-LIFECYCLE-STANDARD.adoc (~2,000 lines)" |
| 236 | +review-duration = "single session ~3 hours" |
| 237 | +all-prs-gpg-signed = true |
| 238 | +all-prs-independent-off-main = true |
| 239 | +git-signing-key = "9639451754496E51D6B537CAD119017EBF695AB1" |
0 commit comments