@guiho/runx 0.2.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/DOCS.md +127 -128
- package/README.md +56 -75
- package/devops/build-binaries.ts +44 -62
- package/devops/devops.xdocs.md +8 -5
- package/devops/install.ps1 +182 -152
- package/devops/install.sh +197 -262
- package/devops/installers.spec.ts +31 -0
- package/devops/verify-release-assets.ts +17 -0
- package/docs/docs.xdocs.md +3 -1
- package/docs/plans/plans.xdocs.md +7 -0
- package/docs/plans/rfc-0034-cli-compliance-migration.md +586 -0
- package/docs/plans/upgrade-reliability-implementation.md +95 -0
- package/docs/reviews/implementation/implementation.xdocs.md +2 -0
- package/docs/reviews/implementation/rfc-0034-cli-compliance-migration-review.md +59 -0
- package/docs/reviews/plans/plans.xdocs.md +6 -0
- package/docs/reviews/plans/rfc-0034-cli-compliance-migration-review.md +80 -0
- package/docs/reviews/plans/upgrade-reliability-implementation-review.md +65 -0
- package/docs/superpowers/specs/2026-07-15-upgrade-reliability-design.md +662 -0
- package/docs/superpowers/specs/specs.xdocs.md +24 -0
- package/docs/superpowers/superpowers.xdocs.md +21 -0
- package/docs/todo/rfc-0034-cli-compliance-migration-implementation.md +58 -0
- package/docs/todo/rfc-0034-cli-compliance-migration.md +151 -0
- package/docs/todo/todo.xdocs.md +6 -2
- package/docs/validation/rfc-0034-cli-compliance-migration.md +67 -0
- package/docs/validation/upgrade-reliability.md +124 -0
- package/docs/validation/validation.xdocs.md +5 -0
- package/library/agents.d.ts +28 -4
- package/library/agents.d.ts.map +1 -1
- package/library/agents.js +143 -41
- package/library/cli.d.ts.map +1 -1
- package/library/cli.js +296 -334
- package/library/configuration.d.ts +57 -0
- package/library/configuration.d.ts.map +1 -0
- package/library/configuration.js +111 -0
- package/library/embedded-resources.d.ts +6 -1
- package/library/embedded-resources.d.ts.map +1 -1
- package/library/embedded-resources.js +10 -4
- package/library/executor.d.ts +5 -1
- package/library/executor.d.ts.map +1 -1
- package/library/executor.js +10 -10
- package/library/help.d.ts +8 -4
- package/library/help.d.ts.map +1 -1
- package/library/help.js +70 -46
- package/library/init.d.ts +8 -27
- package/library/init.d.ts.map +1 -1
- package/library/init.js +37 -159
- package/library/manifest.d.ts +4 -43
- package/library/manifest.d.ts.map +1 -1
- package/library/manifest.js +4 -125
- package/library/path-utils.d.ts +13 -0
- package/library/path-utils.d.ts.map +1 -0
- package/library/path-utils.js +82 -0
- package/library/recovery.d.ts +7 -0
- package/library/recovery.d.ts.map +1 -0
- package/library/recovery.js +23 -0
- package/library/release-catalog.d.ts +32 -0
- package/library/release-catalog.d.ts.map +1 -0
- package/library/release-catalog.js +124 -0
- package/library/self-management.d.ts +24 -4
- package/library/self-management.d.ts.map +1 -1
- package/library/self-management.js +279 -99
- package/library/storage.d.ts +13 -0
- package/library/storage.d.ts.map +1 -0
- package/library/storage.js +38 -0
- package/library/types.d.ts +11 -16
- package/library/types.d.ts.map +1 -1
- package/library/types.js +3 -0
- package/library/update-cache.d.ts +21 -0
- package/library/update-cache.d.ts.map +1 -0
- package/library/update-cache.js +68 -0
- package/library/upgrade-reporting.d.ts +11 -0
- package/library/upgrade-reporting.d.ts.map +1 -0
- package/library/upgrade-reporting.js +67 -0
- package/library/upgrade-types.d.ts +72 -0
- package/library/upgrade-types.d.ts.map +1 -0
- package/library/upgrade-types.js +3 -0
- package/package.json +6 -3
- package/scripts/runx-bin.mjs +49 -0
- package/scripts/runx-bin.spec.ts +62 -0
- package/scripts/scripts.xdocs.md +3 -3
- package/skills/guiho-s-runx/SKILL.md +64 -59
- package/skills/guiho-s-runx/guiho-s-runx.xdocs.md +7 -4
- package/skills/skills.xdocs.md +1 -1
- package/docs/todo/implement-runx-alpha.md +0 -36
- package/scripts/runx-bin.ts +0 -24
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Upgrade Reliability Design
|
|
3
|
+
purpose: Define the implementation-ready reliability contract for RunX self-upgrade, version listing, and recovery installation.
|
|
4
|
+
description: Specifies release discovery, visible progress, transactional replacement, rollback, recovery commands, installer hardening, tests, ownership, and release gates for GitHub issues 12 and 13.
|
|
5
|
+
created: 2026-07-15
|
|
6
|
+
flags:
|
|
7
|
+
- accepted
|
|
8
|
+
tags:
|
|
9
|
+
- architecture
|
|
10
|
+
- cli
|
|
11
|
+
- releases
|
|
12
|
+
- reliability
|
|
13
|
+
keywords:
|
|
14
|
+
- runx upgrade
|
|
15
|
+
- runx upgrade list
|
|
16
|
+
- issue 12
|
|
17
|
+
- issue 13
|
|
18
|
+
- windows self-upgrade
|
|
19
|
+
- recovery install command
|
|
20
|
+
- transactional replacement
|
|
21
|
+
owner: runx-superpowers-specs
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# RunX Upgrade Reliability Design
|
|
25
|
+
|
|
26
|
+
## Status and Scope
|
|
27
|
+
|
|
28
|
+
This is the approved design for `CGuiho/runx` issues #12 and #13. It replaces
|
|
29
|
+
the assumption that a scheduled Windows move is a successful upgrade. It also
|
|
30
|
+
extends the existing verified swap into a complete operator experience:
|
|
31
|
+
|
|
32
|
+
- an upgrade plan and visible progress before long operations;
|
|
33
|
+
- synchronous, verified replacement with rollback;
|
|
34
|
+
- an exact-version recovery installation command after every attempt;
|
|
35
|
+
- a separate process-stop command for replacement conflicts;
|
|
36
|
+
- a complete, semantically sorted catalog of all published releases; and
|
|
37
|
+
- hardened direct installers that verify what they install.
|
|
38
|
+
|
|
39
|
+
The work remains inside the Bun/TypeScript native CLI and its existing
|
|
40
|
+
PowerShell and POSIX installers. Citty continues to own command routing and
|
|
41
|
+
ordinary argument parsing. No Node.js runtime dependency is introduced.
|
|
42
|
+
|
|
43
|
+
## Problem Statement
|
|
44
|
+
|
|
45
|
+
### The 0.2.5 bootstrap trap
|
|
46
|
+
|
|
47
|
+
RunX 0.2.5 downloads an update to `runx.exe.new`, launches a detached
|
|
48
|
+
`cmd.exe` process that tries to move the new file directly over the running
|
|
49
|
+
`runx.exe`, discards the helper's output, and returns `scheduled: true` without
|
|
50
|
+
verifying the canonical path. Windows can reject the direct overwrite while
|
|
51
|
+
the original image is mapped. The detached helper then fails silently and the
|
|
52
|
+
next invocation still reports 0.2.5.
|
|
53
|
+
|
|
54
|
+
The updater that is currently running controls the replacement procedure.
|
|
55
|
+
Therefore a corrected 0.2.6 asset cannot retroactively fix the 0.2.5 updater
|
|
56
|
+
that is trying to install it. Users on 0.2.5 need the exact-version recovery
|
|
57
|
+
installer as the bootstrap bridge into the first reliable release.
|
|
58
|
+
|
|
59
|
+
### What 0.2.6 already proves
|
|
60
|
+
|
|
61
|
+
RunX 0.2.6 implements the correct Windows primitive: rename the mapped current
|
|
62
|
+
executable to a backup, place the new executable at the canonical path, execute
|
|
63
|
+
that canonical path with `--version`, roll back on failure, and defer only
|
|
64
|
+
deletion of the mapped backup. This design preserves that mechanism.
|
|
65
|
+
|
|
66
|
+
The remaining defects are broader than the swap itself:
|
|
67
|
+
|
|
68
|
+
- text output is produced only after the awaited upgrade operation returns;
|
|
69
|
+
- release lookup failures and missing assets can look like a no-op;
|
|
70
|
+
- download validation and temporary-file naming are insufficient;
|
|
71
|
+
- asset selection does not use the installers' baseline-first policy;
|
|
72
|
+
- `upgrade list` reads only one page of 20 releases and lacks metadata;
|
|
73
|
+
- the current comparator does not implement SemVer prerelease precedence;
|
|
74
|
+
- JSON output lacks phases, structured failures, and recovery instructions;
|
|
75
|
+
- direct installers do not verify the installed version or roll back; and
|
|
76
|
+
- generic CLI error handling cannot guarantee recovery output after failure.
|
|
77
|
+
|
|
78
|
+
## Goals
|
|
79
|
+
|
|
80
|
+
1. A successful `runx upgrade` means the canonical executable already runs as
|
|
81
|
+
the selected target version when the command returns.
|
|
82
|
+
2. Human users see the plan before download and a message before every
|
|
83
|
+
potentially slow or disruptive phase.
|
|
84
|
+
3. Every result - upgraded, already current, dry run, or failed - ends with an
|
|
85
|
+
exact-version recovery install command and a separate process-stop command.
|
|
86
|
+
4. `runx upgrade list` returns every published RunX release, newest SemVer
|
|
87
|
+
first, with clear channel and compatibility metadata.
|
|
88
|
+
5. Text and JSON expose equivalent facts without corrupting JSON with streamed
|
|
89
|
+
text.
|
|
90
|
+
6. Direct installers apply the same target, platform, architecture, variant,
|
|
91
|
+
verification, and failure semantics as in-process upgrade.
|
|
92
|
+
7. Automated tests make Windows replacement, rollback, output ordering,
|
|
93
|
+
catalog completeness, and installer verification release blockers.
|
|
94
|
+
|
|
95
|
+
## Non-Goals
|
|
96
|
+
|
|
97
|
+
- RunX will not add a background update daemon or automatically upgrade on
|
|
98
|
+
startup.
|
|
99
|
+
- RunX will not automatically kill other RunX processes. It prints an explicit
|
|
100
|
+
process-stop command for the user to choose.
|
|
101
|
+
- npm, Homebrew, winget, Scoop, and other package-manager integrations are not
|
|
102
|
+
added by this work.
|
|
103
|
+
- This work does not add a new release-signing or checksum publication system.
|
|
104
|
+
It validates platform binary format and verifies the downloaded executable's
|
|
105
|
+
reported version. A future signing design can strengthen provenance.
|
|
106
|
+
- Scheduled replacement is not retained as a fallback success state. Only
|
|
107
|
+
cleanup of a renamed old executable may be deferred.
|
|
108
|
+
|
|
109
|
+
## Chosen Architecture
|
|
110
|
+
|
|
111
|
+
The implementation uses five bounded units.
|
|
112
|
+
|
|
113
|
+
### 1. Release catalog
|
|
114
|
+
|
|
115
|
+
The release catalog owns GitHub pagination, tag normalization, SemVer parsing,
|
|
116
|
+
channel labeling, publication metadata, and compatible-asset selection. It
|
|
117
|
+
returns normalized entries and never silently converts transport or schema
|
|
118
|
+
failure into "already current."
|
|
119
|
+
|
|
120
|
+
### 2. Upgrade planner
|
|
121
|
+
|
|
122
|
+
The planner combines the catalog, installed version, platform, architecture,
|
|
123
|
+
CPU variant policy, and canonical executable path into an immutable plan. The
|
|
124
|
+
in-process upgrader always uses the baseline-first x64 policy; direct installers
|
|
125
|
+
may retain their explicit variant override. No download begins before the plan
|
|
126
|
+
is available and rendered.
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
UpgradePlan
|
|
130
|
+
currentVersion
|
|
131
|
+
targetVersion
|
|
132
|
+
os
|
|
133
|
+
arch
|
|
134
|
+
assetName
|
|
135
|
+
assetUrl
|
|
136
|
+
executablePath
|
|
137
|
+
recovery
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The default target is the highest valid stable SemVer release. Prereleases are
|
|
141
|
+
always visible in `upgrade list` but are not selected by default. A future
|
|
142
|
+
explicit prerelease selector can reuse the catalog without changing listing
|
|
143
|
+
semantics.
|
|
144
|
+
|
|
145
|
+
### 3. Transactional installer
|
|
146
|
+
|
|
147
|
+
The transactional installer owns download, validation, backup, canonical
|
|
148
|
+
replacement, target verification, rollback, and cleanup. It emits typed phase
|
|
149
|
+
events but does not render terminal output.
|
|
150
|
+
|
|
151
|
+
### 4. Upgrade reporter
|
|
152
|
+
|
|
153
|
+
The reporter converts the same plan, events, result, error, and recovery data
|
|
154
|
+
into either streaming human text or one buffered JSON document. This keeps
|
|
155
|
+
execution independent from presentation and prevents text/JSON drift.
|
|
156
|
+
|
|
157
|
+
### 5. Recovery contract
|
|
158
|
+
|
|
159
|
+
The recovery contract generates the exact pinned installer and process-stop
|
|
160
|
+
commands for the detected platform. The CLI and installer contract tests use
|
|
161
|
+
the same version and asset-selection rules. The PowerShell and shell scripts
|
|
162
|
+
remain platform-native implementations, with cross-language contract tests to
|
|
163
|
+
prevent drift.
|
|
164
|
+
|
|
165
|
+
## Release Catalog Contract
|
|
166
|
+
|
|
167
|
+
### Retrieval
|
|
168
|
+
|
|
169
|
+
- Request `GET /repos/CGuiho/runx/releases?per_page=100&page=N`.
|
|
170
|
+
- Continue until the GitHub `Link` header has no `rel="next"` entry. A short
|
|
171
|
+
page may be used as a secondary termination signal, never as a substitute
|
|
172
|
+
when a next link exists.
|
|
173
|
+
- Include every published, non-draft release, including prereleases.
|
|
174
|
+
- Preserve the original tag and normalize project tags such as
|
|
175
|
+
`@guiho/runx@0.3.0` and `v0.3.0` to `0.3.0` for comparison.
|
|
176
|
+
- Report non-2xx responses, rate limits, malformed payloads, and pagination
|
|
177
|
+
failures explicitly.
|
|
178
|
+
|
|
179
|
+
### Ordering and channels
|
|
180
|
+
|
|
181
|
+
- Valid SemVer releases sort by SemVer precedence, descending.
|
|
182
|
+
- A stable release outranks its prereleases.
|
|
183
|
+
- `alpha`, `beta`, and `rc` identifiers are displayed exactly as those
|
|
184
|
+
channels. Another textual prerelease identifier is displayed as itself.
|
|
185
|
+
- A numeric-only prerelease identifier is displayed as `prerelease`.
|
|
186
|
+
- Published tags that are not valid SemVer remain visible after valid SemVer
|
|
187
|
+
entries, ordered by publication time descending, and use channel `other`.
|
|
188
|
+
- `latest stable` means the greatest valid non-prerelease SemVer, independent
|
|
189
|
+
of GitHub's presentation order.
|
|
190
|
+
|
|
191
|
+
### Catalog entry
|
|
192
|
+
|
|
193
|
+
Each text row and JSON entry exposes:
|
|
194
|
+
|
|
195
|
+
- original tag and normalized version;
|
|
196
|
+
- channel and prerelease state;
|
|
197
|
+
- publication timestamp;
|
|
198
|
+
- whether it is the installed version;
|
|
199
|
+
- whether it is the latest stable release; and
|
|
200
|
+
- whether the release has a compatible asset for the current OS,
|
|
201
|
+
architecture, and selected x64 variant policy.
|
|
202
|
+
|
|
203
|
+
Text output uses the existing aligned-table style:
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
AVAILABLE RUNX VERSIONS
|
|
207
|
+
|
|
208
|
+
VERSION CHANNEL PUBLISHED CURRENT LATEST ASSET
|
|
209
|
+
0.3.0-alpha.2 alpha 2026-07-15 yes
|
|
210
|
+
0.2.7 stable 2026-07-15 yes yes
|
|
211
|
+
0.2.6 stable 2026-07-14 yes yes
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The actual catalog is not capped at the example's three rows.
|
|
215
|
+
|
|
216
|
+
JSON uses a complete command-specific wrapper rather than returning a bare
|
|
217
|
+
array:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"schemaVersion": 1,
|
|
222
|
+
"command": "runx upgrade list",
|
|
223
|
+
"currentVersion": "0.2.6",
|
|
224
|
+
"latestStableVersion": "0.2.7",
|
|
225
|
+
"releases": [
|
|
226
|
+
{
|
|
227
|
+
"tag": "@guiho/runx@0.2.7",
|
|
228
|
+
"version": "0.2.7",
|
|
229
|
+
"channel": "stable",
|
|
230
|
+
"prerelease": false,
|
|
231
|
+
"publishedAt": "2026-07-15T00:00:00Z",
|
|
232
|
+
"current": false,
|
|
233
|
+
"latestStable": true,
|
|
234
|
+
"compatibleAsset": {
|
|
235
|
+
"name": "runx-windows-x64-baseline.exe",
|
|
236
|
+
"url": "https://github.com/CGuiho/runx/releases/download/%40guiho%2Frunx%400.2.7/runx-windows-x64-baseline.exe"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`releases` contains every normalized published release from all retrieved
|
|
244
|
+
pages, not only compatible or stable releases. `currentVersion` is always the
|
|
245
|
+
installed version. `latestStableVersion` is the greatest valid stable SemVer,
|
|
246
|
+
or null when no published stable SemVer exists.
|
|
247
|
+
|
|
248
|
+
### Asset selection
|
|
249
|
+
|
|
250
|
+
The CLI and installers use one ordered candidate policy:
|
|
251
|
+
|
|
252
|
+
- x64 baseline preference: `baseline`, default, `modern`;
|
|
253
|
+
- x64 default preference: default, `baseline`, `modern`;
|
|
254
|
+
- x64 modern preference: `modern`, default, `baseline`;
|
|
255
|
+
- ARM64: the single ARM64 asset for the detected OS.
|
|
256
|
+
|
|
257
|
+
The selected asset name and exact GitHub download URL are part of the plan. If
|
|
258
|
+
the latest stable release lacks a compatible asset, upgrade fails explicitly;
|
|
259
|
+
it does not silently skip to an older release. The recovery command remains
|
|
260
|
+
pinned to that target and the installer reports the missing asset clearly.
|
|
261
|
+
|
|
262
|
+
## Upgrade Event and Human Output Contract
|
|
263
|
+
|
|
264
|
+
### Event sequence
|
|
265
|
+
|
|
266
|
+
The execution layer emits ordered events with a monotonically increasing
|
|
267
|
+
sequence number. Every event uses one shared phase name and one shared status:
|
|
268
|
+
|
|
269
|
+
- phase: `plan`, `download`, `validate`, `replace`, `verify`, `cache`, or
|
|
270
|
+
`cleanup`;
|
|
271
|
+
- status: `started`, `succeeded`, `skipped`, or `failed`.
|
|
272
|
+
|
|
273
|
+
A normal mutation emits `started` before a phase and `succeeded` after it.
|
|
274
|
+
Failure emits `failed` for that same phase; there is no separate `failed`
|
|
275
|
+
phase. An already-current or dry-run outcome completes `plan` and marks
|
|
276
|
+
mutation phases `skipped`. RunX has no upgrade cache yet, so `cache` is emitted
|
|
277
|
+
as `skipped` after `verify`; retaining the common phase keeps the JSON contract
|
|
278
|
+
aligned with the other GUIHO CLIs without inventing cache behavior.
|
|
279
|
+
|
|
280
|
+
In text mode, the human message derived from each `started` event is written
|
|
281
|
+
and flushed before the implementation awaits that operation. In particular,
|
|
282
|
+
`Downloading...` is visible before the binary body is awaited,
|
|
283
|
+
`Validating...` appears before binary validation, `Replacing...` appears only
|
|
284
|
+
after a complete validated download, and `Verifying...` appears before
|
|
285
|
+
launching the canonical executable. `cleanup: skipped` is valid only when the
|
|
286
|
+
verified upgrade has handed deletion of a locked old image to the detached
|
|
287
|
+
cleanup helper; it never means replacement was scheduled.
|
|
288
|
+
|
|
289
|
+
### Successful text output
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
------------------------------------------------------------
|
|
293
|
+
Upgrading the CLI
|
|
294
|
+
------------------------------------------------------------
|
|
295
|
+
current : 0.2.6
|
|
296
|
+
target : 0.2.7
|
|
297
|
+
os : windows
|
|
298
|
+
arch : x64
|
|
299
|
+
binary : runx-windows-x64-baseline.exe
|
|
300
|
+
path : C:/Users/crist/.local/bin/runx.exe
|
|
301
|
+
url : https://github.com/CGuiho/runx/releases/download/%40guiho%2Frunx%400.2.7/runx-windows-x64-baseline.exe
|
|
302
|
+
------------------------------------------------------------
|
|
303
|
+
Downloading...
|
|
304
|
+
Validating...
|
|
305
|
+
Replacing...
|
|
306
|
+
Verifying...
|
|
307
|
+
Upgrade complete: 0.2.6 -> 0.2.7
|
|
308
|
+
|
|
309
|
+
If the new version is not active, install RunX 0.2.7 directly:
|
|
310
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command '& ([scriptblock]::Create((Invoke-RestMethod "https://raw.githubusercontent.com/CGuiho/runx/main/devops/install.ps1"))) -Version "0.2.7"'
|
|
311
|
+
|
|
312
|
+
If RunX is still running and blocks installation, stop it first:
|
|
313
|
+
powershell.exe -NoProfile -Command "Get-Process runx -ErrorAction SilentlyContinue | Stop-Process -Force"
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The same recovery block follows `Already up to date`, `Dry run complete`, and
|
|
317
|
+
failure results. For an already-current result, the command is pinned to the
|
|
318
|
+
current version. For a release-discovery failure where no target can be
|
|
319
|
+
truthfully resolved, recovery is pinned to the installed version and described
|
|
320
|
+
as a repair installation.
|
|
321
|
+
|
|
322
|
+
Failures name the phase and specific cause, exit nonzero, and never print
|
|
323
|
+
`Upgrade complete`. Recovery instructions are not hidden behind `--verbose`.
|
|
324
|
+
Plan and phase text, non-failure results, and recovery instructions go to
|
|
325
|
+
stdout. The concise failure line goes to stderr, while recovery remains on
|
|
326
|
+
stdout. Diagnostic stacks remain a verbose-only stderr concern.
|
|
327
|
+
|
|
328
|
+
## JSON Contract
|
|
329
|
+
|
|
330
|
+
`--format=json` emits exactly one valid JSON document. It does not stream human
|
|
331
|
+
phase lines. The reporter buffers events and produces this shared envelope:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"schemaVersion": 1,
|
|
336
|
+
"command": "runx upgrade",
|
|
337
|
+
"outcome": "upgraded",
|
|
338
|
+
"plan": {
|
|
339
|
+
"currentVersion": "0.2.6",
|
|
340
|
+
"targetVersion": "0.2.7",
|
|
341
|
+
"os": "windows",
|
|
342
|
+
"arch": "x64",
|
|
343
|
+
"assetName": "runx-windows-x64-baseline.exe",
|
|
344
|
+
"assetUrl": "https://github.com/CGuiho/runx/releases/download/%40guiho%2Frunx%400.2.7/runx-windows-x64-baseline.exe",
|
|
345
|
+
"executablePath": "C:/Users/crist/.local/bin/runx.exe"
|
|
346
|
+
},
|
|
347
|
+
"events": [
|
|
348
|
+
{ "sequence": 1, "phase": "plan", "status": "started" },
|
|
349
|
+
{ "sequence": 2, "phase": "plan", "status": "succeeded" },
|
|
350
|
+
{ "sequence": 3, "phase": "download", "status": "started" },
|
|
351
|
+
{ "sequence": 4, "phase": "download", "status": "succeeded" },
|
|
352
|
+
{ "sequence": 5, "phase": "validate", "status": "started" },
|
|
353
|
+
{ "sequence": 6, "phase": "validate", "status": "succeeded" },
|
|
354
|
+
{ "sequence": 7, "phase": "replace", "status": "started" },
|
|
355
|
+
{ "sequence": 8, "phase": "replace", "status": "succeeded" },
|
|
356
|
+
{ "sequence": 9, "phase": "verify", "status": "started" },
|
|
357
|
+
{ "sequence": 10, "phase": "verify", "status": "succeeded" },
|
|
358
|
+
{ "sequence": 11, "phase": "cache", "status": "skipped" },
|
|
359
|
+
{ "sequence": 12, "phase": "cleanup", "status": "started" },
|
|
360
|
+
{ "sequence": 13, "phase": "cleanup", "status": "skipped" }
|
|
361
|
+
],
|
|
362
|
+
"result": {
|
|
363
|
+
"installedVersion": "0.2.7",
|
|
364
|
+
"cleanupDeferred": true
|
|
365
|
+
},
|
|
366
|
+
"recovery": {
|
|
367
|
+
"targetVersion": "0.2.7",
|
|
368
|
+
"targetSource": "resolved",
|
|
369
|
+
"installCommand": "powershell.exe -NoProfile -ExecutionPolicy Bypass -Command '& ([scriptblock]::Create((Invoke-RestMethod \"https://raw.githubusercontent.com/CGuiho/runx/main/devops/install.ps1\"))) -Version \"0.2.7\"'",
|
|
370
|
+
"stopProcessCommand": "powershell.exe -NoProfile -Command \"Get-Process runx -ErrorAction SilentlyContinue | Stop-Process -Force\""
|
|
371
|
+
},
|
|
372
|
+
"error": null
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
`outcome` is one of `upgraded`, `up-to-date`, `dry-run`, `rolled-back`, or
|
|
377
|
+
`failed`. A successful rollback uses `rolled-back`, retains the primary error,
|
|
378
|
+
and reports the restored installed version in `result`. `failed` covers a
|
|
379
|
+
failure before replacement, a failure for which rollback is unnecessary, or a
|
|
380
|
+
rollback failure. Failed and rolled-back JSON results exit nonzero.
|
|
381
|
+
|
|
382
|
+
The error is null on non-failure outcomes. Otherwise it contains `code`,
|
|
383
|
+
`phase`, and `message`. When release discovery fails before a plan exists,
|
|
384
|
+
`plan` is null and recovery uses `targetSource: "fallback-current"` with the
|
|
385
|
+
installed version. All other resolved plans use `targetSource: "resolved"`.
|
|
386
|
+
Every envelope includes `result`; it is null when no verified installed-version
|
|
387
|
+
result exists. The top-level CLI handler does not append a second generic text
|
|
388
|
+
error after the JSON document.
|
|
389
|
+
|
|
390
|
+
A discovery-failure recovery block is visibly labeled in text:
|
|
391
|
+
|
|
392
|
+
```text
|
|
393
|
+
Repair reinstall (target lookup failed; pinned to installed RunX 0.2.6):
|
|
394
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command '& ([scriptblock]::Create((Invoke-RestMethod "https://raw.githubusercontent.com/CGuiho/runx/main/devops/install.ps1"))) -Version "0.2.6"'
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Stable error codes are:
|
|
398
|
+
|
|
399
|
+
- `release_lookup_failed`
|
|
400
|
+
- `release_payload_invalid`
|
|
401
|
+
- `no_compatible_asset`
|
|
402
|
+
- `download_failed`
|
|
403
|
+
- `download_invalid`
|
|
404
|
+
- `backup_failed`
|
|
405
|
+
- `replace_failed`
|
|
406
|
+
- `verification_failed`
|
|
407
|
+
- `rollback_failed`
|
|
408
|
+
- `installer_verification_failed`
|
|
409
|
+
|
|
410
|
+
## Transactional Replacement Contract
|
|
411
|
+
|
|
412
|
+
The state machine is:
|
|
413
|
+
|
|
414
|
+
```text
|
|
415
|
+
discovered -> planned -> downloaded -> validated -> backed_up
|
|
416
|
+
-> installed -> verified -> complete
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The downloaded file and backup use unique names in the executable's directory,
|
|
420
|
+
for example `runx.exe.new.<pid>.<nonce>` and `runx.exe.old.<pid>.<nonce>`. The
|
|
421
|
+
same directory preserves rename semantics and avoids cross-volume moves.
|
|
422
|
+
|
|
423
|
+
Before modifying the canonical path, the installer requires:
|
|
424
|
+
|
|
425
|
+
- a successful HTTP response;
|
|
426
|
+
- a non-empty body;
|
|
427
|
+
- an expected Windows PE, Linux ELF, or macOS Mach-O binary signature;
|
|
428
|
+
- a filename compatible with the plan; and
|
|
429
|
+
- executable permissions on Unix platforms.
|
|
430
|
+
|
|
431
|
+
Replacement then proceeds:
|
|
432
|
+
|
|
433
|
+
1. Rename the canonical executable to the unique backup path.
|
|
434
|
+
2. Rename the validated download to the canonical path.
|
|
435
|
+
3. Launch the canonical path with `--version`.
|
|
436
|
+
4. Require exit code zero and output exactly equal to the target version.
|
|
437
|
+
5. Mark the upgrade complete only after verification.
|
|
438
|
+
|
|
439
|
+
If any step after backup fails, remove a failed canonical replacement when
|
|
440
|
+
present and rename the backup to the canonical path. A rollback failure reports
|
|
441
|
+
both the primary and rollback errors and identifies every affected path.
|
|
442
|
+
Temporary downloads are removed on every terminal path.
|
|
443
|
+
|
|
444
|
+
On Windows, the mapped old executable may prevent immediate backup deletion.
|
|
445
|
+
A detached retry helper may remove that backup after the current process exits.
|
|
446
|
+
This is cleanup only: the target is already installed and verified at the
|
|
447
|
+
canonical path. No result field or message describes the upgrade itself as
|
|
448
|
+
scheduled.
|
|
449
|
+
|
|
450
|
+
## Recovery Command Contract
|
|
451
|
+
|
|
452
|
+
Recovery instructions are generated before mutation and retained through every
|
|
453
|
+
error. They are always printed after the final text result and always included
|
|
454
|
+
in JSON.
|
|
455
|
+
|
|
456
|
+
Windows uses the supported PowerShell installer with an explicit version. The
|
|
457
|
+
command invokes `powershell.exe` directly so it is usable from the user's Git
|
|
458
|
+
Bash session as well as a PowerShell terminal. Its separate stop command is:
|
|
459
|
+
|
|
460
|
+
```text
|
|
461
|
+
powershell.exe -NoProfile -Command "Get-Process runx -ErrorAction SilentlyContinue | Stop-Process -Force"
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Linux and macOS use the supported shell installer with an explicit version:
|
|
465
|
+
|
|
466
|
+
```text
|
|
467
|
+
curl -fsSL https://raw.githubusercontent.com/CGuiho/runx/main/devops/install.sh | sh -s -- --version '0.2.7'
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Their separate stop command is:
|
|
471
|
+
|
|
472
|
+
```text
|
|
473
|
+
pkill -x runx
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
The real output substitutes the fully resolved target, including complete
|
|
477
|
+
prerelease identifiers such as `0.3.0-alpha.2`. It never uses `latest` in a
|
|
478
|
+
recovery command. Shell quoting is generated and tested rather than assembled
|
|
479
|
+
from unescaped user input.
|
|
480
|
+
|
|
481
|
+
The recovery object records where its target came from. `resolved` means
|
|
482
|
+
release discovery selected the target. `fallback-current` means discovery
|
|
483
|
+
failed before a target could be selected, so the visibly labeled repair
|
|
484
|
+
reinstall is pinned to the installed version instead of inventing a target.
|
|
485
|
+
|
|
486
|
+
The process-stop command is advisory and never executed automatically. If the
|
|
487
|
+
installer detects an active-image conflict, it preserves the old installation,
|
|
488
|
+
exits nonzero, and tells the user to run the stop command before retrying the
|
|
489
|
+
same pinned install command.
|
|
490
|
+
|
|
491
|
+
## Direct Installer Hardening
|
|
492
|
+
|
|
493
|
+
`devops/install.ps1` already accepts `-Version`; `devops/install.sh` already
|
|
494
|
+
accepts `--version`. Both must now:
|
|
495
|
+
|
|
496
|
+
1. preserve full stable or prerelease versions when resolving the release tag;
|
|
497
|
+
2. use the same ordered asset candidates as the in-process planner;
|
|
498
|
+
3. print the target, OS, architecture, asset, destination, and exact URL before
|
|
499
|
+
download;
|
|
500
|
+
4. distinguish an unavailable candidate from a download, validation,
|
|
501
|
+
replacement, permission, or verification failure;
|
|
502
|
+
5. download and validate before modifying the destination;
|
|
503
|
+
6. install transactionally with backup and rollback;
|
|
504
|
+
7. execute the canonical destination with `--version` and require the requested
|
|
505
|
+
version; and
|
|
506
|
+
8. exit nonzero without claiming installation when any requirement fails.
|
|
507
|
+
|
|
508
|
+
Installer URL, tag, and candidate behavior is covered by shared fixtures and
|
|
509
|
+
cross-language contract tests. The scripts remain independently executable;
|
|
510
|
+
the native CLI does not invoke a remote script during normal self-upgrade.
|
|
511
|
+
|
|
512
|
+
## Error Handling Rules
|
|
513
|
+
|
|
514
|
+
- Network, GitHub API, malformed release, and missing-asset errors are never
|
|
515
|
+
converted to an up-to-date result.
|
|
516
|
+
- An equal installed and target version is the only normal up-to-date result.
|
|
517
|
+
- A newer target without a compatible asset is an explicit release-integrity
|
|
518
|
+
failure.
|
|
519
|
+
- The first failed mutation phase determines the primary error code.
|
|
520
|
+
- Rollback is mandatory after the canonical executable has been moved.
|
|
521
|
+
- A successful rollback still yields a failed upgrade and nonzero exit.
|
|
522
|
+
- A rollback failure includes both errors and retains recovery commands.
|
|
523
|
+
- Human output remains concise; `--verbose` adds technical cause chains without
|
|
524
|
+
changing exit status or recovery visibility.
|
|
525
|
+
- Text and JSON are derived from the same structured outcome.
|
|
526
|
+
|
|
527
|
+
## File Ownership
|
|
528
|
+
|
|
529
|
+
| Path | Responsibility |
|
|
530
|
+
| --- | --- |
|
|
531
|
+
| `source/releases.ts` | GitHub pagination, release normalization, SemVer/channel ordering, and compatible-asset selection. |
|
|
532
|
+
| `source/recovery.ts` | Exact-version install and process-stop command generation with platform-safe quoting. |
|
|
533
|
+
| `source/self-management.ts` | Upgrade planning orchestration, download validation, transactional replacement, verification, rollback, and cleanup. |
|
|
534
|
+
| `source/upgrade-output.ts` | Human event streaming and buffered JSON outcome rendering. |
|
|
535
|
+
| `source/types.ts` | Catalog, plan, event, outcome, structured error, and recovery contracts. |
|
|
536
|
+
| `source/render.ts` | Aligned `upgrade list` table rendering alongside existing command tables. |
|
|
537
|
+
| `source/cli.ts` | Citty routing and upgrade-specific exit handling without a second token parser. |
|
|
538
|
+
| `source/releases.spec.ts` | Pagination, SemVer, channel, metadata, and asset-selection tests. |
|
|
539
|
+
| `source/self-management.spec.ts` | Platform transaction, validation, verification, rollback, and cleanup tests. |
|
|
540
|
+
| `source/upgrade-output.spec.ts` | Human ordering, failure recovery, and single-document JSON tests. |
|
|
541
|
+
| `source/guiho-runx.spec.ts` | Command integration for upgrade, check, list, dry-run, and JSON routing. |
|
|
542
|
+
| `devops/install.ps1` | Exact-version transactional Windows installation and verification. |
|
|
543
|
+
| `devops/install.sh` | Exact-version transactional Linux/macOS installation and verification. |
|
|
544
|
+
| `.github/workflows/ci.yml` | Linux and Windows release-blocking coverage, including installer contracts. |
|
|
545
|
+
| `DOCS.md`, `README.md`, `CHANGELOG.md` | Public command, recovery, and release behavior once implementation is complete. |
|
|
546
|
+
| `source/source.xdocs.md`, `devops/devops.xdocs.md`, and affected docs descriptors | Structured ownership for changed and new files. |
|
|
547
|
+
|
|
548
|
+
The implementation plan may keep a proposed new source file inside an existing
|
|
549
|
+
module if review proves the boundary too small to justify a file. It may not
|
|
550
|
+
merge release retrieval, terminal rendering, and filesystem mutation back into
|
|
551
|
+
one untestable operation.
|
|
552
|
+
|
|
553
|
+
## Test Strategy
|
|
554
|
+
|
|
555
|
+
### Release catalog
|
|
556
|
+
|
|
557
|
+
- multiple GitHub pages, including more than 100 releases;
|
|
558
|
+
- `Link`-header continuation and mid-pagination failure;
|
|
559
|
+
- stable, `rc`, `beta`, `alpha`, arbitrary, and numeric prerelease channels;
|
|
560
|
+
- correct SemVer precedence and invalid-tag fallback ordering;
|
|
561
|
+
- current/latest markers and publication timestamps;
|
|
562
|
+
- Windows, Linux, macOS, x64, ARM64, and CPU-variant asset selection; and
|
|
563
|
+
- missing compatible assets without fallback to an older release.
|
|
564
|
+
|
|
565
|
+
### Output and CLI
|
|
566
|
+
|
|
567
|
+
- plan text is captured before the binary fetch starts;
|
|
568
|
+
- every phase is emitted and flushed in exact order;
|
|
569
|
+
- already-current and dry-run paths omit mutation phases;
|
|
570
|
+
- every success and failure path ends with pinned recovery commands;
|
|
571
|
+
- release-discovery failure recovers to the installed version;
|
|
572
|
+
- stable and prerelease command quoting is exact;
|
|
573
|
+
- JSON is one parseable document with equivalent facts;
|
|
574
|
+
- failed JSON has a nonzero exit and no trailing generic text; and
|
|
575
|
+
- Citty help, `-v`, `--version`, and manifest-free behavior remain unchanged.
|
|
576
|
+
|
|
577
|
+
### Transactional replacement
|
|
578
|
+
|
|
579
|
+
- running Windows executable renamed and replaced before success;
|
|
580
|
+
- canonical path reports the target version before the promise resolves;
|
|
581
|
+
- invalid download rejected before the canonical path changes;
|
|
582
|
+
- stale files do not collide because names are unique;
|
|
583
|
+
- permission, backup, rename, and process-launch failures;
|
|
584
|
+
- version mismatch and nonzero verification exit;
|
|
585
|
+
- successful rollback after every post-backup failure;
|
|
586
|
+
- rollback failure reports both causes and paths;
|
|
587
|
+
- temporary download cleanup on every terminal path;
|
|
588
|
+
- immediate or deferred backup cleanup without scheduled-upgrade output; and
|
|
589
|
+
- equivalent verified transaction behavior on Linux and macOS.
|
|
590
|
+
|
|
591
|
+
### Direct installers
|
|
592
|
+
|
|
593
|
+
- explicit stable and prerelease versions;
|
|
594
|
+
- candidate order for every OS, architecture, and x64 variant;
|
|
595
|
+
- missing asset versus network and replacement errors;
|
|
596
|
+
- temporary-directory installation with no user PATH pollution in tests;
|
|
597
|
+
- exact canonical version verification;
|
|
598
|
+
- rollback after invalid or mismatched binary; and
|
|
599
|
+
- PowerShell and POSIX recovery commands executed against controlled release
|
|
600
|
+
fixtures.
|
|
601
|
+
|
|
602
|
+
## Validation and Release Gates
|
|
603
|
+
|
|
604
|
+
Implementation is not complete until all of these pass:
|
|
605
|
+
|
|
606
|
+
1. `bun run typecheck`
|
|
607
|
+
2. `bun test`
|
|
608
|
+
3. `bun run build`
|
|
609
|
+
4. `bun run binary`
|
|
610
|
+
5. `bun run binaries`
|
|
611
|
+
6. Windows CI, including live executable replacement and PowerShell installer
|
|
612
|
+
contract tests
|
|
613
|
+
7. Linux CI, including POSIX installer and catalog tests
|
|
614
|
+
8. strict XDocs metadata and whole-tree doctor validation
|
|
615
|
+
9. native text and JSON smoke tests outside a manifest directory
|
|
616
|
+
10. `git diff --check` and a reviewed task-only diff
|
|
617
|
+
|
|
618
|
+
Release preparation then follows the repository's Mirror workflow:
|
|
619
|
+
|
|
620
|
+
- update public docs and the configured changelog;
|
|
621
|
+
- run `mirror version plan patch` and review the exact next version;
|
|
622
|
+
- never hand-edit Mirror-managed version fields;
|
|
623
|
+
- apply and publish only with explicit user release authorization; and
|
|
624
|
+
- wait for the protected version-tag workflow to publish all native assets and
|
|
625
|
+
npm successfully.
|
|
626
|
+
|
|
627
|
+
### Bootstrap and issue-closure gates
|
|
628
|
+
|
|
629
|
+
The public acceptance run must test both paths:
|
|
630
|
+
|
|
631
|
+
1. From 0.2.5 on Windows, use the printed or documented exact-version recovery
|
|
632
|
+
installer to enter the new reliable release. Do not claim 0.2.5 can repair
|
|
633
|
+
its own updater.
|
|
634
|
+
2. From a reliable installed release, run `runx upgrade` to the newly published
|
|
635
|
+
patch and confirm the visible plan, phase order, synchronous replacement,
|
|
636
|
+
recovery block, and immediate `runx --version` result.
|
|
637
|
+
|
|
638
|
+
Issue #12 closes only after the published native asset passes the real Windows
|
|
639
|
+
upgrade and complete-catalog acceptance checks. Issue #13 closes only after the
|
|
640
|
+
printed stable and prerelease recovery commands are copied into fresh test
|
|
641
|
+
shells, install the pinned versions, and verify those exact versions. Neither
|
|
642
|
+
issue closes on source tests alone or before the release assets exist.
|
|
643
|
+
|
|
644
|
+
## Rejected Alternatives
|
|
645
|
+
|
|
646
|
+
### Keep scheduled replacement and improve the message
|
|
647
|
+
|
|
648
|
+
Rejected because a schedule is not proof of replacement. It recreates the
|
|
649
|
+
0.2.5 failure mode and cannot satisfy immediate version verification.
|
|
650
|
+
|
|
651
|
+
### Invoke the remote installer script from `runx upgrade`
|
|
652
|
+
|
|
653
|
+
Rejected because normal native self-upgrade would gain a shell dependency,
|
|
654
|
+
duplicate download behavior, weaken JSON/event ownership, and execute a second
|
|
655
|
+
remote program. The scripts remain explicit recovery and direct-install tools.
|
|
656
|
+
|
|
657
|
+
### Patch only terminal output
|
|
658
|
+
|
|
659
|
+
Rejected because progress text cannot repair silent release lookup, incomplete
|
|
660
|
+
pagination, incorrect SemVer ordering, installer verification, or rollback
|
|
661
|
+
gaps. Output must describe a reliable structured operation rather than conceal
|
|
662
|
+
an unreliable one.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
subject: runx-superpowers-specs
|
|
3
|
+
description: Approved implementation-ready design specifications for RunX features and reliability work.
|
|
4
|
+
parent: runx-superpowers
|
|
5
|
+
children: []
|
|
6
|
+
files: {}
|
|
7
|
+
documents:
|
|
8
|
+
2026-07-15-upgrade-reliability-design.md: Defines reliable self-upgrade, release catalog, recovery, installer, validation, and release contracts for issues 12 and 13.
|
|
9
|
+
tags:
|
|
10
|
+
- documentation
|
|
11
|
+
- specifications
|
|
12
|
+
- architecture
|
|
13
|
+
keywords:
|
|
14
|
+
- runx upgrade
|
|
15
|
+
- self-upgrade
|
|
16
|
+
- release catalog
|
|
17
|
+
- recovery commands
|
|
18
|
+
flags: []
|
|
19
|
+
status: stable
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
Specifications in this directory are approved inputs to implementation
|
|
23
|
+
planning. They state observable contracts and acceptance gates, not completed
|
|
24
|
+
implementation evidence.
|