feat(install): add standalone release archive installers - #2187
feat(install): add standalone release archive installers#2187flora131 wants to merge 18 commits into
Conversation
Assistant-model: GPT-5.6 Sol
Assistant-model: GPT-5.6 Sol
Use positive Linux libc detection, bundle payload-local musl C++ runtimes, and validate both release architectures on stock Alpine. Generate a BOM-free relative Windows shim with transactional junction rollback and ordered failure cleanup. Refs: #2159 Assistant-model: GPT-5.6 Sol
Assistant-model: GPT-5.6 Sol
Handle PowerShell redirect headers across supported engines and keep failed rollback work retryable. Resolve POSIX symlink aliases before collision checks, compare PATH entries literally, and pin the musl native-load CI contract. Assistant-model: GPT-5.6 Sol
Assistant-model: GPT-5.6 Sol
Keep API tokens out of downloader arguments, quote POSIX PATH guidance, reject unsafe launcher layouts before requests, and make rollback cleanup retryable. Isolate PowerShell IEX state and scope TLS 1.2 to installer requests. Assistant-model: GPT-5.6 Sol
Fail closed when a requested exact tag does not match the release GitHub returns, before any download. Preserve trailing newlines in custom POSIX install and bin paths. Leave Windows PATH untouched for a semicolon-containing bin directory and print direct-run guidance instead. Assistant-model: Claude Opus 5
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Implementation notes (verbatim artifact)Source: Implementation NotesTask: Implement #2159 in the Atomic repository. Start from the latest fetched origin/main in the requested isolated worktree. Add root POSIX-sh install.sh and Windows PowerShell 5.1 install.ps1 that install only prebuilt GitHub Release archives, never invoke or bootstrap npm, pnpm, yarn, bun, or Node.js, resolve stable/latest and pinned refs, select all supported platform/arch/libc assets, verify SHA256SUMS before touching an existing install, atomically install the full archive payload into versioned roots with a current pointer plus bin symlink/shim, clean up failures, support the issue's environment knobs, remain idempotent, and give PATH guidance. Add robust automated tests, including the exact asset-name drift contract against scripts/build-binaries.sh and the stage-github-release upload list in .github/workflows/publish.yml. Test shell behavior for BusyBox/Alpine-compatible POSIX sh and relevant download/checksum/error/idempotency flows without relying on forbidden package tools; test PowerShell where feasible and use contract/static checks where the host lacks Windows PowerShell. Update README.md, packages/coding-agent/docs/quickstart.md, docs/windows.md, and any containerization/Termux guidance affected by the binary path. Do not add a package changelog entry. Follow CONTRIBUTING.md and repository rules, run targeted suites and npm run check, inspect the full diff, and commit/push a focused feature branch. The implementation must satisfy every acceptance criterion and constraint in issue #2159. Running Notes
Iteration 8: findings artifact was empty, findings recovered elsewhereThe consolidated unresolved-findings artifact for this iteration, Cycle-8 review split: reviewer-a returned Frozen batch and repairs1. [P2] Exact-ref identity was not verified (both installers). The Repair: both installers now compare the resolved tag with the requested ref and fail closed before the 2. [P2] A semicolon-containing Windows Repair: 3. [P3] Trailing newlines were stripped from custom POSIX paths. Repair: TDD receipts
Behavior change worth flaggingThe Windows fixture's Iteration 8 validation receipts
Base drift audit
Decision: do not rebase. The branch did start from the latest fetched Contract amendments received (inherited, cycle 7 — still binding)
No new user amendment arrived this cycle. Deferred (outside the contract, not implemented)
Iteration 8 delivery
|
Reject a pre-existing Windows launcher whose PATHEXT extension resolves ahead of atomic.cmd, such as a stale atomic.exe, before any GitHub request. Reject an ATOMIC_BIN_DIR nested under the transaction-owned current or versions paths before any request or filesystem change. Accept GNU sha256sum binary-mode rows, and restore the caller's umask before creating the install root, bin directory, and version payload so only temporary token and download state stays owner-only. Assistant-model: Claude Opus 5
| $installRoot = [IO.Path]::GetFullPath($installRoot) | ||
|
|
||
| $binDir = $env:ATOMIC_BIN_DIR | ||
| if ([string]::IsNullOrWhiteSpace($binDir)) { | ||
| $binDir = Join-Path $installRoot "bin" | ||
| } | ||
| $binDir = [IO.Path]::GetFullPath($binDir) |
There was a problem hiding this comment.
Transaction-owned directories can be used as the shim location
ATOMIC_BIN_DIR is normalized but never checked against <installRoot>\current or <installRoot>\versions. If it points to either tree or one of their descendants, the installer replaces transaction-managed version/current entries and then creates atomic-current and atomic.cmd within that same tree. A current-nested directory resolves through the junction into the installed version payload, while a versions-nested directory can be moved during version replacement. Reject overlapping paths before downloading or mutating the installation so the stable shim directory remains separate from transactional payload and pointer directories.
Artifacts
Focused source repro for the missing overlap guard
- Node source for the executed focused repro that checks the installer preflight and transaction ordering; it demonstrates that no overlap guard exists.
Focused source repro output showing no overlap guard
- Captured output of the focused repro run from /home/user/repo; it reports an absent overlap preflight and the current-to-bin mutation order, confirming the unsafe topology reaches mutation code.
Modeled Windows overlap-path validation source
- Node source for the executed Windows-path model covering current, current bin, versions, and versions bin overlap candidates; it models the unsafe configurations.
Modeled Windows overlap-path validation output
- Captured output of the overlap-path model run from /home/user/repo; it shows every tested candidate overlaps a transaction-owned tree and the source has no rejection.
Windows installer unit-test output on Linux
- Captured targeted Vitest execution from /home/user/repo; 13 tests passed and 17 Windows-runtime tests were skipped, so existing tests do not exercise this topology here.
Windows runtime availability check
- Captured runtime availability check from /home/user/repo; pwsh, Windows PowerShell, Wine, Docker, Podman, and QEMU are absent on Linux, which blocks native execution.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: install.ps1
Line: 542-548
Comment:
**Transaction-owned directories can be used as the shim location**
`ATOMIC_BIN_DIR` is normalized but never checked against `<installRoot>\current` or `<installRoot>\versions`. If it points to either tree or one of their descendants, the installer replaces transaction-managed version/current entries and then creates `atomic-current` and `atomic.cmd` within that same tree. A `current`-nested directory resolves through the junction into the installed version payload, while a `versions`-nested directory can be moved during version replacement. Reject overlapping paths before downloading or mutating the installation so the stable shim directory remains separate from transactional payload and pointer directories.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reject a PATHEXT that omits .CMD instead of reporting a success where bare atomic cannot resolve the installed shim. Enforce Atomic's release tag grammar for PowerShell explicit refs and resolved latest tags before any archive request, matching install.sh and the Windows docs. Report an unexpected regular current entry under ATOMIC_INSTALL_DIR rather than moving or deleting caller data, alongside the existing atomic-current guard. Initialize transaction cleanup state before the outer try so preflight and API failures always surface their own error. Assistant-model: Claude Opus 5
Implementation notes (full artifact)Source artifact: Implementation NotesTask: Continue issue #2159 on the existing branch feat/2159-curl-irm-installers in /Users/norinlavaee/atomic-curl-irm and repair the final review findings from draft PR #2187. Freeze scope to these concrete items: (1) install.ps1 must detect and reject before download any pre-existing same-stem launcher such as ATOMIC_BIN_DIR\atomic.exe that Windows PATHEXT could resolve ahead of the installed atomic.cmd shim; add PowerShell 5.1-compatible static and native-gated regression tests. (2) install.sh must reject before network or filesystem mutation every impossible ATOMIC_BIN_DIR nested under transaction-owned paths such as ATOMIC_INSTALL_DIR/current or ATOMIC_INSTALL_DIR/versions/; add POSIX fixture tests proving zero requests and no mutation. Also fix the two directly relevant non-blocking correctness defects if reproduced: accept GNU sha256sum binary rows of the form HASH *asset, and restore a normal umask before creating install/bin/version payload paths so only temporary token/download state stays owner-only. Evaluate the Alpine APK URL durability note, but do not redesign release infrastructure unless a current deterministic failure violates issue #2159. Do not add concurrency locking, zero-downtime swap work, arbitrary Git-ref support, UNC support, pre-download TLS work, archive self-update, or other new scope. Remove temporary issues.md when resolved. Run focused installer suites, CI contracts, npm run check, container tests, and diff checks; commit and push focused repairs. Update the existing draft PR #2187 with accurate results and mark it ready only if all objective-required findings are resolved and fresh reviewers approve. Iteration C — four Windows PowerShell 5.1 repairsContract amendments receivedThe research artifact carried a new user amendment, adopted as required behavior (verbatim):
The standing inherited clause about POSIX physical/dangling containment was restated too; the research Reproduction before repair (real PowerShell 7.4,
|
| Finding | Reproduced? | Observed |
|---|---|---|
| 1 — cleanup state initialized too late | No | The preflight throw surfaced verbatim (ATOMIC_BIN_DIR contains atomic.exe, which PATHEXT resolves before atomic.cmd…) and the API throw surfaced verbatim (Failed to query GitHub release API at …). Control flow explains it: the transaction finally belongs to the inner try, entered only after the initialization block, so a preflight or API throw never reached it. |
2 — PATHEXT without .CMD |
Yes | PATHEXT=".EXE;.BAT" proceeded to the GitHub API instead of failing. |
| 3 — no tag grammar | Yes | v1.0.0, 1.0, 1.0.0-alpha.0, 1.0.0-beta.1, release/1.0 all reached the tags API. |
4 — regular installRoot\current |
Yes | A regular current directory holding a marker file proceeded to the API with no preflight rejection. |
Finding 1 is an amendment clause, so I implemented the hardening anyway: it is memory-only, it makes
the invariant structural rather than incidental, and the error-preservation tests are worth having
either way. I did not observe the defect it describes, and say so rather than claiming a fix for
something I could not reproduce.
Repairs (install.ps1)
- Cleanup state before the outer
try.$tempDir,$versionStagePath,$currentNextPath,
$atomicCurrentNextPath,$shimNextPath,$transaction,$transactionCommitted,
$transactionMissingDirectories, and$rollbackRetryLimitare initialized once, before
$previousSecurityProtocol; the duplicate block after the download base was removed. The temp
cleanup became null-safe ($null -ne $tempDir -and (Test-Path …)). Nocatchwas added, so the
original error record still propagates unchanged. - Missing
.CMD.Get-AtomicShimShadowingExtensionstracks$cmdSeen, sets it at the.CMD
break, and throws when the effectivePATHEXTnever lists.CMD. The call site is the existing
preflight, so the throw lands before$apiHeaders, the temp directory, and every download.
Normalization becameTrim().Trim('"').Trim()so a quoted entry with internal whitespace still
matches. Empty or whitespacePATHEXTstill falls back to the stock list, which contains.CMD. - Tag grammar. New
Test-AtomicReleaseTaguses
^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-alpha\.(?:[1-9][0-9]*))?$with
-cmatch, mirroringinstall.sh:is_atomic_release_tagexactly: no leading zeros, only an-alpha.
prerelease, positive alpha revision. The requested ref is validated right after it is resolved from
-Ref/ATOMIC_VERSION(before the API headers and the tags request); the resolved tag — from the
redirect or the API — is validated before$releaseBase. Both throw the POSIX wording:
unsupported release tag: expected MAJOR.MINOR.PATCH or MAJOR.MINOR.PATCH-alpha.REVISION. - Transaction pointer types.
$currentPathand$existingCurrentItemare computed in the
preflight, and a non-reparsecurrententry is rejected there. Reparse points (the installer's own
junction, including a dangling one) still flow into the existing repair/rollback path, and the
atomic-currentguard is unchanged. Nothing is moved or deleted.
Tests
- Always-on static (
test/unit/install-powershell.test.ts): extended the PATHEXT shadow test with
$cmdSeenand missing-.CMDordering; addedWindows installer initializes cleanup state before preflight and API resolution(each declaration appears exactly once and precedes the outer try),
Windows installer enforces Atomic release grammar before archive downloads, andWindows installer protects transaction pointer types before I/O. - Engine-agnostic runnable probe: new
preflightGuardProbeHarnessplus a per-engine test. It is
fully offline — every case fails before any request — and covers missing.CMD, ten invalid explicit
refs, an invalidATOMIC_VERSION, a regularcurrentdirectory and file with marker preservation,
and preflight error-text preservation. A localpwsh7.6.4 exists on this host now, so it actually
ran:PowerShell 7 enforces PATHEXT, tag grammar, and pointer guards before any requestpasses,
and againstgit show 0ea73cc3:install.ps1it fails with
PATHEXT without .CMD failed for the wrong reason: Failed to query GitHub…. - Native Windows PowerShell 5.1 fixture scenarios:
missing-cmd-pathext(rejection plus.cmd;.EXE,
" .CMD " ; ".EXE",.EXE;.CMD;.BAT, and unset positive controls),tag-grammar(ten invalid
refs, invalidATOMIC_VERSION, valid1.0.0and1.0.0-alpha.1, invalid latest redirect tag,
invalid latest APItag_name),pointer-conflicts(regularcurrentandatomic-current, each as
directory and file, with marker preservation, plus an installer-owned junction control upgrading
1.0.0 → 2.0.0), andpreflight-errors(blocker and API error text preserved, no strict-mode variable
error). The harness now pins$env:PATHEXTto the stock default so scenarios do not inherit runner
configuration. - Replaced the
escaped-refsscenario and its static test. It asserted thatrelease/1.0,
hash#tag, andpercent%taginstall successfully, which the amendment now forbids; its encoding
coverage is unreachable once only semver tags are accepted.ref-identitymoved from the non-semver
requested-aliasto1.0.1, since the grammar check would otherwise reject it before the identity
mismatch could be exercised. - CI contract (
test/ci/installers-safety-contract.test.ts): newWindows safety and version checks all precede temp creation and archive downloadsasserting all seven orderings the research listed,
plus that the pointer preflight contains noMove-Item/Remove-Item/New-Item. - Docs:
packages/coding-agent/docs/windows.mdgained the missing-.CMDand pointer-preservation
sentences, with two matching assertions in the docs contract. That file's existing tag-grammar
sentence was aspirational for PowerShell until this change and is now true.
Iteration C validation
| Command | Observed |
|---|---|
pwsh parser on install.ps1 and on the extracted fixture harness |
PARSE_OK, HARNESS_PARSE_OK |
Engine-agnostic preflight probe under pwsh 7.6.4 |
PREFLIGHT_GUARDS_OK (fails against 0ea73cc3) |
npx vitest --run --project unit test/unit/install-shell.test.ts test/unit/install-powershell.test.ts |
46 passed, 17 skipped (was 41/14) |
npm run test:ci-contracts |
56 passed (was 55) |
npm run check |
pass (biome, tsc --noEmit, shrinkwrap) |
npm run test:unit |
5890 passed, 18 skipped (626 files) |
bash scripts/test-installers-containers.sh |
pass on stock alpine:3.22 and debian:bookworm-slim |
sh -n install.sh, dash -n install.sh, git diff --check |
pass |
install.sh was not modified this iteration.
Iteration C deferred
cmd.exepercent expansion atinstall.ps1:762-763(both reviewer P3 findings). The research
states plainly: "This is fail-closed, outside the frozen scope, and must not be fixed in this repair
cycle." Not touched.- Alpine apk revision-bump resilience; concurrency locking; arbitrary Git refs; UNC paths; pre-download
TLS changes; archive self-update; release-infrastructure redesign.
Iteration B (verification iteration) — HEAD 0ea73cc
Research artifact status
research/2026-08-04-continue-issue-2159-...md is 72 bytes and contains exactly one line:
Full unit suite passed: 5,882 tests passed, 17 skipped across 626 files. That is a validation
statistic, not a findings list — the research stage again failed to record findings (0 bytes last
iteration, one stray line this one).
I recovered the authoritative findings from the review artifact instead:
…/artifact-4b319e7a-dc06-47bf-974e-d86f87832936/review-round-latest.json
(convergence_decision: approved=false, stopReviewLoop=false, nextAction=implementation;
6 consolidated findings, 5 blocking). No newer review artifact exists — the only file in the run
directory newer than that round is this stage's own transcript.
Branch state found at start
git log shows six commits landed after my previous iteration's 87874c63, all authored by a
later implementation cycle (Assistant-model: GPT-5.6 Luna):
0ea73cc3 fix(installer): support PowerShell 5.1 redirects
266c8a56 test(installer): honor common PowerShell parameters
7c3884bb test(installer): support Expand-Archive parameter aliases
d0c6ad43 fix(installer): cover preflight and hash fallbacks
75bfa390 fix(installer): support missing PowerShell hash cmdlet
52351057 fix(installer): close final review blockers
87874c63 fix(installer): refuse shadowed shims and transaction-owned bin paths <- my prior cycle
Working tree clean; local HEAD == origin/feat/2159-curl-irm-installers == 0ea73cc3.
PR #2187 is now OPEN and no longer a draft; every required check on 0ea73cc3 is SUCCESS.
Because the review round predates those six commits, this iteration's job was to verify each
finding independently against HEAD rather than re-implement, and to repair anything still
reproduced. Nothing was still reproduced, so this iteration made no code changes.
Finding-by-finding verification at HEAD 0ea73cc
| # | Finding (from review-round-latest.json) |
Status | My evidence |
|---|---|---|---|
| 1 | [P1, reviewer-a] Container test always fails cleanup on native Linux, keeping static-checks red |
Resolved | scripts/test-installers-containers.sh now passes --user "$(id -u):$(id -g)" to both docker run calls. CI job 92167163701 (static-checks (linux-x64), run 30960725545, HEAD 0ea73cc3) ran ./scripts/test-installers-containers.sh, logged both Atomic 1.0.0 installed successfully. lines, contains no Permission denied or rm: cannot remove, and concluded success. |
| 2 | [P1, reviewer-a] Dangling symlink components bypass the transaction-owned containment | Resolved | New reject_dangling_symlink_path in install.sh, called for both ATOMIC_INSTALL_DIR and ATOMIC_BIN_DIR after normalization. My own probe (below) reproduces the reviewer's exact scenario: RED at 87874c63, GREEN at HEAD. |
| 3 | [P1, reviewer-b] Same container cleanup defect | Resolved | Same as #1. |
| 4 | [P1, reviewer-b] Native PowerShell 5.1 fixtures unrunnable — Get-FileHash not recognized, 13 failures |
Resolved | New Get-AtomicFileSha256 falls back to [Security.Cryptography.SHA256] when Get-Command Get-FileHash returns nothing. CI job 92167163390 (suites (windows-x64)) ran test/unit/install-powershell.test.ts with 33 tests, all passing on real Windows PowerShell 5.1, including PowerShell 5.1 fixture installs when Get-FileHash is unavailable, PowerShell 5.1 fixture refuses same-stem launchers that PATHEXT resolves before the shim (the shadowed-shim scenario I authored last iteration), and PowerShell 5.1 fixture honors custom PATHEXT order when .CMD precedes .EXE. |
| 5 | [P2, reviewer-b] Dangling aliases into transaction-owned paths | Resolved | Same as #2; my probe covers both the versions and current alias variants. |
| 6 | [P3, reviewer-a, beyond_objective, confidence 0.55] Final smoke sends the shim path through cmd.exe unescaped, so a %…% token in ATOMIC_BIN_DIR is environment-expanded |
Deferred, not repaired | The code pattern is confirmed present at install.ps1:762-763 ($shimCommand = '"' + $shimPath + '" --version' then & $env:ComSpec /d /c $shimCommand). I could not execute it — no Windows and no cmd.exe on this host. Reasoning for deferring is below. |
Dangling-alias probe (findings 2 and 5) — RED/GREEN
Stub curl on PATH logging every URL; ATOMIC_INSTALL_DIR=$ROOT where $ROOT does not exist;
outside/versions-alias -> $ROOT/versions and outside/current-alias -> $ROOT/current created as
dangling symlinks; ATOMIC_BIN_DIR=outside/<alias>/bin.
- RED at
87874c63(git show 87874c63:install.sh), both aliases:
error: failed to resolve the GitHub release, request log
GET https://api.github.com/repos/bastani-inc/atomic/releases/tags/1.0.0— the run reached the
network, exactly as the reviewer reported. - GREEN at HEAD, both aliases: exit 1,
error: ATOMIC_BIN_DIR contains an unresolved symbolic link; refusing an unresolvable path: …,
empty request log, and$ROOTnever created. - Over-rejection control: with
outside/live-alias ->an existing directory, the installer
passes the preflight and proceeds to the network (fails only on the stub), so a live symlinked
bin directory is still accepted.
Windows shadow rejection re-proved after a semantics change
Get-AtomicShimShadowingExtensions changed between my last verification and HEAD: it previously
unioned $env:PATHEXT with the stock list, and now uses the effective $env:PATHEXT alone, falling
back to the stock list only when PATHEXT is unset or blank. That is the more accurate rule — the
union could reject an atomic.exe in a session whose PATHEXT puts .CMD first, which is a false
rejection, and the new native test honors custom PATHEXT order when .CMD precedes .EXE pins the
corrected behavior. I re-ran the persisted probe harness (extracted verbatim from
test/unit/install-powershell.test.ts) against the current install.ps1 under real PowerShell 7.4
with --network none: SHIM_SHADOW_OK for atomic.exe, atomic.com, atomic.bat, ATOMIC.EXE,
a same-stem directory, and a custom PATHEXT=".WSF;.CMD;.EXE" with atomic.wsf. install.ps1
parses clean (PARSE_OK).
Prior-cycle repairs still present at HEAD
Verified by inspection: for owned_child in current versions preflight (install.sh:269-281),
ORIGINAL_UMASK=$(umask) / umask 077 (388-389) with umask "$ORIGINAL_UMASK" (833) before the
extract and every install-path mkdir, and the GNU binary-row pattern \**) (803). The container
fixture now writes HASH *asset rows, so the stock Alpine/Debian smoke exercises binary-mode
checksums end to end.
Validation run this iteration (all at HEAD 0ea73cc, no local changes)
| Command | Observed |
|---|---|
sh -n install.sh, dash -n install.sh |
pass |
PowerShell parser on install.ps1 (docker pwsh 7.4) |
PARSE_OK |
Persisted shadow probe harness under docker pwsh 7.4, --network none |
SHIM_SHADOW_OK |
npx vitest --run --project unit test/unit/install-shell.test.ts test/unit/install-powershell.test.ts |
41 passed, 14 skipped |
npm run test:ci-contracts |
55 passed |
npm run check (biome + tsc --noEmit + shrinkwrap) |
pass |
npm run test:unit |
5885 passed, 15 skipped (626 files) |
bash scripts/test-installers-containers.sh |
pass on stock alpine:3.22 BusyBox ash and debian:bookworm-slim |
git merge-tree --write-tree HEAD origin/main |
exit 0, no conflicts (origin/main 43557299, merge base f9e672e4) |
ls issues.md |
absent |
git status --short |
clean |
CI at HEAD (run 30960725545): all nine jobs success — static-checks (linux-x64),
suites/agent-suite/test/release-archive on both linux-x64 and windows-x64.
Deferred (outside the contract, not implemented)
- Finding 6,
cmd.exepercent expansion in the final smoke. The pattern is real at
install.ps1:762-763, but I did not repair it: the raising reviewer marked it
beyond_objectiveat confidence 0.55 and wrote that "it should not gate this change"; the
objective freezes scope to the two blockers plus the checksum and umask defects; it fails closed
(rolls back rather than installing something wrong) and needs a bin directory containing a
literal%NAME%pair naming a defined variable; and a new commit would invalidate a fully green
CI run on a PR that is already out of draft. Recorded here so the decision is visible rather than
silently dropped. - Alpine apk revision-bump resilience in
scripts/build-binaries.sh. Latent; all four pinned URLs
returned HTTP 200 when checked last iteration. - Carried forward: concurrency locking and gap-free launcher availability, arbitrary
quote-containing Git refs, UNC support, pre-download TLS repair for the literalirmbootstrap,
atomic updatefor archive installs. - A docs clause naming the new
unresolved symbolic linkmessage. Not added: the existing
quickstart/README wording ("impossible layouts fail before any download or filesystem change")
already covers it truthfully, andwindows.md's PATHEXT sentence is more accurate under the
effective-PATHEXT implementation than it was under the union. No documentation is stale or wrong.
Contract amendments received
None. No user steering or follow-up message arrived during this iteration. The cycle-7 amendment
quoted in earlier notes remains inherited and binding; findings 2 and 5 repaired in the prior cycle
are its "reject impossible install/bin containment before I/O" clause.
PR status
PR #2187 is OPEN, out of draft, head 0ea73cc3, all required checks SUCCESS, reviewDecision
empty (no submitted human review). I did not edit the PR: the acceptance criteria reserve PR
action for create_pr=true, and this stage's constraints say to ignore requests to submit a PR.
A previous implementation cycle already updated the body and marked it ready.
QA E2E Video
Not applicable. This is a terminal-only CLI installer with no web, frontend, or TUI surface, so
playwright-cli video does not apply and none was produced. The end-to-end evidence is the stock
container installs (alpine:3.22, debian:bookworm-slim) locally and in CI, the network-disabled
real PowerShell 7.4 probes, and — new this iteration — 33 passing native Windows PowerShell 5.1
fixture tests in CI job 92167163390.
Iteration A (previous, HEAD 87874c6) — retained for history
Repairs
- [BLOCKING] Windows same-stem PATHEXT shadow (
install.ps1). Added
Get-AtomicShimShadowingExtensionsplus a preflight that throws before the API headers are
built. Same-stem directories are rejected too, becauseSearchPathmatches directories.
Nothing is deleted — the installer reports and stops. - [BLOCKING] POSIX bin path under transaction-owned install paths (
install.sh). Preflight
rejectingBIN_PATH/PHYSICAL_BIN_PATHat or under<root>/currentor<root>/versions, for
both normalized and physical roots, before the required-command probe, the temp directory, and
any request.$INSTALL_ROOTand$INSTALL_ROOT/binstay accepted (positive control). - GNU binary checksum row.
case $checksum_name in \*)→\**). - umask spillover. Capture the original umask, keep
077for the temp directory, download and
token file, restore it immediately beforemkdir "$EXTRACT_ROOT". - Alpine APK durability. Evaluated only; all four pinned URLs returned HTTP 200, so no current
deterministic failure and no redesign.
RED/GREEN receipts (iteration A)
- POSIX containment: RED reproduced reviewer-b's exact
Too many levels of symbolic links/installed atomic --version check failedafter all three
requests. GREEN: rejected with an empty request log and empty TMPDIR. - GNU binary row: RED
error: SHA256SUMS must contain exactly one row for atomic-linux-x64.tar.gz. - umask: RED stock Alpine
drwx------for bin and install root; GREENdrwxr-xr-x, payload
-rw-r--r--. - Windows shadow: RED
atomic.exe failed for the wrong reason: Failed to query GitHub release API;
GREENSHIM_SHADOW_OK.
Iteration A validation
Focused installers 38 passed / 15 skipped; CI contracts 54 passed; npm run check pass; unit 5882
passed / 16 skipped; integration 485 passed; coding-agent 3062 passed / 29 skipped (one
load-sensitive flake in model-registry-dynamic-providers that also passed in isolation and on a
clean tree, untouched by the delta). Committed 87874c63, nine files, +454/-7.
The Windows unit job's first attempt failed because a completed install left an atomic-install-* directory in the fixture temp root: install.ps1 removed $tempDir with one suppressed recursive Remove-Item and never checked whether the directory was actually gone, so a transient Windows handle on the staged atomic.exe left residue without changing the installer result. Remove-AtomicTemporaryDirectory now clears read-only attributes, removes the tree with a terminating Remove-Item, verifies absence, falls back to [IO.Directory]::Delete($Path, $true), verifies again, and retries under a named finite limit with linear backoff. When the budget is exhausted it throws with the exact path, the attempt count, and the last exception. A cleanup failure is captured rather than thrown in place, so rollback, staged-pointer cleanup, and created-parent cleanup all still complete. If the install already failed, the original error is rethrown and the cleanup evidence is a warning; otherwise the cleanup error is thrown. Coverage: always-on source contracts in the unit suite and the CI installer safety contract, plus a native Windows PowerShell 5.1 temp-cleanup fixture that opens a real FileShare.None handle inside the temp root, releases it only after a genuine removal failure, exhausts the budget with a permanent handle, proves the primary installer error survives a failing cleanup, and asserts zero residue after repeated successful and rolled-back installs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Implementation notes artifact for this run, reproduced in full as the last handoff action. Review did not converge (2 iterations); one blocking P2 finding at Implementation NotesTask: Repair the single remaining blocker on draft PR #2187 in /Users/norinlavaee/atomic-curl-irm on branch feat/2159-curl-irm-installers. Freeze scope to Windows temporary-directory cleanup in install.ps1 and its native PowerShell 5.1 regression. At committed HEAD 032d6be, fresh Windows CI job 92181276918 failed its first unit attempt because the missing-cmd-pathext fixture root retained atomic-install-* residue; only the bounded whole-suite retry passed. install.ps1 currently removes $tempDir in finally with Remove-Item -Recurse -Force -ErrorAction SilentlyContinue after executing staged atomic.exe from that directory. Reproduce the race or create a deterministic one-shot deletion-failure probe, implement bounded verified temp-dir removal that handles delayed Windows process/file handles, preserves the original installer error when cleanup also fails, reports incomplete cleanup clearly if retries exhaust, and does not weaken rollback or caller-owned path safety. Add focused always-on contracts plus native Windows PowerShell 5.1 regression/stress coverage. Do not change any other installer behavior or add new findings/scope. Run focused installer tests, CI contracts, npm run check, Windows-native CI without relying on the suite retry, container smoke, and diff checks. Commit and push the focused repair, update PR #2187 accurately, and mark it ready only if fresh first-attempt Windows evidence and both reviewers approve. Running Notes
Root cause: corrected from the research artifact
Acceptance matrix
Implementation
Validation (this session)
The local probes ran under pwsh 7 on macOS; they prove the helper's control flow, the fixture wrapper's Decisions and deferrals
QA E2E VideoNot applicable, and no video was produced. This change touches Signing noteThe commit is unsigned. Fresh Windows CI evidence (first attempt, no suite retry)Commit
The run's single retry annotation belongs to a different job, Iteration 2: no new findings to repairThe research artifact was rewritten to a single line for this iteration:
There are no consolidated unresolved findings in it, so there was no finding batch to repair. State
Re-verification against the committed checkout (this session)
Divergence audit against the current checkout
Verified by inspection of Deferred: greptile P1 comment, out of frozen scope
The claim is factually accurate. It is not repaired in this iteration, deliberately. The objective freezes scope to "Windows temporary- |
Assistant-model: GPT-5.6 Sol
Summary
Adds standalone POSIX shell and Windows PowerShell installers that install Atomic from verified GitHub Release archives without npm, pnpm, Yarn, Bun, or Node.js.
Changes
install.shandinstall.ps1entry points with stable/latest and pinned-release supportSHA256SUMSbefore changing an existing installValidation
npm run checknpm run test:unit -- test/unit/install-powershell.test.ts test/unit/install-shell.test.ts— 48 passed, 18 platform-gated skipsnpm run test:ci-contracts— 57 passednpm run hooks:runNotes
6c01448acompletes.atomic updateremains a separate follow-up.Closes #2159