Skip to content

refactor(tools): move mise bootstrap logic to Go #813

Description

@tbhb-agent

Problem

The bootstrap entry point must work before mise or a Go toolchain is installed, but its installation logic remains shell. It selects a cached version and installation path, resolves platform/archive/checksum inputs, installs mise and invokes it (tools/mise-bootstrap:4, tools/mise-bootstrap:56, tools/mise-bootstrap:142, tools/mise-bootstrap:254, tools/mise-bootstrap:294, tools/mise-bootstrap:402), on origin/main at a0347a715fab580ece45edfcb3d13509620459a8.

This split chooses Go migration with a temporary frozen compatibility path, rather than a permanent bootstrap exception to the shell rule. It must preserve the first-run path when the replacement binary is not yet available.

This is the bootstrap split of #332. Part of #588.

Acceptance criteria

  1. AC1: Version and installation-path selection equal the frozen command's under the existing defaults and environment overrides.
  2. AC2: An existing file at the selected installation path takes the same cache-hit path as today, without downloading or silently enforcing a different version policy.
  3. AC3: Platform and archive selection preserve the frozen command's supported OS/architecture/libc outcomes and unsupported-platform refusals.
  4. AC4: Download-source and checksum selection honor the same configuration precedence and requested version as the frozen command.
  5. AC5: Failed download, checksum retrieval or checksum validation never results in a newly installed executable or successful bootstrap result.
  6. AC6: A successful installation produces the same executable bytes, mode decisions, diagnostics and temporary-artifact cleanup as the frozen command for equivalent inputs.
  7. AC7: Failed platform, installed-version, archive-capability, checksum-tool, cache-path or user-ID probes remain failures even after printing plausible output.
  8. AC8: Invoking the installed mise preserves the caller's invocation name, arguments, input/output streams, working directory and terminal exit status.
  9. AC9: Non-functional: when the released replacement command is already available, bootstrap works without a Go toolchain or an existing mise installation.
  10. AC10: The existing entry point ends as a thin shell wrapper that probes the Go subcommand without side effects and falls back to the frozen shell only when that capability is absent, including first run without any replacement binary.
  11. AC11: A supported Go failure never triggers shell fallback, repeated installation or a repeated installed-command invocation.
  12. AC12: Non-functional: release N-2 bootstrap invocations and environment settings remain supported without a raised binary floor, new required configuration or loss of an existing platform's fallback path.

Scope and non-goals

Preserve cache/version/path precedence and leading-version normalization (tools/mise-bootstrap:5); Linux/Darwin, existing architecture and musl selection (tools/mise-bootstrap:56, tools/mise-bootstrap:67); explicit archive format and older-version format behavior (tools/mise-bootstrap:99); archive capability decisions (tools/mise-bootstrap:112); checksum-tool selection and pinned/dynamic checksums (tools/mise-bootstrap:132, tools/mise-bootstrap:142); download alternatives and error reporting (tools/mise-bootstrap:254); installed-version parsing and opt-in matching-version skip behavior within installation (tools/mise-bootstrap:285, tools/mise-bootstrap:309); requested-source precedence (tools/mise-bootstrap:319); extraction, directory-path refusal, root-specific mode/ownership behavior, final move and success cleanup (tools/mise-bootstrap:327); debug/quiet diagnostics (tools/mise-bootstrap:14); and returning to the invoking directory before forwarding (tools/mise-bootstrap:396).

The public entry point currently skips installation whenever the selected path is already a file (tools/mise-bootstrap:399). Do not confuse that contract with the installation helper's narrower optional version check. Keep the helper's failed-version-probe behavior covered without changing the public cache policy.

The entry point suppresses shell-activation help (tools/mise-bootstrap:398). Migrating the unused activation-help variants at tools/mise-bootstrap:365 is a non-goal: this entry point exposes none of them. Preserve the suppression rather than adding activation instructions or editing shell startup files.

Non-goals: a permanent shell exemption, new supported platforms, expanding #333's release-target inventory, a new binary-acquisition mechanism, changing the pinned mise version, stronger cache-version policy, new checksum trust policy, automatic retries, atomic-install redesign, or adding failure cleanup promises beyond the frozen command's behavior. The existing thin compatibility paths for unrelated helpers remain outside this split. Fallback retirement is separate D9 work and must wait until every supported bootstrap environment has a viable replacement path.

Approach

Write failed-probe and no-install-on-invalid-input checks first. Move the installation and selection behavior to Go and compare it against frozen shell using local fixture archives and controlled platform responses. Existing first-run callers must still bootstrap when no replacement command is available. The pull request decides implementation details; it does not introduce a permanent exception or another release distribution interface.

Dependencies

Depends on #542, following #333 → #544 → #382 → #545 → #546 and the #359 release chain. #333 and #544 establish released binaries and normal installation; this split must not assume those installations already exist on first bootstrap. #332 records this migration decision for #334, which owns later shell-limit enforcement. Part of #588.

Size

M. One pull request for the bootstrap behavior, thin compatibility entry point and installation fixtures.

Verify plan

  • AC7: first port all bootstrap regressions in tests/tools/masked-status.bats:123, tests/tools/masked-status.bats:137, tests/tools/masked-status.bats:168, tests/tools/masked-status.bats:177 and tests/tools/masked-status.bats:195. For each probe, return plausible output then fail; assert the failure propagates, no untrusted value is accepted and no new executable is installed after the failure.
  • AC5: first test failed downloads, empty/failed checksum retrieval and mismatched checksums against fixture archives; assert nonzero status, no success diagnostic and no newly installed executable. No test contacts a real download service.
  • AC1: default/overridden version, leading v, cache-root and explicit-install-path fixtures compare selected paths and versions with frozen shell.
  • AC2: existing-file, absent-file and nonexecutable-cache fixtures compare whether installation runs and the final invocation outcome. Separately cover the helper's optional matching-version branch and rejected failed-version probe; do not add version enforcement to the public cache hit.
  • AC3: enumerate every current OS/architecture/libc outcome, explicit OS/architecture/archive overrides, older-version archive behavior and unavailable archive capability. Compare selected artifacts and unsupported-platform refusals; verify existing platforms outside the released replacement inventory still take the frozen fallback when needed.
  • AC4: default-source, source override, explicit alternate-source request, alternate-version request, pinned and fetched checksum fixtures compare request selection and precedence. Cover both existing download alternatives and both checksum-tool alternatives, with missing-tool refusals.
  • AC6: local archive fixtures cover both supported formats, native and alternate extraction paths, directory-valued destination refusal and root/nonroot mode decisions. Compare installed bytes/modes, stdout/stderr, status and residue after success. Compare failure behavior without inventing rollback or cleanup guarantees.
  • AC8: a recording fake installed command checks invocation name, spaced arguments, stdin, separate output streams, working directory and nonzero exit propagation. Successful installation and cache-hit paths emit no activation-help output.
  • AC9: provide the released replacement command while making Go and mise unavailable; install and invoke only a fixture mise. Assert neither build tooling nor mise is needed to execute bootstrap selection/installation.
  • AC10: no replacement binary, an older binary without the capability and a supported replacement each take the expected path once; assert the read-only probe neither downloads nor installs.
  • AC11: supported failure, failure after partial installation and an indeterminate capability probe all return failure with zero shell fallback, zero repeated installation and zero repeated installed-command invocation.
  • AC12: compare N-2 environment/argument fixtures on absent, older and new binary paths. Run Go tests for the platform-selection matrix on Linux, macOS and Windows, with native fixture installation and wrapper smoke coverage on supported bootstrap systems. Run focused tests, mise run lint and mise run test on the reviewed head. All subprocesses, downloads and ownership-sensitive operations are isolated fixtures or stubs.
  • Definition of done: D1 each AC is verified at the merged head, with the commands and results cited in the pull request's Verification. D2 an independent review approves the exact merged head, and every finding has a recorded disposition. D3 required checks pass with no gate loosened. D4 the leak scans the repository specifies are clean, and every post made for this work (comments, descriptions) passes the per-post leak review before it is posted. D5 every commit is signed. D6 the documentation matches, or the description says No doc impact with the reason. D7 every commit carries an Assisted-by trailer and the pull request carries the provenance:agent-assisted label. D8 the change stays within the pull request size limits. D9 follow-up work, including deferred review findings, is filed as issues, each with a Problem section and numbered acceptance criteria and linking this issue; the pull request's Related section lists them, and any code that defers work links its issue. D10 the merge closes this issue through a closing reference, the project board shows the state Done, and a final Status: comment names the merge commit.

Risks

A replacement that requires mise to launch cannot bootstrap mise. First-run and absent-binary fixtures prevent that cycle. Cache reuse, musl/archive selection and failure handling can also drift; the frozen matrix covers them. Undo: revert the port. Keep the fallback frozen until N-2 support and bootstrap availability permit separate retirement.

Operator authority

None.

Activity

  1. added
    change:featPR adds or changes a feature (Conventional Commit type feat)
    on Oct 26, 2026
  2. tbhb-agent commented on Oct 26, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — #813 accurately specifies the Go bootstrap port and frozen shell fallback contract
    Model: gemini-4-argon-high

    Checked

    • Live issue and baseline: Read live tbhb-dev/devtools#813 (0 comments) and referenced issues #332, #588, #333, #542, #544, #382, #545, #546, #359, and #334. Verified origin/main is at a0347a715fab580ece45edfcb3d13509620459a8.
    • Code citations against origin/main:
      • tools/mise-bootstrap:4 (__mise_bootstrap entry point) and tools/mise-bootstrap:5 (XDG_CACHE_HOME default, MISE_VERSION default 2026.8.0, leading-v strip at tools/mise-bootstrap:7, and MISE_INSTALL_PATH export at tools/mise-bootstrap:8).
      • tools/mise-bootstrap:14 (MISE_DEBUG and MISE_QUIET logging functions debug, info, warn, error, and unsupported_arch through tools/mise-bootstrap:53).
      • tools/mise-bootstrap:56 (get_os mapping Darwin to macos and Linux to linux) and tools/mise-bootstrap:67 (get_arch handling MISE_INSTALL_MUSL, uname -o Android, ldd /bin/ls musl detection, x86_64/aarch64/arm64/armv7l, and unsupported_arch).
      • tools/mise-bootstrap:99 (get_ext honoring MISE_INSTALL_EXT, ^v2024 tar.gz fallback, and tar_supports_zstd) and tools/mise-bootstrap:112 (tar_supports_zstd checking tar --version, zstd, bsdtar, busybox, and GNU tar >= 1.31).
      • tools/mise-bootstrap:132 (shasum_bin selecting shasum or sha256sum) and tools/mise-bootstrap:142 (get_checksum using static SHA-256 checksums for v2026.8.0 or fetching SHASUMS256.txt via curl/wget).
      • tools/mise-bootstrap:254 (download_file using curl or wget with error reporting).
      • tools/mise-bootstrap:285 (installed_mise_version parsing the first field of mise version and stripping leading v), tools/mise-bootstrap:294 (install_mise), and tools/mise-bootstrap:309 (MISE_INSTALL_SKIP_IF_EXISTS opt-in version-matching skip).
      • tools/mise-bootstrap:319 (tarball source precedence across non-current version, MISE_INSTALL_FROM_GITHUB, MISE_TARBALL_URL, and default https://mise.jdx.dev).
      • tools/mise-bootstrap:327 (temporary download/extract directories, checksum validation, directory-valued MISE_INSTALL_PATH refusal at tools/mise-bootstrap:338, extraction with zstd pipe fallback at tools/mise-bootstrap:345, id -u root chown 0:0/chmod 755 at tools/mise-bootstrap:350, move to MISE_INSTALL_PATH, and cleanup through tools/mise-bootstrap:360).
      • tools/mise-bootstrap:365 (after_finish_help), tools/mise-bootstrap:396 (cd -- "$initial_working_dir"), tools/mise-bootstrap:398 (local MISE_INSTALL_HELP=0 suppressing activation help), tools/mise-bootstrap:399 (test -f "$MISE_INSTALL_PATH" || install), and tools/mise-bootstrap:402 (exec -a "$0" "$MISE_INSTALL_PATH" "$@").
    • Test citations against origin/main:
      • tests/tools/masked-status.bats:123 (get_os and get_arch failing after printing valid uname/ldd output).
      • tests/tools/masked-status.bats:137 (installed_mise_version rejecting output from a failing binary).
      • tests/tools/masked-status.bats:168 (tar_supports_zstd rejecting output from a failing tar).
      • tests/tools/masked-status.bats:177 (install_mise stopping before get_checksum when shasum_bin fails).
      • tests/tools/masked-status.bats:195 (install_mise stopping when dirname or id -u prints output and fails).
    • Binary-first / thin-wrapper contract and N-2 compatibility:
      • AC10, AC11, and AC12 match the shared thin-wrapper contract across #332 and #329: side-effect-free capability probe (no downloads or writes during probing), fallback to the frozen shell implementation only when the Go capability or binary is absent (including first run and platforms outside #333's four release targets linux/amd64, linux/arm64, darwin/arm64, windows/amd64), and zero shell fallback or repeated execution after any supported Go failure or indeterminate probe.
      • Preserves N-2 compatibility (docs/compatibility.md:3) without prescribing package/file layout or implementation details.
    • Dependencies, size, and operator authority:
      • Dependencies #542 and #333 → #544 → #382 → #545 → #546 along the #359 chain, parent #332, downstream gate #334, and epic #588 are all real and accurately classified.
      • Target paths (tools/mise-bootstrap, Go command/internal packages, and tests/tools/masked-status.bats) are outside golden_paths, constitutional_paths, gate_config_paths, and owner_paths in .agents/agent-skills.conf:4-9, so Operator authority: None. is accurate.

    Gaps

    None.

    Split

    No further split needed; size M fits one pull request covering the bootstrap logic, thin wrapper entry point, and offline fixtures.

    Definition of done

    The definition of done for #813 (Feature). Tick each item and give its evidence.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link. — Planning coverage only, not completed work: ## Verify plan specifies concrete offline checks for AC1–AC12 and explicitly requires citing each AC ID with commands and results in the pull request's Verification section (D1).
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up. — Planning coverage only, not completed work: ## Verify plan (D2) requires independent reviewer approval of the exact merged head and a recorded disposition for every finding.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head. — Planning coverage only, not completed work: ## Verify plan (AC12, D3) requires mise run lint, mise run test, and required checks to pass with no gate loosened.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post. — Planning coverage only, not completed work: ## Verify plan (D4) requires clean repository leak scans and per-post leak review before any comment or description is posted.
    • D5 The author signed each commit, the squash included. — Planning coverage only, not completed work: ## Verify plan (D5) requires every commit to be signed.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why. — Planning coverage only, not completed work: ## Verify plan (D6) requires matching documentation or No doc impact with the reason.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label. — Planning coverage only, not completed work: ## Verify plan (D7) requires Assisted-by trailers on every commit and the provenance:agent-assisted label on the pull request.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit. — Planning coverage only, not completed work: ## Size bounds the work to one M pull request and ## Verify plan (D8) requires staying within pull request size limits.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue. — Planning coverage only, not completed work: ## Scope and non-goals designates fallback retirement as separate D9 work and ## Verify plan (D9) requires filing follow-up and deferred-finding issues with a Problem section and numbered acceptance criteria linked in Related.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit. — Planning coverage only, not completed work: ## Verify plan (D10) requires closing the issue via a merge closing reference, project board state Done, and a final Status: comment naming the merge commit.

    Round: 1 of 3

  3. tbhb-agent commented on Oct 31, 2026

    @tbhb-agent
    ContributorAuthor

    Status: build attempt returned without a patch, 2026-10-31.

    Done: a build of this issue stopped at the 800-line cap. The new Go files and wrapper measure 815 code lines (scc 4.1.0), with acceptance coverage incomplete (AC1 to AC12 only partly covered), and the change edits the existing shell file tools/mise-bootstrap, which the shell allowlist checks as a whole file.
    Next: the issue needs a split into smaller pull requests or a redesign that avoids editing the existing shell script. It is not a retry.
    Blocked on: a refinement decision by the issue's owner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    change:featPR adds or changes a feature (Conventional Commit type feat)refinement:readyPassed the Definition of Ready; may be picked up

    Fields

    Priority

    None yet

    Work type

    None yet

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions