@guiho/runx 0.2.4 → 0.2.6

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 CHANGED
@@ -15,6 +15,20 @@ owner: runx
15
15
 
16
16
  # Changelog
17
17
 
18
+ ## 0.2.6 - 2026-07-14
19
+
20
+ ### Added
21
+
22
+ - Added Windows CI coverage for replacing a running executable, verifying the
23
+ installed target version, cleaning the old image, and rolling back failures.
24
+
25
+ ### Fixed
26
+
27
+ - Fixed `runx upgrade` on Windows so it replaces and verifies `runx.exe` before
28
+ reporting success instead of returning a detached `scheduled: true` state.
29
+ - Failed Windows replacements now restore the previous executable, report a
30
+ non-zero error, and clean `.new` and `.old` update files safely.
31
+
18
32
  ## 0.2.4 - 2026-07-14
19
33
 
20
34
  ### Added
package/DOCS.md CHANGED
@@ -122,9 +122,11 @@ runx agents install local
122
122
 
123
123
  `devops/install.ps1` installs Windows assets and `devops/install.sh` installs
124
124
  macOS/Linux assets from GitHub Releases into the user's local bin directory.
125
- `runx upgrade` uses the matching release asset and replaces a native executable;
126
- on Windows replacement is scheduled after RunX exits. `runx uninstall` removes
127
- the same native executable and supports `--dry-run`.
125
+ `runx upgrade` uses the matching release asset and replaces a native executable.
126
+ On Windows it renames the running executable, installs and verifies the target
127
+ version at the original path, and restores the previous executable if replacement
128
+ fails. Only cleanup of the renamed old image is deferred until RunX exits.
129
+ `runx uninstall` removes the same native executable and supports `--dry-run`.
128
130
 
129
131
  When the latest GitHub Release matches the installed version, `runx upgrade` prints `Already up to date.` and exits successfully without downloading or scheduling executable replacement. JSON output reports `upToDate: true`.
130
132
 
@@ -6,16 +6,22 @@ children: []
6
6
  files:
7
7
  alpha-boundaries.md: Captures accepted alpha scope, safety, distribution, and compatibility decisions.
8
8
  citty-cli-migration.md: Defines the accepted full migration from handwritten parsing and routing to Citty.
9
+ mirror-automatic-push.md: Defines automatic Mirror release pushes with a synchronized protected-main gate.
9
10
  npm-trusted-publishing.md: Defines the accepted npm OIDC publishing workflow and protected-branch patch-release trial.
11
+ windows-self-upgrade.md: Defines synchronous Windows executable replacement, verification, rollback, and cleanup.
10
12
  documents:
11
13
  alpha-boundaries.md: Decision record for RunX alpha boundaries.
12
14
  citty-cli-migration.md: Decision record for the complete Citty CLI parser and router migration.
15
+ mirror-automatic-push.md: Accepted release-policy decision for Mirror push=true and protected delivery ordering.
13
16
  npm-trusted-publishing.md: Decision record for GitHub Actions npm trusted publishing and the first automated patch release.
17
+ windows-self-upgrade.md: Accepted design for synchronous and recoverable Windows native self-upgrade.
14
18
  tags:
15
19
  - decisions
16
20
  keywords:
17
21
  - runx
18
22
  - decisions
23
+ - mirror push
24
+ - windows self-upgrade
19
25
  flags: []
20
26
  status: stable
21
27
  ---
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: RunX Mirror Automatic Release Push
3
+ purpose: Record the accepted policy that Mirror pushes RunX release commits and tags automatically.
4
+ description: Defines push=true, the synchronized-main precondition, protected GitHub delivery, failure behavior, and rollback boundary.
5
+ created: 2026-07-14
6
+ flags:
7
+ - accepted
8
+ tags:
9
+ - decisions
10
+ - release
11
+ - mirror
12
+ keywords:
13
+ - runx
14
+ - mirror push
15
+ - protected main
16
+ - release tags
17
+ owner: runx-decisions
18
+ ---
19
+
20
+ # RunX Mirror Automatic Release Push
21
+
22
+ ## Status
23
+
24
+ Accepted by the repository owner on 2026-07-14.
25
+
26
+ ## Context
27
+
28
+ RunX previously configured Mirror with `push = false`. Each release applied the
29
+ version commit and tag locally, then required separate branch delivery and tag
30
+ push commands. The owner wants RunX to match the automatic Mirror release flow
31
+ used by the related GUIHO repositories.
32
+
33
+ RunX also protects `main` and `@guiho/runx@*` tags. Automatic pushing must not
34
+ allow a release tag to reference a commit that has not reached protected
35
+ `main`.
36
+
37
+ ## Decision
38
+
39
+ - Set `[git].push = true` in `mirror.config.toml`.
40
+ - Run future `mirror version apply` operations only from a clean, synchronized
41
+ local `main` whose HEAD equals `origin/main`.
42
+ - Complete implementation changes through protected pull requests before
43
+ applying Mirror.
44
+ - Treat `mirror version apply` as the single release mutation that writes the
45
+ version, commits, tags, and pushes the configured refs.
46
+ - Keep package publication behind the GitHub `production` environment approval
47
+ and npm OIDC trusted publishing.
48
+
49
+ ## Alternatives Considered
50
+
51
+ - Keep `push = false`: rejected because it preserves the manual release step the
52
+ owner explicitly asked to remove.
53
+ - Pass `--push` per release: rejected because it makes release behavior depend
54
+ on operator memory instead of repository configuration.
55
+ - Apply Mirror on a release branch and merge later: rejected because the tag
56
+ could publish a commit before that commit is part of protected `main`.
57
+
58
+ ## Consequences
59
+
60
+ - Successful Mirror applies will immediately push release refs and can trigger
61
+ the public Publish workflow.
62
+ - Operators must treat `mirror version apply` as an externally visible release,
63
+ not a local preparation step.
64
+ - The synchronized-main gate becomes mandatory; failures stop before apply.
65
+ - Existing GitHub rulesets, environment review, and OIDC remain the enforcement
66
+ layers for branch, tag, and package publication.
67
+
68
+ ## Reversal Or Revisit Conditions
69
+
70
+ Return to `push = false` if Mirror cannot push through repository protections
71
+ reliably, if releases need a separate staging phase, or if automatic tag
72
+ publication creates unacceptable operational risk.
73
+
74
+ ## Follow-up Work
75
+
76
+ - Update repository instructions and XDocs metadata.
77
+ - Validate resolved Mirror configuration and a read-only patch plan.
78
+ - Deliver the configuration through protected `main` without applying another
79
+ release.
80
+
81
+ ## References
82
+
83
+ - [Citty CLI Migration Plan](../plans/citty-cli-migration.md)
84
+ - [npm Trusted Publishing Decision](npm-trusted-publishing.md)
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: RunX Windows Self-Upgrade
3
+ purpose: Define synchronous and recoverable replacement of the running RunX executable on Windows.
4
+ description: Records the accepted Windows executable-swap, verification, rollback, cleanup, testing, and patch-release behavior.
5
+ created: 2026-07-14
6
+ flags:
7
+ - accepted
8
+ - implemented
9
+ tags:
10
+ - decisions
11
+ - cli
12
+ - windows
13
+ keywords:
14
+ - runx upgrade
15
+ - windows self-upgrade
16
+ - executable replacement
17
+ - rollback
18
+ owner: runx-decisions
19
+ ---
20
+
21
+ # RunX Windows Self-Upgrade
22
+
23
+ ## Context
24
+
25
+ RunX 0.2.5 downloads a Windows update to `runx.exe.new`, starts a detached
26
+ `cmd.exe` process, reports `scheduled: true`, and exits without confirming that
27
+ the installed executable was replaced. The detached move can fail silently and
28
+ leave later invocations on the previous version.
29
+
30
+ Windows permits the running executable to be renamed while its image remains
31
+ mapped. A new executable can then be installed at the original path before the
32
+ current RunX process exits. Only deletion of the renamed, still-running image
33
+ must wait until the process releases it.
34
+
35
+ ## Decision
36
+
37
+ `runx upgrade` on Windows will complete the executable swap synchronously:
38
+
39
+ 1. Download the selected release asset to `runx.exe.new`.
40
+ 2. Remove any stale backup that is not in use.
41
+ 3. Rename the installed `runx.exe` to `runx.exe.old`.
42
+ 4. Rename `runx.exe.new` to the canonical installed path.
43
+ 5. Execute the newly installed path with `--version` and require the expected
44
+ target version and a zero exit code.
45
+ 6. If installation or verification fails, remove the failed replacement and
46
+ restore `runx.exe.old` to the canonical path.
47
+ 7. After successful verification, delete the backup immediately when possible;
48
+ when Windows still locks the running image, schedule only that backup cleanup
49
+ for after the current process exits.
50
+
51
+ The upgrade result reports `scheduled: false`. Deferred deletion of the old,
52
+ renamed image is cleanup and does not defer or weaken successful replacement.
53
+
54
+ ## Error Handling
55
+
56
+ - A replacement failure must throw a concise `RunXError` and produce a non-zero
57
+ CLI exit code.
58
+ - Rollback must be attempted whenever the installed executable was renamed but
59
+ the new executable was not verified.
60
+ - If rollback also fails, the error must report both the replacement and
61
+ recovery failures and identify the affected installed path.
62
+ - The `.new` file must be removed after failures and must not remain after a
63
+ successful rename.
64
+ - A locked stale `.old` file must stop a new upgrade before the current
65
+ executable is modified.
66
+
67
+ ## Testing
68
+
69
+ Automated coverage must prove that:
70
+
71
+ - the configured Windows self path contains the downloaded replacement before
72
+ `upgradeSelf()` resolves;
73
+ - successful Windows upgrades report `scheduled: false`;
74
+ - no `.new` file remains after success;
75
+ - a failed replacement restores the original executable;
76
+ - the equal-version no-op remains unchanged;
77
+ - `runx -h` continues to work outside a manifest directory.
78
+
79
+ The full release gate is `bun run typecheck`, `bun test`, `bun run build`,
80
+ `bun run binary`, `bun run binaries`, and strict XDocs validation.
81
+
82
+ ## Release
83
+
84
+ After implementation and validation, use Mirror to plan and apply the next
85
+ patch. Update the changelog before applying the Mirror-managed version. Do not
86
+ hand-edit the package version or create the release tag manually.
87
+
88
+ ## Acceptance Criteria
89
+
90
+ - When `runx upgrade` succeeds, the executable at the installed path is already
91
+ the target release.
92
+ - A same-terminal or new-terminal invocation immediately reports the target
93
+ version.
94
+ - The command never reports `scheduled: true` for upgrade replacement.
95
+ - Replacement and verification failures are visible and recover the old binary.
96
+ - Temporary update files are cleaned up safely.
97
+ - Issue #9 has automated Windows regression coverage and issue #1 remains
98
+ covered by the Citty help tests.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: RunX Mirror Automatic Push Plan
3
+ purpose: Execute the accepted push=true release policy through protected main without creating another release.
4
+ description: Sequences configuration, safety instructions, XDocs validation, protected delivery, and post-merge verification.
5
+ created: 2026-07-14
6
+ flags:
7
+ - approved
8
+ tags:
9
+ - plans
10
+ - release
11
+ - mirror
12
+ keywords:
13
+ - runx
14
+ - mirror push
15
+ - protected main
16
+ - configuration
17
+ owner: runx-plans
18
+ ---
19
+
20
+ # RunX Mirror Automatic Push Plan
21
+
22
+ ## Source Decision
23
+
24
+ - [RunX Mirror Automatic Release Push](../decisions/mirror-automatic-push.md)
25
+
26
+ ## Unit 1: Configure And Document Automatic Push
27
+
28
+ - Goal: Make automatic release pushing a repository-owned Mirror policy.
29
+ - Owner: RunX repository.
30
+ - Dependencies: Accepted automatic-push decision; `0.2.4` already applied and
31
+ pushed, so this change must not apply another version.
32
+ - Files:
33
+ - `mirror.config.toml`
34
+ - `AGENTS.md`
35
+ - `runx.xdocs.md`
36
+ - Data, auth, and cache: No application data, authentication, authorization, or
37
+ cache impact. GitHub credentials and rulesets remain external.
38
+ - Changes:
39
+ - Set `[git].push = true`.
40
+ - Require clean synchronized `main` before `mirror version apply`.
41
+ - Document that apply is an externally visible release mutation.
42
+ - Checks:
43
+ - `mirror config check`
44
+ - `mirror config show`
45
+ - `mirror version plan patch --format json`
46
+ - Verify the plan reports `pushEnabled: true` and next patch `0.2.5` without
47
+ applying it.
48
+ - Strict root XDocs metadata and doctor checks.
49
+ - Acceptance: The resolved config enables push; safety guidance prevents apply
50
+ from a feature branch or stale main; no version, commit, or tag is created by
51
+ validation.
52
+ - Stop conditions: Stop if Mirror cannot load the config, the read-only plan
53
+ mutates state, or the worktree contains unrelated changes.
54
+
55
+ ## Unit 2: Deliver Through Protected Main
56
+
57
+ - Goal: Merge the configuration policy without bypassing required CI.
58
+ - Dependencies: Unit 1 complete and committed.
59
+ - Delivery:
60
+ - Push `codex/mirror-auto-push`.
61
+ - Open a ready pull request to `main`.
62
+ - Wait for required CI and merge through the repository ruleset.
63
+ - Synchronize local `main` and confirm `mirror config show` reports
64
+ `push: true`.
65
+ - Acceptance: `origin/main` owns the configuration and instructions; no new
66
+ release tag was created.
67
+ - Stop conditions: Stop on CI failure, unresolved protection failure, or an
68
+ unexpected version/tag mutation.
69
+
70
+ ## TODO Alignment
71
+
72
+ This is a bounded owner-approved configuration change completed in one session;
73
+ it does not require a separate TODO entry.
74
+
75
+ ## First Executable Unit
76
+
77
+ Unit 1: set and document the resolved Mirror automatic-push policy.
@@ -6,17 +6,23 @@ children: []
6
6
  files:
7
7
  alpha-implementation.md: Breaks the accepted alpha into implementation and validation units.
8
8
  citty-cli-migration.md: Executes the full Citty command-tree migration, compatibility validation, protected delivery, and patch release.
9
+ mirror-automatic-push.md: Enables automatic Mirror release pushes with a synchronized protected-main safety gate.
9
10
  npm-trusted-publishing-release.md: Sequences workflow implementation, protected delivery, Mirror patching, and public trusted-publishing verification.
11
+ windows-self-upgrade.md: Sequences synchronous Windows replacement, rollback coverage, validation, issue closure, and patch delivery.
10
12
  documents:
11
13
  alpha-implementation.md: Plan used for the initial RunX implementation.
12
14
  citty-cli-migration.md: Active plan for replacing handwritten CLI parsing and routing with Citty and delivering its patch release.
15
+ mirror-automatic-push.md: Approved plan for configuring, validating, and merging Mirror push=true without applying another release.
13
16
  npm-trusted-publishing-release.md: Approved executable plan for the RunX 0.2.1 trusted-publishing release trial.
17
+ windows-self-upgrade.md: Approved executable plan for fixing Windows native self-upgrade and delivering its patch.
14
18
  tags:
15
19
  - plans
16
20
  keywords:
17
21
  - runx
18
22
  - implementation
19
23
  - citty
24
+ - mirror push
25
+ - windows self-upgrade
20
26
  flags: []
21
27
  status: stable
22
28
  ---
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: RunX Windows Self-Upgrade Plan
3
+ purpose: Execute the accepted synchronous and recoverable Windows self-upgrade design.
4
+ description: Sequences executable replacement, regression coverage, documentation, validation, issue closure, and the next Mirror patch.
5
+ created: 2026-07-14
6
+ flags:
7
+ - approved
8
+ - executed
9
+ tags:
10
+ - plans
11
+ - cli
12
+ - windows
13
+ keywords:
14
+ - runx upgrade
15
+ - executable replacement
16
+ - rollback
17
+ - patch release
18
+ owner: runx-plans
19
+ ---
20
+
21
+ # RunX Windows Self-Upgrade Plan
22
+
23
+ ## Sources
24
+
25
+ - [Windows Self-Upgrade Decision](../decisions/windows-self-upgrade.md)
26
+ - [Alpha Command Catalog Requirements](../requirements/alpha-command-catalog.md)
27
+ - [CLI Architecture](../architecture/cli-architecture.md)
28
+ - GitHub issues `CGuiho/runx#9` and `CGuiho/runx#1`
29
+
30
+ ## Unit 1: Replace the Windows Executable Synchronously
31
+
32
+ - Goal: Make `upgradeSelf()` return successfully only after the canonical
33
+ Windows executable path contains and runs the target release.
34
+ - Owner: `C:\GUIHO\runx`.
35
+ - Dependencies: Accepted Windows self-upgrade decision.
36
+ - Files:
37
+ - `source/self-management.ts`
38
+ - `source/types.ts` only if the result contract must change
39
+ - Behavior:
40
+ - Download to `.new`, rename the installed executable to `.old`, install the
41
+ new file at the original path, and verify `<self-path> --version`.
42
+ - Return `scheduled: false` after verified replacement.
43
+ - On failure, remove the failed target, restore `.old`, remove `.new`, and
44
+ return a non-zero `RunXError` with recovery context.
45
+ - Delete `.old` immediately when possible; defer only deletion of the locked
46
+ old image after the current process exits.
47
+ - Data/schema, auth, permissions, and cache: No application data, schema,
48
+ authentication, authorization, or cache impact. Filesystem write permission
49
+ to the installed executable remains required.
50
+ - Acceptance: The installed path is the verified target before
51
+ `upgradeSelf()` resolves and no `.new` file remains.
52
+ - Stop conditions: Stop if Windows cannot rename the running executable, the
53
+ target cannot be verified, rollback cannot restore the installed path, or
54
+ unrelated self-management behavior changes.
55
+
56
+ ## Unit 2: Add Regression Coverage
57
+
58
+ - Goal: Prove the fixed upgrade contract and preserve the already-correct help
59
+ behavior from issue #1.
60
+ - Dependencies: Unit 1 complete.
61
+ - Files:
62
+ - `source/guiho-runx.spec.ts` or a focused colocated self-management spec
63
+ - existing `source/cli.spec.ts` only if help coverage needs correction
64
+ - Checks:
65
+ - Mock the GitHub latest-release response and downloaded Windows binary.
66
+ - Configure `RUNX_SELF_PATH` to an isolated executable path.
67
+ - Assert the replacement exists at that path before the promise resolves,
68
+ reports `scheduled: false`, runs as the expected version, and leaves no
69
+ `.new` file.
70
+ - Force verification failure and assert restoration of the original file.
71
+ - Run the existing `-h`/`--help` tests outside a manifest directory.
72
+ - Acceptance: Windows CI exercises success and rollback; issue #1 remains
73
+ covered without changing its implemented behavior.
74
+ - Stop conditions: Stop on nondeterministic process cleanup, leaked test
75
+ processes, platform-dependent assertions on non-Windows runners, or failure
76
+ to restore global fetch/environment state.
77
+
78
+ ## Unit 3: Align Documentation and Validate
79
+
80
+ - Goal: Make public and structured documentation describe completed rather than
81
+ scheduled Windows replacement, then pass the full release gate.
82
+ - Dependencies: Units 1 and 2 complete.
83
+ - Files:
84
+ - `DOCS.md`
85
+ - `source/source.xdocs.md`
86
+ - `runx.xdocs.md`
87
+ - accepted decision and plan lifecycle flags when implementation completes
88
+ - Checks:
89
+ - `bun run typecheck`
90
+ - `bun test`
91
+ - `bun run build`
92
+ - `bun run binary`
93
+ - `bun run binaries`
94
+ - `xdocs meta . --documents --strict --owner runx --format json`
95
+ - `xdocs doctor . --warnings-as-errors --format json`
96
+ - `git diff --check`
97
+ - Acceptance: All checks pass, docs agree with behavior, and the worktree
98
+ contains only understood task changes.
99
+ - Stop conditions: Stop on any failed validation or unexpected worktree change.
100
+
101
+ ## Unit 4: Close Issues and Apply the Patch
102
+
103
+ - Goal: Close both resolved GitHub issues and publish the next
104
+ Mirror-managed patch state.
105
+ - Dependencies: Unit 3 committed and the worktree clean.
106
+ - Sequence:
107
+ - Confirm issue #9 acceptance evidence and issue #1 Citty regression evidence.
108
+ - Close issues #9 and #1 with concise resolution comments.
109
+ - Run `mirror config show` and `mirror version plan patch`.
110
+ - Update the configured changelog with the planned next version and commit
111
+ release preparation.
112
+ - Run the full typecheck/test gate again if release preparation changes any
113
+ executable inputs.
114
+ - Apply `mirror version apply patch --yes`; allow configured commit/push/tag
115
+ behavior and do not hand-edit Mirror-managed fields.
116
+ - Acceptance: Both issues are closed, the changelog records the fix, and
117
+ package version, release commit, tag, and configured remote push agree.
118
+ - Stop conditions: Stop if an issue acceptance criterion is unproved, Mirror
119
+ plans an unexpected version, validation fails, or protected push is rejected.
120
+
121
+ ## TODO Alignment
122
+
123
+ This is one approved, focused execution session. No additional durable TODO
124
+ entry or delegated component task is required.
125
+
126
+ ## First Executable Unit
127
+
128
+ Unit 1: implement synchronous Windows replacement and rollback in
129
+ `source/self-management.ts`.
@@ -5,8 +5,10 @@ parent: runx-reviews
5
5
  children: []
6
6
  files:
7
7
  citty-cli-migration-review.md: Reviews the implemented Citty command tree, compatibility adapter, tests, documentation, packaging, and release readiness.
8
+ windows-self-upgrade-review.md: Reviews synchronous Windows replacement, rollback, cleanup, tests, CI coverage, and release readiness.
8
9
  documents:
9
10
  citty-cli-migration-review.md: Accepted implementation review for the full Citty CLI migration.
11
+ windows-self-upgrade-review.md: Accepted implementation review for the Windows native self-upgrade fix.
10
12
  tags:
11
13
  - reviews
12
14
  - implementation
@@ -14,6 +16,7 @@ keywords:
14
16
  - runx
15
17
  - citty
16
18
  - implementation review
19
+ - windows self-upgrade
17
20
  flags: []
18
21
  status: stable
19
22
  ---
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: RunX Windows Self-Upgrade Implementation Review
3
+ purpose: Review the implemented Windows self-upgrade fix against its accepted decision and plan.
4
+ description: Confirms synchronous replacement, target verification, rollback, cleanup, tests, CI coverage, documentation, and release readiness.
5
+ created: 2026-07-14
6
+ flags:
7
+ - accepted
8
+ tags:
9
+ - reviews
10
+ - implementation
11
+ - windows
12
+ keywords:
13
+ - runx upgrade
14
+ - executable replacement
15
+ - rollback
16
+ - windows ci
17
+ owner: runx-implementation-reviews
18
+ ---
19
+
20
+ # RunX Windows Self-Upgrade Implementation Review
21
+
22
+ ## Verdict
23
+
24
+ Accepted. No blocker, high, medium, or low findings remain.
25
+
26
+ ## Findings
27
+
28
+ No actionable findings. The implementation matches the approved synchronous
29
+ replacement and recovery design without changing manifest execution, Citty
30
+ routing, uninstall behavior, or non-Windows upgrade behavior.
31
+
32
+ ## Acceptance Criteria
33
+
34
+ - `upgradeSelf()` renames the running Windows executable, installs the
35
+ downloaded target at the canonical path, and awaits `<path> --version`.
36
+ - Successful Windows upgrade results report `scheduled: false`.
37
+ - Verification failures remove the invalid replacement and restore `.old`.
38
+ - `.new` is absent after success and rollback.
39
+ - Cleanup retries deletion of the locked `.old` image after the old process
40
+ releases it.
41
+ - A Windows-only test holds a real copied executable open during replacement,
42
+ verifies the new binary at the canonical path before completion, and observes
43
+ backup cleanup.
44
+ - A second Windows test proves rollback after invalid-binary verification.
45
+ - Existing Citty tests continue to prove `-h` and `--help` outside a manifest.
46
+ - The GitHub workflow now runs typecheck, tests, build, and native compilation
47
+ on `windows-latest` in addition to the existing Linux release-matrix gate.
48
+
49
+ ## Verification Evidence
50
+
51
+ - `bun run typecheck`: passed.
52
+ - `bun test`: 15 passed, 0 failed, 134 assertions.
53
+ - `bun run build`: passed.
54
+ - `bun run binary`: passed.
55
+ - `bun run binaries`: twelve native assets built and verified.
56
+ - Windows x64 native `-h` outside a manifest: passed.
57
+ - Windows x64 native `--version`: reported `0.2.5` before the release bump.
58
+ - CI workflow YAML parse: passed.
59
+ - `xdocs doctor . --warnings-as-errors --format json`: 0 errors, 0 warnings.
60
+ - `git diff --check`: passed.
61
+
62
+ ## Documentation and TODO
63
+
64
+ `DOCS.md`, source/root/workflow XDocs descriptors, the accepted decision, plan,
65
+ plan review, this implementation review, and validation evidence are aligned.
66
+ No TODO entry is needed for this completed single-session fix.
67
+
68
+ ## Residual Risk
69
+
70
+ The new `windows-latest` job cannot execute until the committed workflow reaches
71
+ GitHub. Local Windows coverage exercises the same live-file replacement and
72
+ cleanup behavior. Public upgrade from 0.2.0 to the new patch can only be verified
73
+ after the patch release assets exist.
74
+
75
+ ## References
76
+
77
+ - [Decision](../../decisions/windows-self-upgrade.md)
78
+ - [Plan](../../plans/windows-self-upgrade.md)
79
+ - [Plan Review](../plans/windows-self-upgrade-review.md)
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: RunX Mirror Automatic Push Plan Review
3
+ purpose: Verify that the automatic-push configuration plan is safe, bounded, and ready for execution.
4
+ description: Reviews synchronized-main safeguards, read-only validation, protected delivery, and release side effects.
5
+ created: 2026-07-14
6
+ flags:
7
+ - approved
8
+ tags:
9
+ - reviews
10
+ - plans
11
+ - release
12
+ keywords:
13
+ - runx
14
+ - mirror push
15
+ - plan review
16
+ - protected main
17
+ owner: runx-plan-reviews
18
+ ---
19
+
20
+ # RunX Mirror Automatic Push Plan Review
21
+
22
+ ## Verdict
23
+
24
+ Ready for execution.
25
+
26
+ ## Findings
27
+
28
+ - No blocker or high-severity findings remain.
29
+ - The clean, synchronized `main` precondition is explicit and prevents a
30
+ release tag from pointing at an unmerged feature or stale commit.
31
+ - Unit 1 uses only read-only Mirror validation and explicitly prohibits another
32
+ version apply, so the current `0.2.4` release remains unchanged.
33
+ - The plan documents that `push = true` makes every future Mirror apply an
34
+ externally visible release mutation.
35
+ - No application data, authentication, authorization, or cache behavior is
36
+ affected. GitHub credentials, rulesets, environment approval, and npm OIDC
37
+ remain external enforcement layers.
38
+
39
+ ## Delivery Review
40
+
41
+ - The configuration change must pass the protected pull-request workflow
42
+ before reaching `main`.
43
+ - Post-merge verification checks the resolved configuration on synchronized
44
+ `main` and confirms that no `0.2.5` tag was created.
45
+ - A CI failure, protection failure, or unexpected version mutation is an
46
+ explicit stop condition.
47
+
48
+ ## First Executable Unit
49
+
50
+ Execute Unit 1 in
51
+ [RunX Mirror Automatic Push Plan](../../plans/mirror-automatic-push.md): set and
52
+ document the resolved automatic-push policy without applying a release.
53
+
54
+ ## References
55
+
56
+ - [RunX Mirror Automatic Push Plan](../../plans/mirror-automatic-push.md)
57
+ - [RunX Mirror Automatic Release Push](../../decisions/mirror-automatic-push.md)
@@ -5,10 +5,14 @@ parent: runx-reviews
5
5
  children: []
6
6
  files:
7
7
  citty-cli-migration-review.md: Reviews completeness, compatibility, safety invariants, native packaging, and release sequencing for the Citty migration.
8
+ mirror-automatic-push-review.md: Reviews synchronized-main safeguards, read-only validation, protected delivery, and release side effects.
8
9
  npm-trusted-publishing-release-review.md: Reviews sequencing, acceptance criteria, safety gates, and validation for the RunX 0.2.1 trusted-publishing plan.
10
+ windows-self-upgrade-review.md: Reviews synchronous replacement, rollback, regression coverage, issue closure, and patch delivery.
9
11
  documents:
10
12
  citty-cli-migration-review.md: Ready-for-execution review of the full Citty CLI migration plan.
13
+ mirror-automatic-push-review.md: Ready-for-execution review of the Mirror automatic-push configuration plan.
11
14
  npm-trusted-publishing-release-review.md: Ready-for-execution review of the npm trusted-publishing release plan.
15
+ windows-self-upgrade-review.md: Ready-for-execution review of the Windows native self-upgrade fix plan.
12
16
  tags:
13
17
  - reviews
14
18
  - plans
@@ -16,7 +20,9 @@ keywords:
16
20
  - runx
17
21
  - plan review
18
22
  - citty
23
+ - mirror push
19
24
  - trusted publishing
25
+ - windows self-upgrade
20
26
  flags: []
21
27
  status: stable
22
28
  ---
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: RunX Windows Self-Upgrade Plan Review
3
+ purpose: Verify that the Windows self-upgrade plan is safe, complete, and executable.
4
+ description: Reviews replacement sequencing, rollback, tests, documentation, issue closure, and Mirror patch delivery.
5
+ created: 2026-07-14
6
+ flags:
7
+ - approved
8
+ tags:
9
+ - reviews
10
+ - plans
11
+ - windows
12
+ keywords:
13
+ - runx upgrade
14
+ - executable replacement
15
+ - rollback
16
+ - mirror patch
17
+ owner: runx-plan-reviews
18
+ ---
19
+
20
+ # RunX Windows Self-Upgrade Plan Review
21
+
22
+ ## Verdict
23
+
24
+ Ready for execution.
25
+
26
+ ## Findings
27
+
28
+ No blocker, high, medium, or low findings remain. The plan traces to the accepted
29
+ Windows self-upgrade decision and existing native-upgrade requirement. Each unit
30
+ has a bounded outcome, named files, dependencies, acceptance criteria, checks,
31
+ and explicit stop conditions.
32
+
33
+ ## Sequencing and Failure Safety
34
+
35
+ - Replacement precedes verification, and successful completion requires the
36
+ canonical path to execute the expected target version.
37
+ - Rollback occurs before any failure is surfaced after the old executable was
38
+ renamed.
39
+ - Deferred work is limited to deleting the locked old image; replacement itself
40
+ is complete before success.
41
+ - Issue closure and Mirror application follow implementation validation and a
42
+ clean committed worktree.
43
+ - The plan recognizes configured Mirror commit, push, and tag side effects and
44
+ stops on protected-push rejection.
45
+
46
+ ## Coverage Review
47
+
48
+ - Success, failure, rollback, temporary-file cleanup, and platform isolation are
49
+ explicit.
50
+ - Existing `-h` coverage directly supports closing issue #1.
51
+ - Typecheck, unit tests, library build, local binary, the twelve-target matrix,
52
+ strict XDocs checks, and diff hygiene are included.
53
+ - No route, API, UI, data schema, authentication, authorization, cache, cloud,
54
+ secret, or database work is in scope.
55
+
56
+ ## TODO and Documentation Alignment
57
+
58
+ The work is a single focused package session and does not need a long-running
59
+ TODO entry. Public `DOCS.md`, source/root XDocs descriptors, lifecycle records,
60
+ and the changelog are included in the correct sequence.
61
+
62
+ ## First Executable Unit
63
+
64
+ Implement synchronous replacement, target-version verification, rollback, and
65
+ old-image cleanup in `source/self-management.ts`.
66
+
67
+ ## References
68
+
69
+ - [Plan](../../plans/windows-self-upgrade.md)
70
+ - [Decision](../../decisions/windows-self-upgrade.md)
71
+ - [CLI Architecture](../../architecture/cli-architecture.md)
@@ -6,10 +6,12 @@ children: []
6
6
  files:
7
7
  alpha-implementation-summary.md: Records the completed alpha implementation, checks, and release boundaries.
8
8
  citty-cli-migration.md: Records the complete local validation gate for the Citty command-tree migration.
9
+ windows-self-upgrade.md: Records Windows replacement, rollback, cleanup, native build, CI, and XDocs validation evidence.
9
10
  documents:
10
11
  alpha-implementation-summary.md: Validation summary for the first RunX implementation.
11
12
  citty-cli-migration.md: Validation evidence for TypeScript, tests, native assets, npm packaging, CLI behavior, and XDocs.
12
13
  npm-trusted-publishing-0.2.2.md: Validation evidence for the blocked 0.2.2 npm trusted-publishing retry.
14
+ windows-self-upgrade.md: Validation evidence for GitHub issues #9 and #1 and the Windows self-upgrade patch.
13
15
  tags:
14
16
  - validation
15
17
  keywords:
@@ -17,6 +19,7 @@ keywords:
17
19
  - citty
18
20
  - tests
19
21
  - summary
22
+ - windows self-upgrade
20
23
  flags: []
21
24
  status: stable
22
25
  ---
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: RunX Windows Self-Upgrade Validation
3
+ purpose: Record verification evidence for the Windows self-upgrade fix and issue closure.
4
+ description: Captures Windows replacement and rollback tests, complete Bun build checks, native CLI checks, CI syntax, XDocs health, and residual release risk.
5
+ created: 2026-07-14
6
+ flags:
7
+ - validated
8
+ tags:
9
+ - validation
10
+ - windows
11
+ - cli
12
+ keywords:
13
+ - runx upgrade
14
+ - windows executable
15
+ - rollback
16
+ - issue 9
17
+ - issue 1
18
+ owner: runx-validation
19
+ ---
20
+
21
+ # RunX Windows Self-Upgrade Validation
22
+
23
+ ## Summary
24
+
25
+ The implementation is ready for commit, issue closure, and the requested
26
+ Mirror-managed patch. All local checks passed on Windows.
27
+
28
+ ## Scope
29
+
30
+ - Synchronous Windows replacement of a running native executable.
31
+ - Target-version verification before successful completion.
32
+ - Rollback after invalid replacement verification.
33
+ - Deferred cleanup of the locked old executable only.
34
+ - Existing manifest-free `-h` behavior from issue #1.
35
+ - Windows CI coverage and structured documentation health.
36
+
37
+ ## Commands and Results
38
+
39
+ | Check | Result | Evidence |
40
+ | --- | --- | --- |
41
+ | `bun run typecheck` | passed | TypeScript completed without diagnostics. |
42
+ | `bun test` | passed | 15 tests, 0 failures, 134 assertions. |
43
+ | `bun run build` | passed | Library output compiled. |
44
+ | `bun run binary` | passed | Local Windows executable compiled. |
45
+ | `bun run binaries` | passed | All twelve release assets built and verified. |
46
+ | Native `runx-windows-x64.exe -h` outside a manifest | passed | Citty usage rendered with exit code 0. |
47
+ | Native `runx-windows-x64.exe --version` | passed | Reported `0.2.5` before the patch bump. |
48
+ | `Bun.YAML.parse(.github/workflows/ci.yml)` | passed | Workflow YAML parsed successfully. |
49
+ | `xdocs meta . --documents --strict --owner runx --format json` | passed | Root metadata and companions valid. |
50
+ | `xdocs doctor . --warnings-as-errors --format json` | passed | 0 errors and 0 warnings. |
51
+ | `git diff --check` | passed | No whitespace errors. |
52
+
53
+ ## Behavioral Evidence
54
+
55
+ `source/self-management.spec.ts` runs only on Windows for the platform-specific
56
+ cases. Its success case launches a copied PowerShell executable from the
57
+ configured self path, keeps that executable running, upgrades the same path to
58
+ the current Bun executable, and requires the replacement to report the expected
59
+ version before `upgradeSelf()` resolves. It also observes removal of `.new` and
60
+ eventual removal of `.old` after the running process exits.
61
+
62
+ The rollback case downloads invalid executable bytes, requires verification to
63
+ fail, and confirms that the original bytes are restored with neither `.new` nor
64
+ `.old` left behind.
65
+
66
+ The existing `source/cli.spec.ts` root-help test proves both `-h` and `--help`
67
+ work without a manifest, satisfying issue #1.
68
+
69
+ ## Skipped or Blocked Checks
70
+
71
+ - The new GitHub-hosted `windows-latest` job is not locally runnable; it will
72
+ execute after the commit reaches GitHub.
73
+ - An actual 0.2.0-to-new-patch public upgrade is blocked until the new release
74
+ assets are published.
75
+
76
+ ## Readiness
77
+
78
+ Ready for commit, closing GitHub issues #9 and #1, changelog preparation, and
79
+ the requested Mirror patch plan/apply sequence.
80
+
81
+ ## References
82
+
83
+ - [Implementation Review](../reviews/implementation/windows-self-upgrade-review.md)
84
+ - [Plan](../plans/windows-self-upgrade.md)
85
+ - [Decision](../decisions/windows-self-upgrade.md)
@@ -1,8 +1,9 @@
1
1
  import type { RunXUpgradeResult, UpdateResult } from './types.js';
2
- export declare const checkForLatestVersion: () => Promise<UpdateResult>;
3
- export declare const listAvailableVersions: () => Promise<string[]>;
4
- export declare const upgradeSelf: (dryRun: boolean) => Promise<RunXUpgradeResult>;
5
- export declare const uninstallSelf: (dryRun: boolean) => Promise<{
2
+ export { checkForLatestVersion, listAvailableVersions, uninstallSelf, upgradeSelf, };
3
+ declare const checkForLatestVersion: () => Promise<UpdateResult>;
4
+ declare const listAvailableVersions: () => Promise<string[]>;
5
+ declare const upgradeSelf: (dryRun: boolean) => Promise<RunXUpgradeResult>;
6
+ declare const uninstallSelf: (dryRun: boolean) => Promise<{
6
7
  executablePath: string;
7
8
  scheduled: boolean;
8
9
  dryRun: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAMjE,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,YAAY,CAYlE,CAAA;AAED,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,EAAE,CAK9D,CAAA;AAED,eAAO,MAAM,WAAW,WAAkB,OAAO,KAAG,OAAO,CAAC,iBAAiB,CAmB5E,CAAA;AAED,eAAO,MAAM,aAAa,WAAkB,OAAO,KAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAW5H,CAAA"}
1
+ {"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEjE,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,WAAW,GACZ,CAAA;AAMD,QAAA,MAAM,qBAAqB,QAAa,OAAO,CAAC,YAAY,CAY3D,CAAA;AAED,QAAA,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,EAAE,CAKvD,CAAA;AAED,QAAA,MAAM,WAAW,WAAkB,OAAO,KAAG,OAAO,CAAC,iBAAiB,CAmBrE,CAAA;AAED,QAAA,MAAM,aAAa,WAAkB,OAAO,KAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAWrH,CAAA"}
@@ -1,9 +1,11 @@
1
+ import { Buffer } from 'node:buffer';
1
2
  import { chmod, rename, rm } from 'node:fs/promises';
2
3
  import { basename } from 'node:path';
3
4
  import { RunXError } from './errors.js';
4
5
  import { readVersion } from './help.js';
6
+ export { checkForLatestVersion, listAvailableVersions, uninstallSelf, upgradeSelf, };
5
7
  const repositoryApi = 'https://api.github.com/repos/CGuiho/runx/releases';
6
- export const checkForLatestVersion = async () => {
8
+ const checkForLatestVersion = async () => {
7
9
  const currentVersion = readVersion();
8
10
  try {
9
11
  const response = await fetch(`${repositoryApi}/latest`, { headers: { accept: 'application/vnd.github+json' } });
@@ -18,14 +20,14 @@ export const checkForLatestVersion = async () => {
18
20
  return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
19
21
  }
20
22
  };
21
- export const listAvailableVersions = async () => {
23
+ const listAvailableVersions = async () => {
22
24
  const response = await fetch(`${repositoryApi}?per_page=20`, { headers: { accept: 'application/vnd.github+json' } });
23
25
  if (!response.ok)
24
26
  throw new RunXError(`Could not retrieve RunX releases: HTTP ${response.status}`);
25
27
  const releases = await response.json();
26
28
  return releases.map((release) => release.tag_name.replace(/^@guiho\/runx@/, '').replace(/^v/, ''));
27
29
  };
28
- export const upgradeSelf = async (dryRun) => {
30
+ const upgradeSelf = async (dryRun) => {
29
31
  const executablePath = requireNativeExecutable();
30
32
  const update = await checkForLatestVersion();
31
33
  const upToDate = !update.updateAvailable && update.latestVersion === update.currentVersion && Boolean(update.url);
@@ -43,10 +45,10 @@ export const upgradeSelf = async (dryRun) => {
43
45
  await rename(temporaryPath, executablePath);
44
46
  return { ...update, executablePath, scheduled: false, upToDate: false };
45
47
  }
46
- scheduleWindowsReplacement(temporaryPath, executablePath);
47
- return { ...update, executablePath, scheduled: true, upToDate: false };
48
+ await replaceWindowsExecutable(temporaryPath, executablePath, update.latestVersion);
49
+ return { ...update, executablePath, scheduled: false, upToDate: false };
48
50
  };
49
- export const uninstallSelf = async (dryRun) => {
51
+ const uninstallSelf = async (dryRun) => {
50
52
  const executablePath = requireNativeExecutable();
51
53
  if (dryRun)
52
54
  return { executablePath, scheduled: false, dryRun: true };
@@ -66,10 +68,65 @@ const requireNativeExecutable = () => {
66
68
  throw new RunXError('Self-management requires a native RunX executable installed from a GitHub release.');
67
69
  return executablePath;
68
70
  };
69
- const scheduleWindowsReplacement = (temporaryPath, executablePath) => {
70
- const command = `ping 127.0.0.1 -n 2 > nul & move /y "${temporaryPath}" "${executablePath}" > nul`;
71
- Bun.spawn(['cmd.exe', '/d', '/s', '/c', command], { detached: true, stdout: 'ignore', stderr: 'ignore', stdin: 'ignore' });
71
+ const replaceWindowsExecutable = async (temporaryPath, executablePath, expectedVersion) => {
72
+ const backupPath = `${executablePath}.old`;
73
+ let originalMoved = false;
74
+ try {
75
+ await rm(backupPath, { force: true });
76
+ await rename(executablePath, backupPath);
77
+ originalMoved = true;
78
+ await rename(temporaryPath, executablePath);
79
+ await verifyExecutableVersion(executablePath, expectedVersion);
80
+ await cleanupWindowsBackup(backupPath);
81
+ }
82
+ catch (error) {
83
+ const replacementFailure = errorMessage(error);
84
+ await rm(temporaryPath, { force: true }).catch(() => undefined);
85
+ if (!originalMoved) {
86
+ throw new RunXError(`Could not replace the Windows RunX executable at ${executablePath}: ${replacementFailure}`);
87
+ }
88
+ try {
89
+ await rm(executablePath, { force: true });
90
+ await rename(backupPath, executablePath);
91
+ }
92
+ catch (rollbackError) {
93
+ throw new RunXError(`Windows RunX upgrade failed at ${executablePath}: ${replacementFailure}. Automatic rollback also failed: ${errorMessage(rollbackError)}`);
94
+ }
95
+ throw new RunXError(`Windows RunX upgrade failed; the previous executable was restored at ${executablePath}: ${replacementFailure}`);
96
+ }
97
+ };
98
+ const verifyExecutableVersion = async (executablePath, expectedVersion) => {
99
+ const verification = Bun.spawn([executablePath, '--version'], { stdin: 'ignore', stdout: 'pipe', stderr: 'pipe' });
100
+ const [stdout, stderr, exitCode] = await Promise.all([
101
+ new Response(verification.stdout).text(),
102
+ new Response(verification.stderr).text(),
103
+ verification.exited,
104
+ ]);
105
+ const actualVersion = stdout.trim();
106
+ if (exitCode !== 0)
107
+ throw new Error(`replacement exited with code ${exitCode}${stderr.trim() ? `: ${stderr.trim()}` : ''}`);
108
+ if (actualVersion !== expectedVersion)
109
+ throw new Error(`replacement reported version ${actualVersion || '<empty>'}; expected ${expectedVersion}`);
110
+ };
111
+ const cleanupWindowsBackup = async (backupPath) => {
112
+ try {
113
+ await rm(backupPath, { force: true });
114
+ }
115
+ catch { }
116
+ scheduleWindowsBackupCleanup(backupPath);
117
+ };
118
+ const scheduleWindowsBackupCleanup = (backupPath) => {
119
+ const command = 'for ($attempt = 0; $attempt -lt 300; $attempt += 1) { if (-not (Test-Path -LiteralPath $env:RUNX_BACKUP_PATH)) { exit 0 }; try { Remove-Item -LiteralPath $env:RUNX_BACKUP_PATH -Force -ErrorAction Stop; exit 0 } catch { Start-Sleep -Milliseconds 100 } }; exit 1';
120
+ const encodedCommand = Buffer.from(command, 'utf16le').toString('base64');
121
+ Bun.spawn(['cmd.exe', '/d', '/s', '/c', `powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -EncodedCommand ${encodedCommand}`], {
122
+ detached: true,
123
+ env: { ...process.env, RUNX_BACKUP_PATH: backupPath },
124
+ stdout: 'ignore',
125
+ stderr: 'ignore',
126
+ stdin: 'ignore',
127
+ });
72
128
  };
129
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
73
130
  const compareVersions = (left, right) => {
74
131
  const parse = (value) => value.split(/[.+-]/).map((part) => Number.parseInt(part, 10) || 0);
75
132
  const a = parse(left);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guiho/runx",
3
3
  "description": "A language-agnostic, documented command catalog and local CLI executor.",
4
- "version": "0.2.4",
4
+ "version": "0.2.6",
5
5
  "type": "module",
6
6
  "main": "./library/guiho-runx.js",
7
7
  "types": "./library/guiho-runx.d.ts",