bootstrap's BOX_MANUAL is not a pasteable command when a host has two candidates #125

Closed
opened 2026-07-29 15:13:39 +00:00 by claude-bot-andresmgsl · 1 comment

commands/bootstrap.sh builds BOX_MANUAL, the recovery command printed to the
operator whenever the box install is skipped or fails. On a non-GitHub BOX_HOST
there are two candidate URLs, and !114 made the string name both by joining them
with a semicolon and a prose clause:

curl -fsSL .../box/raw/tag/main/install.sh | BOX_YES=1 BOX_REF=main bash; if that 404s: curl -fsSL .../box/raw/branch/main/install.sh | BOX_YES=1 BOX_REF=main bash

That is not valid shell:

$ bash -n manual.txt
manual.txt: line 2: syntax error: unexpected end of file
$ echo $?
2

if opens a compound command that never closes. An operator who pastes the
whole line runs the first curl | bash and then eats a syntax error — and if the
first URL was the one that 404s, they are left believing the recovery path is
broken rather than that they pasted two commands as one.

Three paths print this without ever probing, so the first candidate is unverified
in all of them: RIG_SKIP_BOX_INSTALL=1, curl absent, and every candidate
failing.

Why bother

The listing behavior is right and should stay — it fixed a real finding, that a
branch-valued BOX_REF was handed a single /raw/tag/ URL that 404s. Only the
joining is wrong. And the bar is this repo's own: #111 opened by objecting that
install.sh:228 "tells the operator to run a command that 404s". A command that
cannot be pasted is the same class of defect.

BOX_MANUAL is also the only recovery instruction an operator gets on a
half-converged host, which is the moment they are least able to debug it.

Spec

Keep both candidates, drop the ;, and make the alternate unambiguously prose so
the leading command stays copy-pasteable:

curl -fsSL <tag-url> | BOX_YES=1 BOX_REF=<ref> bash    (if that 404s, the branch form: curl -fsSL <branch-url> | BOX_YES=1 BOX_REF=<ref> bash)

The GitHub path emits one candidate and must stay exactly as it is.

Tasks

  • Rebuild the multi-candidate BOX_MANUAL without shell metacharacters between commands
  • Leave the single-candidate (GitHub) string byte-identical
  • Assert pasteability in test/cli.sh

Acceptance criteria

  • The leading command of BOX_MANUAL parses under bash -n on every host
  • Both candidate URLs still appear on a multi-candidate host
  • The GitHub string is unchanged
  • The post-probe rewrite to the URL that actually worked still happens

Test plan

Extract the BOX_MANUAL construction with the awk idiom test/cli.sh already
uses on box_install_urls, render it for GitHub and Forgejo across a
version-shaped and a branch-shaped BOX_REF, and pipe the leading command
through bash -n.

Dependencies

Follow-up to #111 / !114, raised in review and approved as non-blocking.

Filed under @andres's explicit authorization on !114 to create process issues
directly. No queue label applied — ready does not exist yet.

`commands/bootstrap.sh` builds `BOX_MANUAL`, the recovery command printed to the operator whenever the box install is skipped or fails. On a non-GitHub `BOX_HOST` there are two candidate URLs, and !114 made the string name both by joining them with a semicolon and a prose clause: ``` curl -fsSL .../box/raw/tag/main/install.sh | BOX_YES=1 BOX_REF=main bash; if that 404s: curl -fsSL .../box/raw/branch/main/install.sh | BOX_YES=1 BOX_REF=main bash ``` That is not valid shell: ``` $ bash -n manual.txt manual.txt: line 2: syntax error: unexpected end of file $ echo $? 2 ``` `if` opens a compound command that never closes. An operator who pastes the whole line runs the first `curl | bash` and then eats a syntax error — and if the first URL was the one that 404s, they are left believing the recovery path is broken rather than that they pasted two commands as one. Three paths print this without ever probing, so the first candidate is unverified in all of them: `RIG_SKIP_BOX_INSTALL=1`, `curl` absent, and every candidate failing. ## Why bother The listing behavior is right and should stay — it fixed a real finding, that a branch-valued `BOX_REF` was handed a single `/raw/tag/` URL that 404s. Only the joining is wrong. And the bar is this repo's own: #111 opened by objecting that `install.sh:228` "tells the operator to run a command that 404s". A command that cannot be pasted is the same class of defect. `BOX_MANUAL` is also the only recovery instruction an operator gets on a half-converged host, which is the moment they are least able to debug it. ## Spec Keep both candidates, drop the `;`, and make the alternate unambiguously prose so the leading command stays copy-pasteable: ``` curl -fsSL <tag-url> | BOX_YES=1 BOX_REF=<ref> bash (if that 404s, the branch form: curl -fsSL <branch-url> | BOX_YES=1 BOX_REF=<ref> bash) ``` The GitHub path emits one candidate and must stay exactly as it is. ## Tasks - [ ] Rebuild the multi-candidate `BOX_MANUAL` without shell metacharacters between commands - [ ] Leave the single-candidate (GitHub) string byte-identical - [ ] Assert pasteability in `test/cli.sh` ## Acceptance criteria - [ ] The leading command of `BOX_MANUAL` parses under `bash -n` on every host - [ ] Both candidate URLs still appear on a multi-candidate host - [ ] The GitHub string is unchanged - [ ] The post-probe rewrite to the URL that actually worked still happens ## Test plan Extract the `BOX_MANUAL` construction with the awk idiom `test/cli.sh` already uses on `box_install_urls`, render it for GitHub and Forgejo across a version-shaped and a branch-shaped `BOX_REF`, and pipe the leading command through `bash -n`. ## Dependencies Follow-up to #111 / !114, raised in review and approved as non-blocking. *Filed under @andres's explicit authorization on !114 to create process issues directly. No queue label applied — `ready` does not exist yet.*
claude-bot-andresmgsl added the
bug
scope:bootstrap
labels 2026-07-29 15:13:39 +00:00

@andres#125 can be closed as completed by merged !114 (a3ec471, merge commit 12803b2).

I checked each acceptance criterion against current origin/main:

  • Forgejo recovery is emitted as separate try: / or: lines; each command is independently pasteable and passes bash -n.
  • Both /raw/tag/ and /raw/branch/ candidates remain visible.
  • GitHub remains a single bare raw.githubusercontent.com command.
  • A successful probe still rewrites recovery to the URL that actually worked.
  • All skip/failure consumers route through box_manual_emit, preventing a multiline command from being interpolated into one warning.

Fresh verification in a detached worktree at merge commit 12803b2: bash test/cli.sh664 passed, 0 failed.

This is the only currently open issue whose full acceptance contract was completed by #111 / !114. I have not closed it.

@andres — #125 can be closed as completed by merged !114 (`a3ec471`, merge commit `12803b2`). I checked each acceptance criterion against current `origin/main`: - Forgejo recovery is emitted as separate `try:` / `or:` lines; each command is independently pasteable and passes `bash -n`. - Both `/raw/tag/` and `/raw/branch/` candidates remain visible. - GitHub remains a single bare `raw.githubusercontent.com` command. - A successful probe still rewrites recovery to the URL that actually worked. - All skip/failure consumers route through `box_manual_emit`, preventing a multiline command from being interpolated into one warning. Fresh verification in a detached worktree at merge commit `12803b2`: `bash test/cli.sh` → **664 passed, 0 failed**. This is the only currently open issue whose full acceptance contract was completed by #111 / !114. I have not closed it.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/rig#125
No description provided.