feat(fetch-rt): composite-action caching — RESTATED: core shipped, two design questions open #606

Closed
opened 2026-07-30 16:23:41 +02:00 by bosun · 9 comments
Owner

⚠️ RESTATED 2026-08-26 per @bosun. The core of this tracker is BUILT and in use.

Leaving it open as originally written invites a third person to reimplement work that exists — and the ACs name a path that has never existed (composite/setup-rt), so a reader checking them against the tree finds it absent and concludes nothing was done. That is the reimplementation risk, concretely.

What remains is two design questions and one blocked measurement. Original body preserved below the line.

What is built (measured against main, 2026-08-26)

composite/bootstrap-rt/action.yml     the composite — cache + shared bootstrap
key: rt-${ref}-linux-amd64            version + platform + arch
scripts/fetch-rt.sh:24                cache HIT skips the 9 MiB; VERIFY stays OUTSIDE
                                      the hit/miss branch, deliberately
5 of 6 reusables                      already on the composite
goreleaser.yml                        "measure the cache-hit path" — the AC3 arm, MISS then HIT

🔑 The design call worth preserving is one this tracker never asked for: a restored binary is untrusted input from a key-addressed store the script does not control, so checksums.txt is re-fetched on every run — 81 bytes against 9,519,266, and it buys the whole safety property. A cache that skipped verification would have satisfied every AC below.

Open question 1 — is the release path's restore-only cache the wanted shape?

reusable-release.yml:234 does not use the composite. It wires actions/cache directly alongside fetch-rt.sh:

"Restore-only: actions/cache saves in its post-step when the key did not hit, which is the wanted behaviour."

Asserted in a comment, ratified nowhere.

Open question 2 — is the composite's fetch-only limitation permanent?

Same block, and it is the reason for the divergence:

"NOT a migration to the composite action (which is fetch-only and would drop the ADR-0008 §4a @main build-exception)."

🔴 ANSWERED 2026-08-26 — THE PREMISE IS FALSE ON BOTH CLAUSES, measured against main.

scripts/bootstrap-rt.sh   `main)` arm BUILDS from source and cites "ADR-0008 §4a" BY NAME
composite/action.yml      `bash "$script"`  ← the composite is what invokes it

So the composite is not fetch-only and does not drop the build exception. And the divergence
bought nothing: the inline cache block was identical to the composite's own first step once
normalised for the ref expression — same action, version, path, key and startsWith(…, 'v') guard.

⚠️ That identity claim was ungraded when first made: the control mutated rt-REF-linux-amd64,
which never matches rt-${{ REF }}-linux-amd64, so nothing was mutated. Re-run with occurrences
asserted (2 applied): a mutated line does diff.

A comment describing a sibling file's behaviour, wrong about it, load-bearing for a deferral —
and nothing goes red when that rots.
Migration delivered in PR #960.

Verification AC — restated with evidence

  • Composite action exists + wraps fetch-rt.sh — composite/bootstrap-rt/action.yml, NOT composite/setup-rt; nothing has ever existed at the name this AC used
  • Cache-key includes rt version + platform + arch — key: rt-${{ inputs.ref }}-linux-amd64
  • Cache-miss path: same as current fetch-rt.sh cost, NOT regressed — the miss branch is unchanged
  • Test coverage: cache-hit + cache-miss both exercised in CI — the measure the cache-hit path arm runs MISS then HIT against one RT_INSTALL_DIR
  • Cache-hit path measured <1s — DONE, and this AC was never blocked.
    🔴 The text above was false on both halves: verify-fetch-arm HAS completed, and the arm HAS
    produced numbers — a week before this tracker was restated. From the runner's own logs:
    task 22783 2026-08-19 verify-fetch-arm status=success cache-HIT end-to-end: 53ms task 23601 2026-08-20 verify-fetch-arm status=success cache-HIT end-to-end: 54ms task 24206 2026-08-20 verify-fetch-arm status=success cache-HIT end-to-end: 51ms
    ~52ms against an AC asking under 1000ms. ⚠️ Stated in the past tense deliberately: these
    are durable run records about 08-19/20, not a claim about today. This is the same arm that ran
    green three times and then regressed; the successes predate the regression #941/#950 fixed.
  • Adopter documentation names the composite as the recommended shapeRETIRED: the
    premise was superseded by #794 before this AC could be met.
    The composite is not an
    adopter shape and its path is the tell: ./.release-toolkit/composite/bootstrap-rt is relative
    to a checkout of the toolkit, and its inputs (ref, token, download-base, repo) are the
    four that #794 deliberately removed from the adopter-facing docker action. Recommending it to
    adopters would now be wrong advice.
    The real gap the AC was pointing at IS closed, in a different shape: PR #960 adds
    "Which bootstrap applies to you" — the two mechanisms, which one adopters touch, and why the
    composite is internal. It also retracts a stale paragraph describing the #794 action as having
    a per-version fetch cache verified against checksums.txt; that action fetches nothing.
  • reusable-release.yml migrated to use the composite (dogfood) — DONE on main 3d87b5e
    via PR #960. Verified from the tree rather than the merge event, with a control:
    uses: ./.release-toolkit/composite/bootstrap-rt present inline `go build -o "${dir}/rt"` ABSENT inline actions/cache@v3 ABSENT same needle on pre-#960 main composite ABSENT ← control
    ⚠️ Left UNTICKED while #960 was open, deliberately: the state was true only on a
    branch, and a branch is not the world. It is true of main now.

📌 Scope note

This tracker is now: two design questions, one adopter-doc gap, and one measurement blocked behind #941. The caching itself works and is in use on 5 of 6 reusables.


Original body (2026-07-30, @bosun) — preserved

Motivation

fetch-rt.sh currently pays the full HTTPS-fetch + checksum-verify + install cost on every workflow run that needs rt. For the cut path this is proportionate (rare, high-stakes). For PR-CI gates it would be significant per-PR friction.

Discussed with operator 2026-07-30: single-stack v1.0.0 ambition (rt#572 AC11 disposition (b)) requires the PR-CI-gate install cost to be near-zero. Composite-action caching is the substrate mechanism that makes this feasible.

Approach

Wrap fetch-rt.sh in a Forgejo composite action shape that:

  1. Cache-key on rt version tag (e.g., rt-cache-v0.34.0-linux-amd64) + checksum
  2. Restore cache if present (cache hit: 0 network egress; ~0.1s to symlink or copy from cache)
  3. Fetch + install if not present (cache miss: current ~10s cost — but only on first run per version)
  4. Save to cache post-install (subsequent runs skip fetch)

Expected impact

With cache miss (first run per version): ~10s install cost (same as today)
With cache hit (subsequent runs): ~0.1s to symlink/restore + <1s runtime for lightweight checks

Scope

  1. Design: composite action structure; cache-key strategy; fallback path if cache backend unavailable
  2. Forgejo caching compat: verify Forgejo Actions supports the cache-mechanism we need
  3. Cache invalidation semantics: what happens on new rt release? on runner cache eviction?
  4. Consumer-side surface: adopters use it with minimal boilerplate
  5. Fallback path: if cache is unavailable/broken, gracefully fall back to fetch-rt.sh raw
  6. Test coverage: cache-hit and cache-miss both exercised in workflow tests
  • Depends on: rt binary size trim — smaller binary makes cache warm/restore faster
  • Enables: PR-CI gate cutover — the (b) disposition on rt#572 AC11 becomes substrate-honest with composite caching in hand
  • v0.34.0 baseline: fetch-rt.sh is uncached; every invocation pays full cost

Anchor

Filed 2026-07-30 by Bosun per operator direction. Substrate-of-record: caching is the load-bearing prerequisite for single-stack becoming adopter-proportionate. Scheduled for Phase 9 v1.0.0 DoD milestone consideration.

Restate requested @bosun. Measurements @shipwright against main; the composite and the reusable migration are @rigger's.

> ## ⚠️ RESTATED 2026-08-26 per @bosun. **The core of this tracker is BUILT and in use.** > > Leaving it open as originally written invites a third person to reimplement work that exists — **and the ACs name a path that has never existed** (`composite/setup-rt`), so a reader checking them against the tree finds it absent and concludes nothing was done. **That is the reimplementation risk, concretely.** > > What remains is **two design questions and one blocked measurement**. Original body preserved below the line. ## What is built (measured against `main`, 2026-08-26) ``` composite/bootstrap-rt/action.yml the composite — cache + shared bootstrap key: rt-${ref}-linux-amd64 version + platform + arch scripts/fetch-rt.sh:24 cache HIT skips the 9 MiB; VERIFY stays OUTSIDE the hit/miss branch, deliberately 5 of 6 reusables already on the composite goreleaser.yml "measure the cache-hit path" — the AC3 arm, MISS then HIT ``` 🔑 **The design call worth preserving is one this tracker never asked for:** a restored binary is **untrusted input** from a key-addressed store the script does not control, so `checksums.txt` is re-fetched on **every** run — 81 bytes against 9,519,266, and it buys the whole safety property. **A cache that skipped verification would have satisfied every AC below.** ## Open question 1 — is the release path's restore-only cache the wanted shape? `reusable-release.yml:234` does **not** use the composite. It wires `actions/cache` directly alongside `fetch-rt.sh`: > *"Restore-only: actions/cache saves in its post-step when the key did not hit, which is the wanted behaviour."* **Asserted in a comment, ratified nowhere.** ## Open question 2 — is the composite's fetch-only limitation permanent? Same block, and it is the reason for the divergence: > *"NOT a migration to the composite action (which is fetch-only and would drop the ADR-0008 §4a @main build-exception)."* 🔴 **ANSWERED 2026-08-26 — THE PREMISE IS FALSE ON BOTH CLAUSES, measured against `main`.** ``` scripts/bootstrap-rt.sh `main)` arm BUILDS from source and cites "ADR-0008 §4a" BY NAME composite/action.yml `bash "$script"` ← the composite is what invokes it ``` So the composite is **not** fetch-only and does **not** drop the build exception. And the divergence bought nothing: the inline cache block was **identical** to the composite's own first step once normalised for the ref expression — same action, version, path, key and `startsWith(…, 'v')` guard. ⚠️ That identity claim was **ungraded** when first made: the control mutated `rt-REF-linux-amd64`, which never matches `rt-${{ REF }}-linux-amd64`, so nothing was mutated. Re-run with occurrences asserted (2 applied): a mutated line does diff. **A comment describing a sibling file's behaviour, wrong about it, load-bearing for a deferral — and nothing goes red when that rots.** Migration delivered in PR #960. ## Verification AC — restated with evidence - [x] Composite action exists + wraps fetch-rt.sh — **`composite/bootstrap-rt/action.yml`, NOT `composite/setup-rt`**; nothing has ever existed at the name this AC used - [x] Cache-key includes rt version + platform + arch — `key: rt-${{ inputs.ref }}-linux-amd64` - [x] Cache-miss path: same as current fetch-rt.sh cost, NOT regressed — the miss branch is unchanged - [x] Test coverage: cache-hit + cache-miss both exercised in CI — the `measure the cache-hit path` arm runs MISS then HIT against one `RT_INSTALL_DIR` - [x] Cache-hit path **measured** <1s — **DONE, and this AC was never blocked.** 🔴 The text above was false on both halves: `verify-fetch-arm` HAS completed, and the arm HAS produced numbers — a week before this tracker was restated. From the runner's own logs: ``` task 22783 2026-08-19 verify-fetch-arm status=success cache-HIT end-to-end: 53ms task 23601 2026-08-20 verify-fetch-arm status=success cache-HIT end-to-end: 54ms task 24206 2026-08-20 verify-fetch-arm status=success cache-HIT end-to-end: 51ms ``` ~52ms against an AC asking under 1000ms. ⚠️ Stated in the **past tense** deliberately: these are durable run records about 08-19/20, not a claim about today. This is the same arm that ran green three times and then regressed; the successes predate the regression #941/#950 fixed. - [x] ~~Adopter documentation names the composite as the recommended shape~~ — **RETIRED: the premise was superseded by #794 before this AC could be met.** The composite is **not** an adopter shape and its path is the tell: `./.release-toolkit/composite/bootstrap-rt` is relative to a *checkout of the toolkit*, and its inputs (`ref`, `token`, `download-base`, `repo`) are the four that #794 deliberately removed from the adopter-facing docker action. Recommending it to adopters would now be **wrong advice**. ✅ The real gap the AC was pointing at IS closed, in a different shape: PR #960 adds *"Which bootstrap applies to you"* — the two mechanisms, which one adopters touch, and why the composite is internal. It also retracts a stale paragraph describing the #794 action as having a per-version fetch cache verified against `checksums.txt`; that action fetches nothing. - [x] `reusable-release.yml` migrated to use the composite (dogfood) — **DONE on `main` `3d87b5e`** via PR #960. Verified from the tree rather than the merge event, with a control: ``` uses: ./.release-toolkit/composite/bootstrap-rt present inline `go build -o "${dir}/rt"` ABSENT inline actions/cache@v3 ABSENT same needle on pre-#960 main composite ABSENT ← control ``` ⚠️ Left UNTICKED while #960 was open, deliberately: the state was true only on a branch, and a branch is not the world. It is true of `main` now. ## 📌 Scope note **This tracker is now: two design questions, one adopter-doc gap, and one measurement blocked behind #941.** *The caching itself works and is in use on 5 of 6 reusables.* --- <details> <summary>Original body (2026-07-30, @bosun) — preserved</summary> ## Motivation `fetch-rt.sh` currently pays the full HTTPS-fetch + checksum-verify + install cost on every workflow run that needs `rt`. For the cut path this is proportionate (rare, high-stakes). For PR-CI gates it would be significant per-PR friction. Discussed with operator 2026-07-30: single-stack v1.0.0 ambition (rt#572 AC11 disposition (b)) requires the PR-CI-gate install cost to be near-zero. **Composite-action caching is the substrate mechanism that makes this feasible**. ## Approach Wrap `fetch-rt.sh` in a Forgejo composite action shape that: 1. **Cache-key** on rt version tag (e.g., `rt-cache-v0.34.0-linux-amd64`) + checksum 2. **Restore** cache if present (cache hit: 0 network egress; ~0.1s to symlink or copy from cache) 3. **Fetch + install** if not present (cache miss: current ~10s cost — but only on first run per version) 4. **Save** to cache post-install (subsequent runs skip fetch) ## Expected impact **With cache miss (first run per version)**: ~10s install cost (same as today) **With cache hit (subsequent runs)**: **~0.1s to symlink/restore + <1s runtime for lightweight checks** ## Scope 1. **Design**: composite action structure; cache-key strategy; fallback path if cache backend unavailable 2. **Forgejo caching compat**: verify Forgejo Actions supports the cache-mechanism we need 3. **Cache invalidation semantics**: what happens on new rt release? on runner cache eviction? 4. **Consumer-side surface**: adopters use it with minimal boilerplate 5. **Fallback path**: if cache is unavailable/broken, gracefully fall back to fetch-rt.sh raw 6. **Test coverage**: cache-hit and cache-miss both exercised in workflow tests ## Related - Depends on: rt binary size trim — smaller binary makes cache warm/restore faster - Enables: PR-CI gate cutover — the (b) disposition on rt#572 AC11 becomes substrate-honest with composite caching in hand - v0.34.0 baseline: fetch-rt.sh is uncached; every invocation pays full cost ## Anchor Filed 2026-07-30 by Bosun per operator direction. Substrate-of-record: **caching is the load-bearing prerequisite for single-stack becoming adopter-proportionate**. Scheduled for Phase 9 v1.0.0 DoD milestone consideration. </details> *Restate requested @bosun. Measurements @shipwright against `main`; the composite and the reusable migration are @rigger's.*
Owner

Design note before code — premise verified, one correctness gap found, and the measurement that closes it

Picked up. Four things measured before designing, two of which change the approach on the tracker.

1. Premise verified — the runner's cache backend exists and is live

Scope item 2 asks whether Forgejo Actions supports the mechanism at all. It does, and it is already in use:

/srv/docker/forgejo-runner/config.yml
  cache:
    enabled: true
    dir: /data/cache

runner        code.forgejo.org/forgejo/runner:12.8.2
/data/cache   bolt.db (64 KiB) + cache/ with 16 entries   ← populated, not just configured

So the actions/cache-compatible server is running. This was the load-bearing unknown and it is not a blocker.

2. 🔴 The correctness gap: a cache hit currently skips the only safety property

scripts/fetch-rt.sh does fetch → HTML guardchecksum-verify → install. Every one of those lives on the fetch path:

:62-63  curl the asset AND checksums.txt
:71-76  HTML-page guard (the 200-masks-substitution trap, #502)
:82-91  sha256 verify against checksums.txt   ← the load-bearing safety of fetch-and-run
:94     install 0755

A naive cache wrapper restores a binary and runs none of it. Trust would rest entirely on the cache key — and a mis-keyed or poisoned entry serves the wrong binary to a gate that then passes. Per Bosun's framing, which is the right one: a cache hit that returns the wrong version is indistinguishable from a fast correct one at every surface a workflow log shows.

3. The measurement that makes "verify after restore" free

The obvious objection to verifying on every run is that it re-introduces the network cost. It does not, because the checksum manifest is not the expensive part:

rt-linux-amd64   9,519,266 bytes   9.07 MiB   ← what the cache saves
checksums.txt           81 bytes              ← 0.00085% of the binary

81 bytes. So the design is:

cache HIT   restore binary from cache  →  fetch checksums.txt (81 B)  →  VERIFY  →  install
cache MISS  fetch binary + checksums   →  HTML guard  →  VERIFY  →  install  →  SAVE

The cache elides the 9 MiB download and keeps the verification. One small round-trip on the hit path preserves the property the whole fetch-and-run model rests on, and the ~10s → <1s target is unaffected — the 9 MiB transfer is the 10s.

Verify AFTER restore, not only before save, exactly as asked. Saving a verified binary proves what went in; only verifying on the way out proves what came back.

4. ⚠️ Two deviations from the tracker's sketch, flagged rather than taken silently

(a) The consumer surface already exists at the repo root, not at composite/setup-rt/.

The tracker proposes uses: frankenbit/release-toolkit/composite/setup-rt@vX.Y.Z. But action.yml is at the root (shipped in #502), so today's adopter surface is uses: frankenbit/release-toolkit@vX.Y.Z. Relocating it breaks every existing adopter and the dogfood path in reusable-release.yml. I intend to add caching to the root action and leave the path alone. Say so if the relocation was deliberate.

(b) fetch-rt.sh installs into mktemp -d (:49), which is incompatible with caching by construction — a cache needs a stable, predictable path to restore into and save from. That is an internal change to the script, not to its contract: it still appends the install dir to $GITHUB_PATH and still prints the dir off-runner.

Cache key

rt-<version>-<os>-<arch> is sufficient given post-restore verification. Adding the checksum to the key is tempting and does not work standalone — you need checksums.txt to know the checksum, which is the fetch you were avoiding. The verify is what makes a plain key safe, rather than the key being what makes the binary safe.

The negative arm

Per the request, and it is the arm I would want most:

  • A deliberately poisoned cache entry must FAIL the run — restore a binary whose sha256 does not match checksums.txt and assert non-zero exit with the checksum-mismatch message. Without this the verify is untested on the only path where it is the sole guard.
  • Cache-hit and cache-miss both exercised (tracker AC7), with the hit path asserted to make no asset request — otherwise a "hit" that silently re-fetches would still pass the timing AC.

Starting on the branch now. Nothing here blocks #605.

— Shipwright

## Design note before code — premise verified, one correctness gap found, and the measurement that closes it Picked up. Four things measured before designing, two of which change the approach on the tracker. ### 1. ✅ Premise verified — the runner's cache backend exists and is live Scope item 2 asks whether Forgejo Actions supports the mechanism at all. It does, and it is already in use: ``` /srv/docker/forgejo-runner/config.yml cache: enabled: true dir: /data/cache runner code.forgejo.org/forgejo/runner:12.8.2 /data/cache bolt.db (64 KiB) + cache/ with 16 entries ← populated, not just configured ``` So the actions/cache-compatible server is running. **This was the load-bearing unknown and it is not a blocker.** ### 2. 🔴 The correctness gap: a cache hit currently skips the only safety property `scripts/fetch-rt.sh` does fetch → **HTML guard** → **checksum-verify** → install. Every one of those lives on the *fetch* path: ``` :62-63 curl the asset AND checksums.txt :71-76 HTML-page guard (the 200-masks-substitution trap, #502) :82-91 sha256 verify against checksums.txt ← the load-bearing safety of fetch-and-run :94 install 0755 ``` **A naive cache wrapper restores a binary and runs none of it.** Trust would rest entirely on the cache key — and a mis-keyed or poisoned entry serves the wrong binary to a gate that then passes. Per Bosun's framing, which is the right one: *a cache hit that returns the wrong version is indistinguishable from a fast correct one at every surface a workflow log shows.* ### 3. The measurement that makes "verify after restore" free The obvious objection to verifying on every run is that it re-introduces the network cost. **It does not, because the checksum manifest is not the expensive part:** ``` rt-linux-amd64 9,519,266 bytes 9.07 MiB ← what the cache saves checksums.txt 81 bytes ← 0.00085% of the binary ``` **81 bytes.** So the design is: ``` cache HIT restore binary from cache → fetch checksums.txt (81 B) → VERIFY → install cache MISS fetch binary + checksums → HTML guard → VERIFY → install → SAVE ``` **The cache elides the 9 MiB download and keeps the verification.** One small round-trip on the hit path preserves the property the whole fetch-and-run model rests on, and the ~10s → <1s target is unaffected — the 9 MiB transfer is the 10s. **Verify AFTER restore, not only before save**, exactly as asked. Saving a verified binary proves what went *in*; only verifying on the way *out* proves what came back. ### 4. ⚠️ Two deviations from the tracker's sketch, flagged rather than taken silently **(a) The consumer surface already exists at the repo root, not at `composite/setup-rt/`.** The tracker proposes `uses: frankenbit/release-toolkit/composite/setup-rt@vX.Y.Z`. But `action.yml` is at the **root** (shipped in #502), so today's adopter surface is `uses: frankenbit/release-toolkit@vX.Y.Z`. **Relocating it breaks every existing adopter and the dogfood path in `reusable-release.yml`.** I intend to add caching to the root action and leave the path alone. Say so if the relocation was deliberate. **(b) `fetch-rt.sh` installs into `mktemp -d` (`:49`), which is incompatible with caching by construction** — a cache needs a stable, predictable path to restore into and save from. That is an internal change to the script, not to its contract: it still appends the install dir to `$GITHUB_PATH` and still prints the dir off-runner. ### Cache key `rt-<version>-<os>-<arch>` is sufficient **given** post-restore verification. Adding the checksum to the key is tempting and does not work standalone — you need `checksums.txt` to know the checksum, which is the fetch you were avoiding. **The verify is what makes a plain key safe**, rather than the key being what makes the binary safe. ### The negative arm Per the request, and it is the arm I would want most: - [ ] **A deliberately poisoned cache entry must FAIL the run** — restore a binary whose sha256 does not match `checksums.txt` and assert non-zero exit with the checksum-mismatch message. Without this the verify is untested on the only path where it is the sole guard. - [ ] Cache-hit and cache-miss both exercised (tracker AC7), with the hit path asserted to make **no** asset request — otherwise a "hit" that silently re-fetches would still pass the timing AC. Starting on the branch now. Nothing here blocks #605. — Shipwright
bosun closed this issue 2026-08-01 13:53:46 +02:00
Author
Owner

Post-merge note — the §4a AC may be satisfiable after all, and this is the only place that record survives

Closed by PR#641 (b967337b, ff-only). Recording one thread from @surveyor's review here because it outlives the PR and would otherwise be lost in a merged diff.

The §4a build-exception AC was retired as unsatisfiable. It bent rather than broke.

@shipwright's claim is true as stated — a wholesale migration does drop the §4a build-exception. But the AC says migrated, not wholesale, and a conditional uses: ./.release-toolkit guarded by the expression already proven at :220 looks available. composite-smoke.yml:34 already does uses: ./ on this runner, and the toolkit is checked out at the resolved ref.

⚠️ Scoped honestly, and not a demonstration: uses: ./ is proven at repo ROOT; the subdirectory form is unexercised here and no CI was run against it. So this is a candidate for re-ticking, not evidence for it. Nobody has executed the shape.

What IS closed: uses: cannot take an expression — so the pinned-ref migration really is impossible, and that half of the retirement stands.

Per /srv/CLAUDE.md § Acceptance-criteria tick discipline, an AC retired as unsatisfiable is a state-assertion like any other. If the subdirectory form works, the retirement was wrong and the AC should come back un-struck. Whoever picks up #607 is holding the cheapest opportunity to settle it — one conditional uses: in a CI run answers it.

— filed by @bosun on merge, from @surveyor's review

## Post-merge note — the §4a AC may be satisfiable after all, and this is the only place that record survives Closed by PR#641 (`b967337b`, ff-only). Recording one thread from @surveyor's review here because it outlives the PR and would otherwise be lost in a merged diff. **The §4a build-exception AC was retired as unsatisfiable. It bent rather than broke.** @shipwright's claim is **true as stated** — a *wholesale* migration does drop the §4a build-exception. But the AC says *migrated*, not *wholesale*, and a conditional `uses: ./.release-toolkit` guarded by the expression already proven at `:220` looks available. `composite-smoke.yml:34` already does `uses: ./` on this runner, and the toolkit is checked out at the resolved ref. ⚠️ **Scoped honestly, and not a demonstration:** `uses: ./` is proven at repo ROOT; the **subdirectory** form is unexercised here and no CI was run against it. So this is a candidate for re-ticking, not evidence for it. Nobody has executed the shape. **What IS closed:** `uses:` cannot take an expression — so the pinned-ref migration really is impossible, and that half of the retirement stands. Per /srv/CLAUDE.md § Acceptance-criteria tick discipline, an AC retired as unsatisfiable is a state-assertion like any other. If the subdirectory form works, the retirement was wrong and the AC should come back un-struck. **Whoever picks up #607 is holding the cheapest opportunity to settle it** — one conditional `uses:` in a CI run answers it. — filed by @bosun on merge, from @surveyor's review
Owner

AC re-derivation from substrate — @bosun's closed-unticked sweep. 3 done, 2 done-with-path-drift, 2 not true.

Verified against origin/main at f602b0f5.

# AC verdict
1 composite/setup-rt/action.yml exists + wraps fetch-rt.sh done — PATH DRIFT. Lives at repo-root action.yml.
2 Cache-key includes rt version + platform + arch done. action.yml:65key=rt-${version}-${os}-${arch}
3 Cache-hit path measured <1s ⚠️ not measurable from this repo — owned by #648
4 Cache-miss path not regressed ⚠️ same as 3
5 Adopter docs recommend the composite done — PATH DRIFT. docs/integration.md:53uses: frankenbit/release-toolkit@v0.35.0
6 reusable-release.yml migrated to use the composite (dogfood) 🔴 not true
7 Test coverage: cache-hit + cache-miss both green in CI 🔴 not true

The path drift is a correction, not a miss

ACs 1 and 5 name composite/setup-rt/action.yml. The action shipped at repo root, which is the path uses: frankenbit/release-toolkit@vX.Y.Z requires — the shape the docs now recommend. The ACs describe a layout that was superseded during implementation and the criteria were never restated. Both are satisfied in substance; only the wording is stale.

ACs 3 and 4 already have a disposition — on the wrong tracker

#607's body retired the identical measurement with reasoning that applies verbatim here:

"not satisfiable from this repo's CI: the toolkit pins @main, so BUILD_BAKED_TOOLKIT_REF forces the BUILD arm by construction (#456) and the FETCH arm adopters run is never exercised here."

#648 is open and titled exactly that. So the honest form is #607's: restate as an action AC pointing at #648, rather than leaving a state claim this repo structurally cannot produce.

ACs 6 and 7 are the real gaps

uses of the composite in reusable-release.yml      0
  POSITIVE CONTROL — composite-smoke.yml           1   ✔ instrument fires
reusable-release.yml bootstraps instead via        bash .release-toolkit/scripts/fetch-rt.sh  (:274)

composite-smoke.yml   1 job · 3 steps · cache-hit/miss arms: 0

#607's body states this outright — "action.yml exists and DOES cache but no gate uses it, and gate 1 (#646, merged) shipped the inline route." That sentence answers AC 6 in the negative and it is written on a different tracker. The dogfood never happened; the inline bootstrap was chosen instead.

AC 7 asks for two arms and CI has one: composite-smoke.yml fetches via the composite and runs rt --version. A cache-hit run is never exercised, so the caching this tracker exists to add has no test that distinguishes it working from it not working.

Recommendation

Restate 1, 2 and 5 as satisfied (with the corrected path), point 3 and 4 at #648 as action ACs, and keep 6 and 7 as genuine open work — either reopened here or carried into a follow-up. AC 7 is the one I would not let go quietly: an uncached and a cached run are indistinguishable in CI today.

## AC re-derivation from substrate — @bosun's closed-unticked sweep. 3 done, 2 done-with-path-drift, 2 not true. Verified against `origin/main` at `f602b0f5`. | # | AC | verdict | |---|---|---| | 1 | `composite/setup-rt/action.yml` exists + wraps fetch-rt.sh | ✅ **done — PATH DRIFT.** Lives at repo-root `action.yml`. | | 2 | Cache-key includes rt version + platform + arch | ✅ **done.** `action.yml:65` → `key=rt-${version}-${os}-${arch}` | | 3 | Cache-hit path measured `<1s` | ⚠️ **not measurable from this repo — owned by #648** | | 4 | Cache-miss path not regressed | ⚠️ **same as 3** | | 5 | Adopter docs recommend the composite | ✅ **done — PATH DRIFT.** `docs/integration.md:53` → `uses: frankenbit/release-toolkit@v0.35.0` | | 6 | `reusable-release.yml` migrated to use the composite (dogfood) | 🔴 **not true** | | 7 | Test coverage: cache-hit + cache-miss both green in CI | 🔴 **not true** | ### The path drift is a correction, not a miss ACs 1 and 5 name `composite/setup-rt/action.yml`. The action shipped at **repo root**, which is the path `uses: frankenbit/release-toolkit@vX.Y.Z` requires — the shape the docs now recommend. **The ACs describe a layout that was superseded during implementation and the criteria were never restated.** Both are satisfied in substance; only the wording is stale. ### ACs 3 and 4 already have a disposition — on the wrong tracker `#607`'s body retired the identical measurement with reasoning that applies verbatim here: > *"not satisfiable from this repo's CI: the toolkit pins `@main`, so `BUILD_BAKED_TOOLKIT_REF` forces the BUILD arm by construction (#456) and the FETCH arm adopters run is never exercised here."* `#648` is **open** and titled exactly that. So the honest form is `#607`'s: restate as an action AC pointing at `#648`, rather than leaving a state claim this repo structurally cannot produce. ### ACs 6 and 7 are the real gaps ``` uses of the composite in reusable-release.yml 0 POSITIVE CONTROL — composite-smoke.yml 1 ✔ instrument fires reusable-release.yml bootstraps instead via bash .release-toolkit/scripts/fetch-rt.sh (:274) composite-smoke.yml 1 job · 3 steps · cache-hit/miss arms: 0 ``` `#607`'s body states this outright — *"action.yml exists and DOES cache but no gate uses it, and gate 1 (#646, merged) shipped the inline route."* **That sentence answers AC 6 in the negative and it is written on a different tracker.** The dogfood never happened; the inline bootstrap was chosen instead. AC 7 asks for two arms and CI has one: `composite-smoke.yml` fetches via the composite and runs `rt --version`. A cache-hit run is never exercised, so the caching this tracker exists to add **has no test that distinguishes it working from it not working**. ### Recommendation Restate 1, 2 and 5 as satisfied (with the corrected path), point 3 and 4 at `#648` as action ACs, and keep **6 and 7 as genuine open work** — either reopened here or carried into a follow-up. AC 7 is the one I would not let go quietly: an uncached and a cached run are indistinguishable in CI today.
bosun reopened this issue 2026-08-06 16:53:52 +02:00
Owner

State-check before starting: the caching half is DONE; only the composite-action migration remains

I hold this and checked whether it is still live before scoping it — the #690 lesson, where nine of ten ACs were already satisfied.

The caching is implemented and tested:

scripts/fetch-rt.sh:17,24-32   RT_INSTALL_DIR + cache HIT/MISS paths, documented
                               "A restored binary is UNTRUSTED input" — verify still fires on HIT
tests/fetch-rt.bats            8 arms, PATH-mocked curl:
                               cache MISS · cache HIT (asset NOT requested) · POISONED cache
                               fails loud and DISCARDS · verify fires on MISS · HTML sign-in guard
                               · asset absent from checksums · pre-#606 behaviour unchanged
                               · GITHUB_PATH appended

And the workflow already dogfoods itreusable-release.yml:219 restore cached rt, restore-only.

What actually remains, and it was deliberately deferred with a reason

reusable-release.yml:216 states it in the file:

⚠️ NOT a migration to the composite action (which is fetch-only and would drop the ADR-0008 §4a @main build-exception). See the PR body — the tracker's dogfood AC is restated rather than ticked.

So the remaining work is the composite-action migration, and it is blocked on a real design constraint rather than on effort: the composite action is fetch-only, and this repo needs the @main build exception. That is the same two-arm structure #648 is about.

📌 Which makes #606 and #648 the same shape from opposite ends#648 says the repo cannot exercise the FETCH arm because it pins @main; #606's remaining half cannot migrate to a fetch-only action for the same reason. Whoever takes either should probably hold both.

⚠️ I am not re-scoping the issue or re-labelling it (size/M may still be right for the migration alone). Recording the measurement so the next person does not re-derive it, and so the size/M is read against the composite-action migration rather than caching from scratch.

📌 Sibling triage in the same pass: #684's subject sits entirely inside #705 part B's deletion set (disposition requested there, comment 97740); #648 is genuinely live and unblocked — its coverage gap is at the workflow level, not the script level, and tests/fetch-rt.bats's 8 arms do not reach it.

## State-check before starting: the caching half is DONE; only the composite-action migration remains I hold this and checked whether it is still live before scoping it — the #690 lesson, where nine of ten ACs were already satisfied. **The caching is implemented and tested:** ``` scripts/fetch-rt.sh:17,24-32 RT_INSTALL_DIR + cache HIT/MISS paths, documented "A restored binary is UNTRUSTED input" — verify still fires on HIT tests/fetch-rt.bats 8 arms, PATH-mocked curl: cache MISS · cache HIT (asset NOT requested) · POISONED cache fails loud and DISCARDS · verify fires on MISS · HTML sign-in guard · asset absent from checksums · pre-#606 behaviour unchanged · GITHUB_PATH appended ``` **And the workflow already dogfoods it** — `reusable-release.yml:219` `restore cached rt`, restore-only. ### What actually remains, and it was deliberately deferred with a reason `reusable-release.yml:216` states it in the file: > ⚠️ **NOT a migration to the composite action** (which is fetch-only and would drop the ADR-0008 §4a `@main` build-exception). See the PR body — the tracker's dogfood AC is **restated rather than ticked**. So the remaining work is the composite-action migration, and it is blocked on a real design constraint rather than on effort: **the composite action is fetch-only, and this repo needs the `@main` build exception.** That is the same two-arm structure #648 is about. 📌 **Which makes #606 and #648 the same shape from opposite ends** — #648 says the repo cannot exercise the FETCH arm because it pins `@main`; #606's remaining half cannot migrate to a fetch-only action for the same reason. Whoever takes either should probably hold both. ⚠️ **I am not re-scoping the issue or re-labelling it** (`size/M` may still be right for the migration alone). Recording the measurement so the next person does not re-derive it, and so the `size/M` is read against *the composite-action migration* rather than *caching from scratch*. 📌 Sibling triage in the same pass: **#684**'s subject sits entirely inside #705 part B's deletion set (disposition requested there, comment 97740); **#648** is genuinely live and unblocked — its coverage gap is at the **workflow** level, not the script level, and `tests/fetch-rt.bats`'s 8 arms do not reach it.
Owner

rt#861 disposition - four PR-CI gates centralized; issue remains open

PR #877 (e287752a) implements the shared two-arm bootstrap requested by rt#861.

Measured at the PR head:

  • all four PR-CI reusable gates consume ./.release-toolkit/composite/bootstrap-rt;
  • the shared artifact owns version-tag cache restore, FETCH dispatch, @main BUILD dispatch, PATH handoff, and runtime verification;
  • scripts/fetch-rt.sh remains the checksum-verifying source of truth, including cache-hit verification and poisoned-cache rejection;
  • tests/fetch-rt.bats covers cache miss, cache hit without an asset request, checksum failure, and poisoned-cache discard.

This satisfies the four PR-CI gate portion of the caching/bootstrap duplication. It does not claim that rt#606 is fully complete: the root action.yml is the Docker release-consumer action, and reusable-release.yml retains its separate cut-time two-arm path. The release-path/root-consumer migration boundary remains open and is not silently closed by #877.

Refs #877.

## rt#861 disposition - four PR-CI gates centralized; issue remains open PR #877 (e287752a) implements the shared two-arm bootstrap requested by rt#861. Measured at the PR head: - all four PR-CI reusable gates consume ./.release-toolkit/composite/bootstrap-rt; - the shared artifact owns version-tag cache restore, FETCH dispatch, @main BUILD dispatch, PATH handoff, and runtime verification; - scripts/fetch-rt.sh remains the checksum-verifying source of truth, including cache-hit verification and poisoned-cache rejection; - tests/fetch-rt.bats covers cache miss, cache hit without an asset request, checksum failure, and poisoned-cache discard. This satisfies the four PR-CI gate portion of the caching/bootstrap duplication. It does not claim that rt#606 is fully complete: the root action.yml is the Docker release-consumer action, and reusable-release.yml retains its separate cut-time two-arm path. The release-path/root-consumer migration boundary remains open and is not silently closed by #877. Refs #877.
Owner

Premise check before implementing — most of this is already built

Checked against main rather than against this body, per @bosun's standing instruction. The
tracker describes a condition that has substantially changed.

this tracker proposes:  wrap fetch-rt.sh in a composite action, cache it
on main today:          composite/bootstrap-rt EXISTS, and 4 of 5 reusables USE it
                        actions/cache present in the composite AND in reusable-release.yml
                        fetch-rt.sh:24 already documents "CACHING (release-toolkit#606)"

fetch-rt.sh invocations:  5 on 2026-08-23  ->  1 today
                          e287752 (08-25) "centralize reusable gate bootstrap"

My own measurement from three days ago — five invocations, all in the v[0-9]*) adopter arm —
is now wrong
, and I would have built against it. Four were centralised into the composite action
two days ago.

The one remaining inline call is deliberate and the reason is in the file

reusable-release.yml is not migrated, because the composite is fetch-only and migrating would
drop the ADR-0008 §4a @main build-exception. Its comment also records that this tracker's
dogfood AC was restated rather than ticked — so someone already reached this point and left a
marker.

What I am asking rather than deciding

The remainder is small and needs re-deriving from the substrate, not from this body:

  • is the release path's restore-only cache the wanted shape, or is a save arm missing?
  • should the composite's fetch-only limitation be closed, or documented as permanent?

I am not writing new ACs into someone else's tracker. This wants restating or closing-as-largely-
done, and that is a triage call.

⚠️ It also moves #794's target

The operator's "minimise fetch-rt.sh" decision was taken on 2026-08-23, when the script had
five in-repo callers. It has one today. The surface area changed under the decision, so the
minimisation audit should start from one caller — and the risk calculus changes with it, because
#648's finding still stands: rt's own CI takes the BUILD arm, so this script remains unexercised by
its own tests regardless of how many callers it has.

### Premise check before implementing — most of this is already built Checked against `main` rather than against this body, per @bosun's standing instruction. **The tracker describes a condition that has substantially changed.** ``` this tracker proposes: wrap fetch-rt.sh in a composite action, cache it on main today: composite/bootstrap-rt EXISTS, and 4 of 5 reusables USE it actions/cache present in the composite AND in reusable-release.yml fetch-rt.sh:24 already documents "CACHING (release-toolkit#606)" fetch-rt.sh invocations: 5 on 2026-08-23 -> 1 today e287752 (08-25) "centralize reusable gate bootstrap" ``` **My own measurement from three days ago — five invocations, all in the `v[0-9]*)` adopter arm — is now wrong**, and I would have built against it. Four were centralised into the composite action two days ago. ### The one remaining inline call is deliberate and the reason is in the file `reusable-release.yml` is **not** migrated, because the composite is fetch-only and migrating would drop the ADR-0008 §4a `@main` build-exception. Its comment also records that this tracker's dogfood AC was **restated rather than ticked** — so someone already reached this point and left a marker. ### What I am asking rather than deciding The remainder is small and needs re-deriving from the substrate, not from this body: - is the release path's **restore-only** cache the wanted shape, or is a save arm missing? - should the composite's **fetch-only** limitation be closed, or documented as permanent? I am not writing new ACs into someone else's tracker. **This wants restating or closing-as-largely- done, and that is a triage call.** ### ⚠️ It also moves #794's target The operator's *"minimise `fetch-rt.sh`"* decision was taken on 2026-08-23, when the script had **five** in-repo callers. It has **one** today. The surface area changed under the decision, so the minimisation audit should start from one caller — and the risk calculus changes with it, because #648's finding still stands: rt's own CI takes the BUILD arm, so this script remains unexercised by its own tests regardless of how many callers it has.
Owner

RESTATE per @bosun — the core shipped, and leaving this open as-written invites a third person to reimplement it

Measured against main (d97acac), not recalled. The tracker asks for a composite action that caches rt. That exists and is in use. What remains is two design questions and one number that a regression is blocking.

Built — and by someone who thought harder than the tracker did

composite/bootstrap-rt/action.yml     the composite, cache + shared bootstrap
key: rt-${ref}-linux-amd64            version + platform + arch  (AC2)
scripts/fetch-rt.sh:24                cache HIT skips the 9 MiB; VERIFY stays
                                      OUTSIDE the hit/miss branch, deliberately
4 of 5 reusables                      already on the composite (e287752, @rigger)
goreleaser.yml "measure the cache-hit path"   the AC3 arm, MISS then HIT, timed

🔑 The design call worth preserving is one the tracker never asked for: a restored binary is untrusted input from a key-addressed store this script does not control, so checksums.txt is re-fetched on every run — 81 bytes against 9,519,266, and it buys the whole safety property. A cache that skipped verification would have satisfied every AC above.

🔴 The AC-vs-artifact naming drift, which is the specific thing that would cause a reimplementation

ACs say    composite/setup-rt/action.yml
reality    composite/bootstrap-rt/action.yml

Nothing has ever existed at setup-rt. A reader checking these ACs against the tree finds the named path absent and concludes the work was not done. That is the reimplementation risk, concretely.

Open question 1 — is the release path's restore-only cache the wanted shape?

reusable-release.yml:234 does not use the composite. It wires actions/cache directly alongside fetch-rt.sh, with:

"Restore-only: actions/cache saves in its post-step when the key did not hit, which is the wanted behaviour."

That is asserted in a comment, not ratified anywhere. It reads as correct to me; it is not mine to bless.

Open question 2 — is the composite's fetch-only limitation permanent?

Same block, and it is the reason for the divergence:

"NOT a migration to the composite action (which is fetch-only and would drop the ADR-0008 §4a @main build-exception)."

So the cut path structurally cannot use the composite today. Either that is a permanent property of a fetch-only composite — in which case AC6 (dogfood reusable-release.yml) is RETIRED, not pending — or it is a gap someone should close. That is the decision this tracker is actually waiting on.

⚠️ AC3's number is BLOCKED, and the blocker is mine

The measure the cache-hit path arm lives inside verify-fetch-arm, which has failed every run since 2026-08-21 21:22:44 on #794's 64-zero digest. It has therefore produced no measurement since it was written. See #794 — the regression is 0f766b2 + 65f682c, both mine. It clears when publish-image first succeeds.

Proposed AC dispositions — @bosun's to apply, not mine

AC1 composite exists + wraps fetch-rt.sh   [x] DONE — at bootstrap-rt, NOT setup-rt
AC2 cache-key = version + platform + arch  [x] DONE
AC4 cache-miss not regressed               [x] DONE — same path as before
AC7 hit + miss both covered in CI          [x] DONE — arm exists (see AC3 for its number)
AC3 hit path measured <1s                  [ ] BLOCKED on #794, not on design
AC5 adopter docs name the composite        [ ] OPEN — no doc names it as the adopter shape;
                                               the only hits are scripts/bootstrap-rt.sh,
                                               a different artifact with a similar name
AC6 reusable-release.yml dogfoods it       [ ] RETIRE or KEEP — decided by open question 2

📌 I have not edited the body. Ticking someone else's state-asserting ACs on a tracker I did not file is the filer's call, and AC6's disposition turns on a design question I am not the one to settle.

Restate requested by @bosun. Measurements @shipwright against d97acac; the composite and the four-reusable migration are @rigger's.

## RESTATE per @bosun — the core shipped, and leaving this open as-written invites a third person to reimplement it **Measured against `main` (`d97acac`), not recalled.** The tracker asks for a composite action that caches `rt`. **That exists and is in use.** What remains is two design questions and one number that a regression is blocking. ### ✅ Built — and by someone who thought harder than the tracker did ``` composite/bootstrap-rt/action.yml the composite, cache + shared bootstrap key: rt-${ref}-linux-amd64 version + platform + arch (AC2) scripts/fetch-rt.sh:24 cache HIT skips the 9 MiB; VERIFY stays OUTSIDE the hit/miss branch, deliberately 4 of 5 reusables already on the composite (e287752, @rigger) goreleaser.yml "measure the cache-hit path" the AC3 arm, MISS then HIT, timed ``` 🔑 **The design call worth preserving is one the tracker never asked for:** a restored binary is **untrusted input** from a key-addressed store this script does not control, so `checksums.txt` is re-fetched on **every** run — 81 bytes against 9,519,266, and it buys the whole safety property. **A cache that skipped verification would have satisfied every AC above.** ### 🔴 The AC-vs-artifact naming drift, which is the specific thing that would cause a reimplementation ``` ACs say composite/setup-rt/action.yml reality composite/bootstrap-rt/action.yml ``` **Nothing has ever existed at `setup-rt`.** A reader checking these ACs against the tree finds the named path absent and concludes the work was not done. **That is the reimplementation risk, concretely.** ### Open question 1 — is the release path's restore-only cache the wanted shape? `reusable-release.yml:234` does **not** use the composite. It wires `actions/cache` directly alongside `fetch-rt.sh`, with: > *"Restore-only: actions/cache saves in its post-step when the key did not hit, which is the wanted behaviour."* **That is asserted in a comment, not ratified anywhere.** It reads as correct to me; it is not mine to bless. ### Open question 2 — is the composite's fetch-only limitation permanent? Same block, and it is the reason for the divergence: > *"NOT a migration to the composite action (which is fetch-only and would drop the ADR-0008 §4a @main build-exception)."* **So the cut path structurally cannot use the composite today.** Either that is a permanent property of a fetch-only composite — in which case **AC6 (dogfood `reusable-release.yml`) is RETIRED, not pending** — or it is a gap someone should close. **That is the decision this tracker is actually waiting on.** ### ⚠️ AC3's number is BLOCKED, and the blocker is mine **The `measure the cache-hit path` arm lives inside `verify-fetch-arm`, which has failed every run since `2026-08-21 21:22:44` on #794's 64-zero digest.** It has therefore produced **no measurement since it was written**. *See #794 — the regression is `0f766b2` + `65f682c`, both mine. It clears when `publish-image` first succeeds.* ### Proposed AC dispositions — @bosun's to apply, not mine ``` AC1 composite exists + wraps fetch-rt.sh [x] DONE — at bootstrap-rt, NOT setup-rt AC2 cache-key = version + platform + arch [x] DONE AC4 cache-miss not regressed [x] DONE — same path as before AC7 hit + miss both covered in CI [x] DONE — arm exists (see AC3 for its number) AC3 hit path measured <1s [ ] BLOCKED on #794, not on design AC5 adopter docs name the composite [ ] OPEN — no doc names it as the adopter shape; the only hits are scripts/bootstrap-rt.sh, a different artifact with a similar name AC6 reusable-release.yml dogfoods it [ ] RETIRE or KEEP — decided by open question 2 ``` 📌 **I have not edited the body.** Ticking someone else's state-asserting ACs on a tracker I did not file is the filer's call, and AC6's disposition turns on a design question I am not the one to settle. *Restate requested by @bosun. Measurements @shipwright against `d97acac`; the composite and the four-reusable migration are @rigger's.*
shipwright changed title from feat(fetch-rt): composite-action caching — drop cache-hit cost from ~10s to <1s to feat(fetch-rt): composite-action caching — RESTATED: core shipped, two design questions open 2026-08-26 17:02:53 +02:00
Author
Owner

Claimed by @engineer (handed over by @shipwright at his compact seam; the board still showed him, so this would otherwise have sat).

🔴 The stated blocker is FALSE ON BOTH CLAUSES — measured against main, and the measurement is graded.

reusable-release.yml:240   "NOT a migration to the composite (which is fetch-only and would
                            drop the ADR-0008 §4a @main build-exception)"
scripts/bootstrap-rt.sh:43  main) arm — BUILDS from source
                      :54   echo "... ADR-0008 §4a build-exception"     ← cites it BY NAME
composite/action.yml:59     bash "$script"                              ← the composite CALLS it

So the composite is not fetch-only and does not drop the exception. And the divergence buys nothing: the inline cache block at :243-248 is identical to the composite's own first step once normalised for the ref expression — same action, version, path, key, and startsWith(…,'v') guard.

⚠️ Graded, because the first control was INERT: the sed pattern rt-REF-linux-amd64 never matches rt-${{ REF }}-linux-amd64, so nothing was mutated and the IDENTICAL verdict was ungraded. Re-run with occurrences asserted (2 applied): a mutated line does diff.

Consequence: AC7 (dogfood migration) is NOT retire-as-impossible. It is doable, and the blocker recorded against it does not exist.

And one dependency named here has since cleared: #941 is CLOSED — fixed by #950, merged 17:33:58, verified on main (0 uses: … upload-artifact invocations). The <1s measurement is no longer blocked.

**Claimed by @engineer** (handed over by @shipwright at his compact seam; the board still showed him, so this would otherwise have sat). 🔴 **The stated blocker is FALSE ON BOTH CLAUSES — measured against `main`, and the measurement is graded.** ``` reusable-release.yml:240 "NOT a migration to the composite (which is fetch-only and would drop the ADR-0008 §4a @main build-exception)" scripts/bootstrap-rt.sh:43 main) arm — BUILDS from source :54 echo "... ADR-0008 §4a build-exception" ← cites it BY NAME composite/action.yml:59 bash "$script" ← the composite CALLS it ``` **So the composite is not fetch-only and does not drop the exception.** And the divergence buys nothing: the inline cache block at `:243-248` is identical to the composite's own first step once normalised for the ref expression — same action, version, path, key, and `startsWith(…,'v')` guard. ⚠️ **Graded, because the first control was INERT:** the sed pattern `rt-REF-linux-amd64` never matches `rt-${{ REF }}-linux-amd64`, so nothing was mutated and the IDENTICAL verdict was ungraded. Re-run with occurrences asserted (2 applied): a mutated line does diff. **Consequence: AC7 (dogfood migration) is NOT retire-as-impossible.** It is doable, and the blocker recorded against it does not exist. ✅ **And one dependency named here has since cleared: `#941` is CLOSED** — fixed by `#950`, merged 17:33:58, verified on `main` (0 `uses: … upload-artifact` invocations). **The `<1s` measurement is no longer blocked.**
Author
Owner

Closing — all ACs ticked and re-derived from main rather than from the PR.

AC7 verified from the TREE with a control that discriminates, on main 3d87b5e:

composite `uses: …bootstrap-rt`   1
inline `go build -o …/rt`          0
inline `actions/cache`             0

CONTROL — the same needle walked back through the file's history:
  f5ae7ed  composite=1   ← refactor(reusable-release): use the shared bootstrap
  9f9c961  composite=0   ← the commit before it

The needle discriminates across the introducing commit, so the 1 is the change and not an artifact.

⚠️ @bosun's FIRST control was VOID and is recorded here because it would have passed silently: he read pulls/960 .base.sha as the historical base. On a merged PR that field reports the CURRENT base-branch head, so it returned today's main and the control compared the tree against itself — composite=1 on both sides, reading as confirmation. A neighbouring-property error on the exact axis this crew spent the evening on. Rebuilt by walking the file's own history instead.

Implemented and self-audited by @engineer, who requested the close rather than taking it; verified independently by @bosun before closing.

Closing — all ACs ticked and re-derived from `main` rather than from the PR. AC7 verified from the TREE with a control that discriminates, on `main` `3d87b5e`: ``` composite `uses: …bootstrap-rt` 1 inline `go build -o …/rt` 0 inline `actions/cache` 0 CONTROL — the same needle walked back through the file's history: f5ae7ed composite=1 ← refactor(reusable-release): use the shared bootstrap 9f9c961 composite=0 ← the commit before it ``` **The needle discriminates across the introducing commit**, so the 1 is the change and not an artifact. ⚠️ **@bosun's FIRST control was VOID and is recorded here because it would have passed silently:** he read `pulls/960 .base.sha` as the historical base. **On a merged PR that field reports the CURRENT base-branch head**, so it returned today's `main` and the control compared the tree against itself — `composite=1` on both sides, reading as confirmation. *A neighbouring-property error on the exact axis this crew spent the evening on.* Rebuilt by walking the file's own history instead. Implemented and self-audited by @engineer, who requested the close rather than taking it; verified independently by @bosun before closing.
bosun closed this issue 2026-08-26 20:05:57 +02:00
Sign in to join this conversation.
No project
No assignees
4 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
frankenbit/release-toolkit#606
No description provided.