docs(single-stack): fetch-rt.sh is the BOOTSTRAP and cannot be ported — record the retirement`s terminal state #778

Closed
opened 2026-08-20 08:31:51 +02:00 by bosun · 2 comments
Owner

Motivation

This tracker exists to stop the bash retirement from reading as unfinished forever.

scripts/fetch-rt.sh171 lines — fetches the released rt binary, checksum-verifies
it
, installs it 0755 and puts its directory on $GITHUB_PATH. It is the single source of
truth for fetch-and-verify, shared by the composite bootstrap action.yml and
reusable-release.yml's 2c-hybrid FETCH path (#582).

🔴 It cannot be ported to Go, by construction

It is the script that OBTAINS the Go binary. Porting it to Go would require rt to already
be installed — which is the thing it exists to install.

This is not a difficulty estimate or a scheduling preference. No amount of effort moves it,
and any plan that lists it as remaining work is describing an impossibility. The retirement's
terminal state is one file, and this is the file.

⚠️ Nor should its security-critical half move ANYWHERE

The checksum verify is deliberately outside the cache hit/miss branch, so a cache hit cannot
skip it:

cache HIT    skip asset fetch · fetch checksums.txt (81 B) · VERIFY · done
cache MISS   fetch asset + checksums · HTML guard · install · VERIFY

A restored binary is UNTRUSTED input — it arrives from a key-addressed store this script does
not control, and a mis-keyed or poisoned entry would otherwise be executed by every downstream
gate with no tell. checksums.txt is 81 bytes against a 9,519,266-byte asset (0.00085%), so
re-fetching it every run buys the whole safety property for nothing.

And it verifies at the binary's FINAL PATH, not the downloaded file, so what is checked is
exactly what later steps execute, on both paths.

🔑 A guard that runs before the thing it guards is available cannot itself depend on that
thing.
That is the same reason the file is irreducible.

📌 It also already moved once, in the correct direction: it was extracted from action.yml's
inline body
per the extract-to-script-for-coverage preference, so the verify lives in one
greppable, testable place rather than two copies that can silently diverge. Pushing it back
into the workflow would undo a deliberate fix
, which answers the "or Forgejo workflows?" half
of the scoping question.

Scope

No code change. This is a documentation and bookkeeping tracker:

  • Record in docs/adr/0009-go-substrate-for-v1.md (or its successor) that the single-stack goal
    terminates at one bootstrap script, with the reason above
  • Update any doc or tracker that states or implies "zero bash" as the target
  • State the residual honestly where the retirement is summarised: 1 file, 171 lines, bootstrap
    only, permanent

What WOULD change this, stated so the decision is checkable rather than assumed

Only a change to how rt is delivered — a distro package, a container image with rt
preinstalled, or a runner image baking it in (cf. alcatraz-infra#528, which did exactly this
for bats/shellcheck/graphviz). Then the bootstrap moves into the image and this file goes
away as a side effect.
That is an infrastructure decision, not a porting task, and it is not
proposed here.

Acceptance criteria

  • The ADR / roadmap states the terminal state as one bootstrap file, with the reason
  • No remaining doc or tracker implies zero-bash is reachable
  • The residual is named wherever the retirement's completion is claimed
  • The condition that would retire it (delivery-mechanism change) is written down
  • #582 — the 2c-hybrid bootstrap FETCH path that shares this script
  • #606 — the install-dir caching, and why VERIFY sits outside the branch
  • #705 — retire the last bash (forgejo-api.sh); the last unit that CAN be ported
  • alcatraz-infra#528 — baking tools into runner images; the shape that would make this moot

Anchor

2026-08-20 09:23. Operator scoping of the remainder; the bootstrap-circularity finding came from
reading the file's own header rather than from its line count. Census measured recursively
against origin/main: 5 files, 1,789 lines, of which forgejo-api.sh is 1,177.

## Motivation **This tracker exists to stop the bash retirement from reading as unfinished forever.** `scripts/fetch-rt.sh` — **171 lines** — fetches the released `rt` binary, **checksum-verifies it**, installs it `0755` and puts its directory on `$GITHUB_PATH`. It is the single source of truth for fetch-and-verify, shared by the composite bootstrap `action.yml` and `reusable-release.yml`'s 2c-hybrid FETCH path (`#582`). ## 🔴 It cannot be ported to Go, by construction > **It is the script that OBTAINS the Go binary. Porting it to Go would require `rt` to already > be installed — which is the thing it exists to install.** This is not a difficulty estimate or a scheduling preference. **No amount of effort moves it**, and any plan that lists it as remaining work is describing an impossibility. The retirement's terminal state is **one file, and this is the file.** ## ⚠️ Nor should its security-critical half move ANYWHERE The checksum verify is deliberately **outside** the cache hit/miss branch, so a cache hit cannot skip it: ``` cache HIT skip asset fetch · fetch checksums.txt (81 B) · VERIFY · done cache MISS fetch asset + checksums · HTML guard · install · VERIFY ``` **A restored binary is UNTRUSTED input** — it arrives from a key-addressed store this script does not control, and a mis-keyed or poisoned entry would otherwise be executed by every downstream gate with no tell. `checksums.txt` is 81 bytes against a 9,519,266-byte asset (**0.00085%**), so re-fetching it every run buys the whole safety property for nothing. **And it verifies at the binary's FINAL PATH, not the downloaded file**, so what is checked is exactly what later steps execute, on both paths. 🔑 **A guard that runs before the thing it guards is available cannot itself depend on that thing.** That is the same reason the file is irreducible. 📌 **It also already moved once, in the correct direction**: it was *extracted from `action.yml`'s inline body* per the extract-to-script-for-coverage preference, so the verify lives in one greppable, testable place rather than two copies that can silently diverge. **Pushing it back into the workflow would undo a deliberate fix**, which answers the *"or Forgejo workflows?"* half of the scoping question. ## Scope **No code change.** This is a documentation and bookkeeping tracker: - Record in `docs/adr/0009-go-substrate-for-v1.md` (or its successor) that the single-stack goal terminates at **one bootstrap script**, with the reason above - Update any doc or tracker that states or implies *"zero bash"* as the target - State the residual honestly where the retirement is summarised: **1 file, 171 lines, bootstrap only, permanent** ## ✅ What WOULD change this, stated so the decision is checkable rather than assumed Only a change to how `rt` is delivered — a distro package, a container image with `rt` preinstalled, or a runner image baking it in (cf. `alcatraz-infra#528`, which did exactly this for `bats`/`shellcheck`/`graphviz`). **Then the bootstrap moves into the image and this file goes away as a side effect.** That is an infrastructure decision, not a porting task, and it is not proposed here. ## Acceptance criteria - [x] The ADR / roadmap states the terminal state as one bootstrap file, with the reason - [x] No remaining doc or tracker implies zero-bash is reachable - [x] The residual is named wherever the retirement's completion is claimed - [x] The condition that would retire it (delivery-mechanism change) is written down ## Related - `#582` — the 2c-hybrid bootstrap FETCH path that shares this script - `#606` — the install-dir caching, and why VERIFY sits outside the branch - `#705` — retire the last bash (`forgejo-api.sh`); the last unit that CAN be ported - `alcatraz-infra#528` — baking tools into runner images; the shape that would make this moot ## Anchor 2026-08-20 09:23. Operator scoping of the remainder; the bootstrap-circularity finding came from reading the file's own header rather than from its line count. Census measured recursively against `origin/main`: 5 files, 1,789 lines, of which `forgejo-api.sh` is 1,177.
bosun closed this issue 2026-08-20 09:31:34 +02:00
Author
Owner

ACs ticked — each re-derived from origin/main at tick time, not assumed from the merge

AC1 terminal state = one bootstrap file, with the reason   MET  ADR-0009 §4 carries the residual
AC2 no doc implies zero-bash is reachable                  MET  see below
AC3 residual named where completion is claimed             MET  arc42/04 §strategy
AC4 the retiring CONDITION is written down                 MET  ADR-0009: "a delivery-mechanism
                                                                change: a distro package, a
                                                                container image with rt preinstalled…"

⚠️ AC2 first read as NOT MET and that was my instrument, not the docs. A grep for
zero[- ]bash returned 2 hits, which I nearly reported as unfinished work on a closed tracker.
Reading them:

ADR-0009:301        "release engine plus this bootstrap fetcher, NOT zero bash"
arc42/04:55         "The terminal state is intentionally NOT 'zero bash'"

Both occurrences are the AC being SATISFIED. The needle matched the phrase and was blind to
its negation — the same polarity gap that has cost this crew four separate measurements this
week. A count is not a reading.

## ACs ticked — each re-derived from `origin/main` at tick time, not assumed from the merge ``` AC1 terminal state = one bootstrap file, with the reason MET ADR-0009 §4 carries the residual AC2 no doc implies zero-bash is reachable MET see below AC3 residual named where completion is claimed MET arc42/04 §strategy AC4 the retiring CONDITION is written down MET ADR-0009: "a delivery-mechanism change: a distro package, a container image with rt preinstalled…" ``` ⚠️ **AC2 first read as NOT MET and that was my instrument, not the docs.** A grep for `zero[- ]bash` returned 2 hits, which I nearly reported as unfinished work on a closed tracker. Reading them: ``` ADR-0009:301 "release engine plus this bootstrap fetcher, NOT zero bash" arc42/04:55 "The terminal state is intentionally NOT 'zero bash'" ``` **Both occurrences are the AC being SATISFIED.** The needle matched the phrase and was blind to its negation — the same polarity gap that has cost this crew four separate measurements this week. *A count is not a reading.*
Author
Owner

⚠️ Amended by an operator ruling — the terminal state is fetch-rt.sh ALONE

This tracker recorded the terminal state as "one bootstrap file", meaning one .sh file.
The operator has ruled that workflow-embedded bash is in scope too (rt#792), so:

PERMANENT   scripts/fetch-rt.sh          — bootstrap circularity, unchanged by this ruling
IN SCOPE    1421 lines in run: blocks    — rt#792

The reason this tracker gave is untouched and still correct: fetch-rt.sh fetches the Go
binary, so porting it needs rt already installed. What changes is that "one bootstrap file"
must not be quoted as "the retirement is done"
— it is a statement about .sh files, and the
arc now has a second number.

⚠️ And one open question this ruling creates, flagged rather than answered: three of the
workflow blocks are bootstrap-shaped toocheckout release-toolkit (36 lines), install deps
(21), verify rt on PATH (32). A step that fetches and verifies the binary cannot be written in
the binary.
They may be permanent for the same reason this file is. That needs deciding, not
assuming.

## ⚠️ Amended by an operator ruling — the terminal state is `fetch-rt.sh` ALONE This tracker recorded the terminal state as *"one bootstrap file"*, meaning one `.sh` **file**. The operator has ruled that **workflow-embedded bash is in scope too** (`rt#792`), so: ``` PERMANENT scripts/fetch-rt.sh — bootstrap circularity, unchanged by this ruling IN SCOPE 1421 lines in run: blocks — rt#792 ``` **The reason this tracker gave is untouched and still correct**: `fetch-rt.sh` fetches the Go binary, so porting it needs `rt` already installed. **What changes is that "one bootstrap file" must not be quoted as "the retirement is done"** — it is a statement about `.sh` files, and the arc now has a second number. ⚠️ **And one open question this ruling creates**, flagged rather than answered: three of the workflow blocks are *bootstrap-shaped too* — `checkout release-toolkit` (36 lines), `install deps` (21), `verify rt on PATH` (32). **A step that fetches and verifies the binary cannot be written in the binary.** They may be permanent for the same reason this file is. That needs deciding, not assuming.
Sign in to join this conversation.
No project
No assignees
1 participant
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
frankenbit/release-toolkit#778
No description provided.