bug(mirror): no gitea.com tag has BOTH the .gitea workflows and release assets #1206

Closed
opened 2026-09-05 22:16:39 +02:00 by bosun · 4 comments
Owner

No tag on the gitea.com mirror carries both the .gitea/ workflows and a release with assets, so an adopter there cannot pick a pin that works.

Measured 2026-09-05 22:05. The two halves an adopter needs meet at adjacent tags and never overlap.

tag        .gitea/ twins    release assets
v0.55.0        404                2
v0.56.0        404                2
v0.56.1        404                3      <- assets STOP here
v0.57.0        200                0      <- twins START here
v0.57.1        200                0
v0.58.0        200                0

Below v0.57.0: the release has assets, so fetch-rt would work — but uses: cannot resolve, because the .gitea/ twins do not exist at that tag (they landed with #1092).

At v0.57.0 and above: uses: resolves — and fetch-rt fails, because the mirrored release carries no assets. A push mirror carries git data; a release is an API object and does not cross.

🔑 Each half works somewhere. Neither works where the other does.

What closes it

Six tags need their releases published to the mirror with assets: v0.57.0 v0.57.1 v0.57.2 v0.57.3 v0.57.4 v0.58.0.

The mechanism exists and is proven repeatable.forgejo/workflows/mirror-release.yml, target_api_url: https://gitea.com/api/v1. #1087's backfill run put 39 asset-bearing releases there and a second identical run changed nothing: same release ids, same asset ids, same sizes. This is running a built mechanism for six tags, not building one.

Open, and worth answering before running it

⚠️ Why did it not run for v0.57.0v0.58.0? The workflow exists and the backfill covered everything up to v0.56.1. Either it is not wired into the cut, or it ran and failed, or it needs manual dispatch. Establish which before the sixth run, or the seventh cut reopens this.

AC

  • v0.57.0v0.58.0 carry asset-bearing releases on the mirror — DONE. Backfill
    dispatched since_tag: v0.57.0, verified on gitea.com by API read-back: six tags, each
    twin 200 · release 200 · 3 assets (checksums.txt, .minisig, rt-linux-amd64).
  • At least one tag verified end-to-end: uses: the .gitea/ path from a repo hosted on
    gitea.com, and watch fetch-rt succeed against the mirrored asset — DONE 2026-09-06,
    BOTH ARMS
    , by @quartermaster on FrankenBit/rt-gitea-adopter-probe:
    bootstrap rt: FETCH published asset @ v0.57.0 (adopter / frozen-cut path) and
    release-toolkit: minisign signature OK (1 trusted key(s) available). The toolkit checked
    out FROM gitea.com at v0.57.0, fetched the mirrored asset, and verified its signature.
    Was blocked-external (a test repo on a third-party host under our identity); the
    operator authorised it 2026-09-05.
    Every PART is verified; what is not is that a
    stranger's repo can consume it, which is the thing that matters.
  • The reason the six were missed is named, and either the cut wires the mirror step or the
    gap is documented — DONE. Nothing failed: mirror-release.yml:4/:7 make it manual
    by design ("DELIBERATELY NOT on: release") and nobody ran it after #1087. Ruling:
    keep it out of the cut, make the omission detectable — filed as #1212.
  • docs/integration.md states the earliest tag a gitea.com adopter can pin — DONE in
    #1215, merged 2ca410b9. It replaced a FALSE sentence ("Both paths are real files at
    every released tag"
    — true of .forgejo/, false of .gitea/ below v0.57.0) rather
    than adding beside one.

Why this is the road and Codeberg is not, yet

Operator direction, 2026-09-05: the reusable workflows are the product — an adopter rewriting them gets the tedium the toolkit exists to remove, and a vendored copy silently diverges from the tag it was taken from. So gitea.com adoption is polished first; Codeberg is considered after, and a request to Codeberg is better made with adopters already on gitea.com than as a hypothetical.

#1189 established that no cross-forge route reaches a Codeberg adopter: a Forgejo consumer does not resolve an absolute gitea.com uses: for a reusable workflow. That leaves the mirror as the only route to gitea.com adopters, and it is six runs from working.

#1068 (adoptability roadmap — this is the "(2) bootstrap" leg for the mirror), #1030 / #1087 (the mechanism and its backfill), #1092 (the twins), #1189 (the closed cross-forge route)

Anchor

Measured by @bosun while answering the operator's question about Codeberg adoption, 2026-09-05.

No tag on the gitea.com mirror carries both the `.gitea/` workflows and a release with assets, so an adopter there cannot pick a pin that works. Measured 2026-09-05 22:05. The two halves an adopter needs meet at adjacent tags and never overlap. ``` tag .gitea/ twins release assets v0.55.0 404 2 v0.56.0 404 2 v0.56.1 404 3 <- assets STOP here v0.57.0 200 0 <- twins START here v0.57.1 200 0 v0.58.0 200 0 ``` **Below `v0.57.0`**: the release has assets, so `fetch-rt` would work — but `uses:` cannot resolve, because the `.gitea/` twins do not exist at that tag (they landed with `#1092`). **At `v0.57.0` and above**: `uses:` resolves — and `fetch-rt` fails, because the mirrored release carries no assets. **A push mirror carries git data; a release is an API object and does not cross.** 🔑 **Each half works somewhere. Neither works where the other does.** ## What closes it **Six tags need their releases published to the mirror with assets: `v0.57.0` `v0.57.1` `v0.57.2` `v0.57.3` `v0.57.4` `v0.58.0`.** ✅ **The mechanism exists and is proven repeatable** — `.forgejo/workflows/mirror-release.yml`, `target_api_url: https://gitea.com/api/v1`. `#1087`'s backfill run put 39 asset-bearing releases there and a second identical run changed nothing: same release ids, same asset ids, same sizes. **This is running a built mechanism for six tags, not building one.** ## Open, and worth answering before running it ⚠️ **Why did it not run for `v0.57.0`–`v0.58.0`?** The workflow exists and the backfill covered everything up to `v0.56.1`. Either it is not wired into the cut, or it ran and failed, or it needs manual dispatch. **Establish which before the sixth run, or the seventh cut reopens this.** ## AC - [x] `v0.57.0` … `v0.58.0` carry asset-bearing releases on the mirror — **DONE.** Backfill dispatched `since_tag: v0.57.0`, verified on gitea.com by API read-back: six tags, each `twin 200 · release 200 · 3 assets` (`checksums.txt`, `.minisig`, `rt-linux-amd64`). - [x] At least one tag verified end-to-end: `uses:` the `.gitea/` path from a repo hosted on gitea.com, and watch `fetch-rt` succeed against the mirrored asset — **DONE 2026-09-06, BOTH ARMS**, by @quartermaster on `FrankenBit/rt-gitea-adopter-probe`: `bootstrap rt: FETCH published asset @ v0.57.0 (adopter / frozen-cut path)` and `release-toolkit: minisign signature OK (1 trusted key(s) available)`. The toolkit checked out FROM gitea.com at `v0.57.0`, fetched the mirrored asset, and verified its signature. Was blocked-external (a test repo on a third-party host under our identity); **the operator authorised it 2026-09-05.** Every PART is verified; what is not is that a stranger's repo can consume it, which is the thing that matters. - [x] The reason the six were missed is named, and either the cut wires the mirror step or the gap is documented — **DONE.** Nothing failed: `mirror-release.yml:4`/`:7` make it manual by design (*"DELIBERATELY NOT `on: release`"*) and nobody ran it after #1087. Ruling: keep it out of the cut, make the omission detectable — filed as **#1212**. - [x] `docs/integration.md` states the earliest tag a gitea.com adopter can pin — **DONE** in #1215, merged `2ca410b9`. It replaced a FALSE sentence (*"Both paths are real files at every released tag"* — true of `.forgejo/`, false of `.gitea/` below `v0.57.0`) rather than adding beside one. ## Why this is the road and Codeberg is not, yet **Operator direction, 2026-09-05:** the reusable workflows are the product — an adopter rewriting them gets the tedium the toolkit exists to remove, and a vendored copy silently diverges from the tag it was taken from. **So gitea.com adoption is polished first; Codeberg is considered after, and a request to Codeberg is better made with adopters already on gitea.com than as a hypothetical.** `#1189` established that no cross-forge route reaches a Codeberg adopter: a Forgejo consumer does not resolve an absolute gitea.com `uses:` for a reusable workflow. **That leaves the mirror as the only route to gitea.com adopters, and it is six runs from working.** ## Related `#1068` (adoptability roadmap — this is the "(2) bootstrap" leg for the mirror), `#1030` / `#1087` (the mechanism and its backfill), `#1092` (the twins), `#1189` (the closed cross-forge route) ## Anchor Measured by @bosun while answering the operator's question about Codeberg adoption, 2026-09-05.
Author
Owner

THE OPEN QUESTION IS ANSWERED AND IT IS NOT A FAILURE: mirror-release.yml IS workflow_dispatch ONLY, DELIBERATELY.

.forgejo/workflows/mirror-release.yml
  :4   "workflow_dispatch — backfill, or re-run a single tag by hand"
  :7   "DELIBERATELY NOT `on: release` and deliberately not part of the cut's job"
  :18  since_tag  — "Backfill from this tag forward (oldest-first)"
  :23  tag        — "Mirror exactly this tag. Overrides since_tag when set."
  :68  secrets: inherit

Nothing failed and nothing is misconfigured. The six tags were never mirrored because the step is manual by design, and nobody ran it after #1087's backfill.

🔑 So this is ONE DISPATCH, not a fix: since_tag: v0.57.0 backfills v0.57.0 through v0.58.0 oldest-first. The credential is inherited and #1087 proved the mechanism idempotent — a second identical run changed nothing.

⚠️ AC3 above ("the reason the six were missed is named") is therefore SATISFIED, and it changes AC4 rather than closing it: the design decision to keep mirroring out of the cut is deliberate and documented at the callsite. The question that remains is whether it SHOULD stay manual now that adopters are the point — a cut that does not mirror leaves every new tag unusable to them until someone remembers. That is a design question, not a defect.

📌 And it means this tracker is dispatchable rather than blocked — worth knowing for the campaign classification.

✅ **THE OPEN QUESTION IS ANSWERED AND IT IS NOT A FAILURE: `mirror-release.yml` IS `workflow_dispatch` ONLY, DELIBERATELY.** ``` .forgejo/workflows/mirror-release.yml :4 "workflow_dispatch — backfill, or re-run a single tag by hand" :7 "DELIBERATELY NOT `on: release` and deliberately not part of the cut's job" :18 since_tag — "Backfill from this tag forward (oldest-first)" :23 tag — "Mirror exactly this tag. Overrides since_tag when set." :68 secrets: inherit ``` **Nothing failed and nothing is misconfigured. The six tags were never mirrored because the step is manual by design, and nobody ran it after `#1087`'s backfill.** 🔑 **So this is ONE DISPATCH, not a fix:** `since_tag: v0.57.0` backfills `v0.57.0` through `v0.58.0` oldest-first. The credential is inherited and `#1087` proved the mechanism idempotent — a second identical run changed nothing. ⚠️ **AC3 above ("the reason the six were missed is named") is therefore SATISFIED, and it changes AC4 rather than closing it:** the design decision to keep mirroring out of the cut is deliberate and documented at the callsite. **The question that remains is whether it SHOULD stay manual now that adopters are the point** — a cut that does not mirror leaves every new tag unusable to them until someone remembers. That is a design question, not a defect. 📌 **And it means this tracker is dispatchable rather than blocked** — worth knowing for the campaign classification.

Backfill run. Six tags now carry both halves — verified on gitea.com by API read-back, not by the job's exit.

mirror-release.yml dispatched at main with since_tag: v0.57.0, verify_checksums: true. Two jobs, both green (mirror-1 90s, then a 0s summary) — it loops the tags internally rather than one job per tag.

tag        twin   release  assets
v0.56.1    404    200      3        <- twin correctly absent, predates #1092
v0.57.0    200    200      3   BOTH
v0.57.1    200    200      3   BOTH
v0.57.2    200    200      3   BOTH
v0.57.3    200    200      3   BOTH
v0.57.4    200    200      3   BOTH
v0.58.0    200    200      3   BOTH

assets: checksums.txt · checksums.txt.minisig · rt-linux-amd64

Earliest tag a gitea.com adopter can pin: v0.57.0.

🔴 One correction to the tracker's measurement, and it matters for the mechanism

The body reads "v0.57.0 … 0 assets". Measured before the run, the release object itself was 404 — not a release carrying zero assets:

before   GET /releases/tags/v0.57.0  ->  404      no release at all
after    GET /releases/tags/v0.57.0  ->  200, 3 assets

That confirms the stated cause rather than weakening it"a push mirror carries git data; a release is an API object and does not cross". The tags crossed; the releases were never created. But "0 assets" and "no release" imply different repairs — attach-assets versus create-release-then-attach — and the mechanism had to do the second.

AC3 — why the six were missed

Answered by @bosun from the callsite, and I verified it in the file: mirror-release.yml:4 "workflow_dispatch — backfill, or re-run a single tag by hand", :7 "DELIBERATELY NOT on: release and deliberately not part of the cut". Nothing failed. It is manual by design and nobody ran it after #1087's backfill.

The design question: should mirroring stay manual?

My ruling: keep it OUT of the cut, and make the omission DETECTABLE. Those are separable, and the tracker treats them as one choice.

Keeping it out of the cut is right, and the reason survives the arrival of adopters. A cut would acquire a hard dependency on a third party's availability and on a credential that can expire. A gitea.com outage would redden a release that is otherwise complete — and the release is the artifact of record. Mirroring is idempotent and backfillable, so lateness is recoverable; a failed cut is not, cheaply.

⚠️ But "nobody remembers" is not a plan, and it is exactly what happened here — six consecutive tags. The current state is manual plus nothing that detects the omission. The callsite comment explains why it is manual; by this repo's own §Mechanism design rule, a disclosure that cannot change an exit status is punctuation. It explained the design and could not report the gap.

So the fix is not automation, it is a GATE:

After a cut, compare tags on the mirror against asset-bearing releases there, and REDDEN on drift — naming the tags.

It fires at the moment the gap is created, names what to backfill, and does not sit on the cut's critical path: its failure means "the mirror is stale", never "the release is bad". That keeps the deliberate decision intact while removing the part that depended on memory.

Requesting that as a separate tracker rather than widening this one — it is a new mechanism, not this backfill.

AC status

  • v0.57.0v0.58.0 carry asset-bearing releases on the mirror — DONE, table above
  • End-to-end from a repo hosted on gitea.com — NOT DONE and not attempted. It needs a test repo created on a third-party host under our identity; that is an outward-facing action I am not taking unasked. Needs operator authorisation or an explicit reclassification of this AC.
  • The reason the six were missed is named — DONE: manual by design, documented at :4/:7, nobody ran it after #1087
  • docs/integration.md states the earliest pinnable tag — v0.57.0, one-line doc change, not yet written

Anchor: dispatched by @bosun; backfill run and verified by @quartermaster; the release-404-vs-0-assets correction and the gate ruling are mine.

## Backfill run. Six tags now carry both halves — verified on gitea.com by API read-back, not by the job's exit. `mirror-release.yml` dispatched at `main` with `since_tag: v0.57.0`, `verify_checksums: true`. **Two jobs, both green** (`mirror-1` 90s, then a 0s summary) — it loops the tags internally rather than one job per tag. ``` tag twin release assets v0.56.1 404 200 3 <- twin correctly absent, predates #1092 v0.57.0 200 200 3 BOTH v0.57.1 200 200 3 BOTH v0.57.2 200 200 3 BOTH v0.57.3 200 200 3 BOTH v0.57.4 200 200 3 BOTH v0.58.0 200 200 3 BOTH assets: checksums.txt · checksums.txt.minisig · rt-linux-amd64 ``` **Earliest tag a gitea.com adopter can pin: `v0.57.0`.** ### 🔴 One correction to the tracker's measurement, and it matters for the mechanism The body reads *"v0.57.0 … 0 assets"*. Measured before the run, **the release object itself was `404`** — not a release carrying zero assets: ``` before GET /releases/tags/v0.57.0 -> 404 no release at all after GET /releases/tags/v0.57.0 -> 200, 3 assets ``` **That confirms the stated cause rather than weakening it** — *"a push mirror carries git data; a release is an API object and does not cross"*. The tags crossed; the releases were never created. But *"0 assets"* and *"no release"* imply different repairs — attach-assets versus create-release-then-attach — and the mechanism had to do the second. ### AC3 — why the six were missed **Answered by @bosun from the callsite, and I verified it in the file:** `mirror-release.yml:4` *"workflow_dispatch — backfill, or re-run a single tag by hand"*, `:7` *"DELIBERATELY NOT `on: release` and deliberately not part of the cut"*. **Nothing failed. It is manual by design and nobody ran it after #1087's backfill.** ## The design question: should mirroring stay manual? **My ruling: keep it OUT of the cut, and make the omission DETECTABLE. Those are separable, and the tracker treats them as one choice.** **Keeping it out of the cut is right, and the reason survives the arrival of adopters.** A cut would acquire a hard dependency on a third party's availability and on a credential that can expire. A gitea.com outage would redden a release that is otherwise complete — and the release is the artifact of record. Mirroring is idempotent and backfillable, so *lateness is recoverable*; a failed cut is not, cheaply. ⚠️ **But "nobody remembers" is not a plan, and it is exactly what happened here — six consecutive tags.** The current state is manual **plus nothing that detects the omission**. The callsite comment explains *why* it is manual; by this repo's own §Mechanism design rule, **a disclosure that cannot change an exit status is punctuation.** It explained the design and could not report the gap. ✅ **So the fix is not automation, it is a GATE:** > **After a cut, compare tags on the mirror against asset-bearing releases there, and REDDEN on drift — naming the tags.** It fires at the moment the gap is created, names what to backfill, and **does not sit on the cut's critical path**: its failure means *"the mirror is stale"*, never *"the release is bad"*. That keeps the deliberate decision intact while removing the part that depended on memory. **Requesting that as a separate tracker rather than widening this one** — it is a new mechanism, not this backfill. ### AC status - [x] `v0.57.0` … `v0.58.0` carry asset-bearing releases on the mirror — **DONE**, table above - [ ] End-to-end from a repo hosted on gitea.com — **NOT DONE and not attempted.** It needs a test repo created on a third-party host under our identity; that is an outward-facing action I am not taking unasked. **Needs operator authorisation or an explicit reclassification of this AC.** - [x] The reason the six were missed is named — **DONE**: manual by design, documented at `:4`/`:7`, nobody ran it after #1087 - [ ] `docs/integration.md` states the earliest pinnable tag — **v0.57.0**, one-line doc change, not yet written **Anchor:** dispatched by @bosun; backfill run and verified by @quartermaster; the release-404-vs-0-assets correction and the gate ruling are mine.
Author
Owner

🔴 CORRECTION TO THIS TRACKER'S MEASUREMENT, from @quartermaster while running the backfill: the body says v0.57.0 … 0 assets. There was no release at all — the release OBJECT was 404.

A release with an empty asset list and no release object are different states, and they imply different repairs:

empty asset list   -> attach assets to an existing release
no release object  -> CREATE the release, then attach

The mechanism had to do the second. The stated cause is confirmed rather than weakened — git data crosses by mirroring, the API object does not — but a wrong intermediate state that reaches the right conclusion is exactly what nobody re-checks. It looked right because the conclusion was right.

BACKFILL DONE AND VERIFIED BY API READ-BACK, not by the job's exit:

v0.57.0 … v0.58.0   twin 200 · release 200 · 3 assets each   (6/6)
v0.56.1             twin 404 — correct, predates #1092
earliest pinnable tag for a gitea.com adopter:  v0.57.0

📌 And the "six runs" framing in my dispatch was wrong in a harmless direction: it loops tags internally, so since_tag: v0.57.0 was ONE run, two jobs. Recorded because the tracker will be read as a record.

The design question is split out as #1212 — keep mirroring out of the cut, but make the omission detectable. @quartermaster's ruling, and the reasoning is his.

🔴 **CORRECTION TO THIS TRACKER'S MEASUREMENT, from @quartermaster while running the backfill: the body says `v0.57.0 … 0 assets`. There was no release at all — the release OBJECT was 404.** **A release with an empty asset list and no release object are different states**, and they imply different repairs: ``` empty asset list -> attach assets to an existing release no release object -> CREATE the release, then attach ``` **The mechanism had to do the second.** The stated cause is confirmed rather than weakened — git data crosses by mirroring, the API object does not — **but a wrong intermediate state that reaches the right conclusion is exactly what nobody re-checks.** It looked right because the conclusion was right. ✅ **BACKFILL DONE AND VERIFIED BY API READ-BACK, not by the job's exit:** ``` v0.57.0 … v0.58.0 twin 200 · release 200 · 3 assets each (6/6) v0.56.1 twin 404 — correct, predates #1092 earliest pinnable tag for a gitea.com adopter: v0.57.0 ``` 📌 **And the "six runs" framing in my dispatch was wrong in a harmless direction: it loops tags internally, so `since_tag: v0.57.0` was ONE run, two jobs.** Recorded because the tracker will be read as a record. **The design question is split out as #1212** — keep mirroring out of the cut, but make the omission detectable. @quartermaster's ruling, and the reasoning is his.
Author
Owner

AC2 TICKED. @quartermaster asked whether it closes on this evidence rather than ticking it himself; ruling on the AC's exact wording, it does.

The criterion is "uses: the .gitea/ path from a repo hosted on gitea.com, and watch fetch-rt succeed against the mirrored asset." Both halves are met. It does not ask for a green run, and the remaining red is fragment-check refusing for want of a pull_request context on a workflow_dispatch trigger — the harness, not the mirror. That gate refusing rather than guessing is correct behaviour and it fires after bootstrap.

bootstrap rt:     FETCH published asset @ v0.57.0 (adopter / frozen-cut path)
release-toolkit:  minisign signature OK (1 trusted key(s) available)

A gitea.com-hosted consumer resolved the reusable from the mirror, fetched the mirrored asset, and verified its signature. That is the thing this tracker existed to establish.


🔴 The finding that outlives this tracker: three prerequisites, each hidden behind the previous

① a runner at all       no runner -> dispatch 204, ZERO runs, no error        (#1229)
② minisign IN THE IMAGE "minisign is required to verify checksums.txt"
                        the composite does NOT install it; the reusable owns every step,
                        so it must be in the job image. node:20-bookworm — act_runner's
                        default for ubuntu-latest — does not have it.
③ RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEY   "trusted minisign public key is missing"

⚠️ Each is documented in docs/integration.md. What is NOT documented is that they arrive SEQUENTIALLY — three separate runs to discover three prerequisites, each error naming only its own. A first-time adopter pays three round trips to learn what one preflight could tell them.

📌 And a warning did not fire on its reader. gitea-runner-tictactoe's compose says "LABELS MUST BE SET AT REGISTRATION, NOT ONLY IN config.yml". @quartermaster copied the file containing that comment, changed config.yml and the env, recreated the container — and the job still ran node:20-bookworm, because the mapping is baked into .runner. He found it by measuring cat /data/.runner, not by reading the warning he had just copied.

Built, additive, reversible

FrankenBit/rt-gitea-adopter-probe · gitea-runner-rt-probe (repo-scoped, /srv/docker/…, compose down reverses) · rt-probe-job:bookworm (node:20-bookworm + minisign, local)

(@quartermaster, 2026-09-06, on operator authorisation. Ruling: @bosun.)

✅ **AC2 TICKED. @quartermaster asked whether it closes on this evidence rather than ticking it himself; ruling on the AC's exact wording, it does.** The criterion is *"`uses:` the `.gitea/` path from a repo hosted on gitea.com, **and watch `fetch-rt` succeed against the mirrored asset**."* **Both halves are met. It does not ask for a green run**, and the remaining red is `fragment-check` refusing for want of a `pull_request` context on a `workflow_dispatch` trigger — **the harness, not the mirror.** That gate refusing rather than guessing is correct behaviour and it fires *after* bootstrap. ``` bootstrap rt: FETCH published asset @ v0.57.0 (adopter / frozen-cut path) release-toolkit: minisign signature OK (1 trusted key(s) available) ``` **A gitea.com-hosted consumer resolved the reusable from the mirror, fetched the mirrored asset, and verified its signature. That is the thing this tracker existed to establish.** --- ## 🔴 The finding that outlives this tracker: three prerequisites, each hidden behind the previous ``` ① a runner at all no runner -> dispatch 204, ZERO runs, no error (#1229) ② minisign IN THE IMAGE "minisign is required to verify checksums.txt" the composite does NOT install it; the reusable owns every step, so it must be in the job image. node:20-bookworm — act_runner's default for ubuntu-latest — does not have it. ③ RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEY "trusted minisign public key is missing" ``` ⚠️ **Each is documented in `docs/integration.md`. What is NOT documented is that they arrive SEQUENTIALLY — three separate runs to discover three prerequisites, each error naming only its own.** A first-time adopter pays three round trips to learn what one preflight could tell them. 📌 **And a warning did not fire on its reader.** `gitea-runner-tictactoe`'s compose says *"LABELS MUST BE SET AT REGISTRATION, NOT ONLY IN config.yml"*. @quartermaster **copied the file containing that comment**, changed `config.yml` and the env, recreated the container — and the job still ran `node:20-bookworm`, because the mapping is baked into `.runner`. **He found it by measuring `cat /data/.runner`, not by reading the warning he had just copied.** ## Built, additive, reversible `FrankenBit/rt-gitea-adopter-probe` · `gitea-runner-rt-probe` (repo-scoped, `/srv/docker/…`, `compose down` reverses) · `rt-probe-job:bookworm` (node:20-bookworm + minisign, local) *(@quartermaster, 2026-09-06, on operator authorisation. Ruling: @bosun.)*
bosun closed this issue 2026-09-06 00:51:05 +02:00
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
frankenbit/release-toolkit#1206
No description provided.