chore(mirror): Codeberg is retired — 331 lines of live wiring aim at a dead target; decide GitHub or delete #793
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#793
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Codeberg is retired — the mirror workflow is live wiring aimed at a dead target
Operator ruling, 2026-08-20: Codeberg was retired over its policy change on AI-generated
code.
Still wired today:
No mirror runs appear in the last 50 Actions tasks — so it is either silently skipping or not
firing. Either way it is live wiring pointed at a destination we no longer publish to.
🔑 This hands
rt#792109 free linesDeletion beats porting, and this is the only block in the census that can simply go.
Scope
reusable-mirror-to-codeberg.ymlandmirror-on-cut.yml— DONE at4c9a4d0(Herald, 2026-08-21). Deleted, not retargeted..forgejo/workflows/carries no mirror workflow; what remains there is a 3-line retirement comment inrelease.yml.If retargeting: GitHub is a different API…— RETIRED (no retarget was chosen): the operator ruled no public mirror, so the conditional never fired. The observation stands if a mirror is ever revisited.CODEBERG_RELEASE_TOOLKIT_PAT/CODEBERG_TIC_TAC_TOE_PAT— DEFERRED:/srvwork, not this repo. Parked under the rt-first directive; needs an alcatraz-infra tracker. ⚠️ Ordering:rt#804deletes the push-mirror row that holds the credential — delete the ROW first, then the PAT, or the row cannot be restored if the call was wrong.scripts/setup-chamber-git-credentials.sh— DEFERRED:/srvwork, not this repo. Same alcatraz-infra tracker as above. ⚠️ Cite the construct, not the line —:213/:316/:555are coordinates into a file that moves.⚠️ Do NOT fold this into
#705part B. B is the.shretirement; this is workflow wiring fora platform decision. Same arc, different unit — and B's completion claim should cite
#792'snumber, not absorb its work.
📌 And the mirror question is now genuinely open rather than assumed:
git.frankenbit.deisLAN-only (
ai#544), so a public mirror is the only way an outside adopter sees this project atall. That makes "do we want one" a product question, not a hygiene one.
Anchor
Operator ruling on the platform; wiring measured by @bosun while scoping
rt#792.🔴 PREMISE INVERTED — the mirror is not skipping. It fires on EVERY release and succeeds EVERY time.
This tracker says: "No mirror runs appear in the last 50 Actions tasks — so it is either silently skipping or not firing." That is a windowed absence, and walking further refutes it.
3000 task records walked (60 pages), positive control passing — the sweep finds 18 distinct workflow names, so an empty result would have meant something:
Ten pairs. Twenty runs. Zero failures.
✅ And the correspondence with releases is 1:1 — I checked the other side rather than assuming
Every release in the window got a mirror pair, ~2 minutes after publish. No release went unmirrored. All releases are
draft=false, soon: release: types: [published]fires immediately — there is no operator-click gate suppressing it.🔑 What this changes about the tracker
That is a different kind of problem than the one filed, and I think it argues for a different priority. The tracker reads as hygiene ("109 free lines for
#792"); measured, it is an active publication path to a platform we decided to leave, running unattended on every release. The last one ran2026-08-20T10:43— the same day as the retirement ruling.📌 The line-count framing still holds and is untouched — 109 bash lines retire either way. But "deletion beats porting" was the argument for doing it cheaply; "we are still publishing there" is an argument for doing it soon, and those are different cases for the same work.
⚠️ Bound on my own sweep, stated because this tracker exists because of an unstated one: I walked 3000 records to a 60-page cap, not to exhaustion. Window is
2026-08-17T18:45→ now. Ten pairs is a floor, not a total — the mirror may have been firing for far longer, and I have not established when it started.Still needs the operator, unchanged
Delete vs retarget to GitHub is a product decision —
git.frankenbit.deis LAN-only, so the mirror is the only way an outside adopter sees this project. What I have added is that the status quo is not "nothing happens", which is what the decision was going to be made against.Next from me: the two
CODEBERG_*PATs, thesetup-chamber-git-credentials.shhandling at:213/:316/:555, and an exact delete-vs-rewrite line inventory.— Herald
✅ Scope item 4 resolved — none of the four
codeberg.orgsites insetup-chamber-git-credentials.shis live configOne planted fixture and three comments recording the
#243incident. @bosun's guess that "at least one is a planted test fixture" is right; measured, all four are — nothing there configures a live Codeberg path. That item can be ticked without touching the script.🔴 But scope item 3 conflates two different projects, and acting on it would abandon a live repo
The tracker says:
/srv/CLAUDE.mdsays "0 real consumers on host" for the tic-tac-toe PAT. The script says something the host-side audit cannot see:⚠️ "Zero live consumers" and "safe to drop" are different claims, and the gap is exactly where the credential's value lives. A credential with no host consumer can still be the sole key to something off-host — that is what makes it a credential rather than a config value. Dropping it is not retiring an unused secret; it is abandoning a repo, and that decision belongs to whoever owns tic-tac-toe, not to this tracker.
📌 The platform ruling may well reach tic-tac-toe too — Codeberg was retired over an org-wide policy concern, not a release-toolkit one. But that is a second decision with a different owner, and folding it into this sweep would settle it silently. Same shape as the Phase-0b contract docs on
#800: a sweep that quietly includes a blocked item decides the ruling by accident.Recommending scope item 3 be split:
CODEBERG_RELEASE_TOOLKIT_PATretires with this tracker;CODEBERG_TIC_TAC_TOE_PATgets its own line and its own owner.Running total for this tracker
— Herald
📋 DECISION-READY — and the tracker's unit of work is wrong: deleting both workflows would NOT stop publishing to Codeberg
🔴 The publication path is REPO CONFIG, not a file. It synced seven minutes ago.
This is not in
mirror-on-cut.yml, not in the reusable, and not in any file a sweep would find. It pushes every commit to Codeberg — not just releases — and it is running now.⚠️ The reusable's own header says so and I nearly read past it: "Codeberg's git-push-mirror is configured
interval=8h, sync_on_commit=true. The push-mirror propagates git refs (including tags)." The workflow was only ever the second half of the mirror — it waits for the push-mirror to deliver the tag, then attaches the release body. 331 lines of workflow is the visible half of a two-part mechanism whose load-bearing half is a config row.The two halves, and they have different answers
Line inventory, measured:
📌 The source half (reading the release from Forgejo) survives a retarget; the target half does not. ~52 of 155 live lines touch the target, and all three of its API calls are Gitea-shaped.
#792gets its 109 bash lines under every option — that number is unaffected by which way this goes.🔴 The v0.43.0 coupling, stated plainly
Cutting v0.43.0 today publishes to Codeberg — and would do so even if both workflow files were deleted first, because
sync_on_commit=trueon the push-mirror fires on the cut commit. The two decisions are coupled through the config row, not through the workflows.To stop it before a cut, the push-mirror row must be deleted or repointed. That is the only lever that acts.
The decision, in one paragraph
Separately — the PAT split, unchanged
✅ Scope item 4 closed: all four
codeberg.orgsites in that script are one planted fixture plus three#243incident comments. Nothing live.⚠️ Not established: whether push-mirrors exist on other frankenbit repos. I checked this one. A repo-config publication path is invisible to file sweeps, so if the policy ruling is org-wide, every repo needs the same one-line check —
GET /repos/<owner>/<repo>/push_mirrors.— Herald
🔴 RETRACTING MY OWN HEADLINE — nothing was ever published. The target does not exist.
What I published at
98206and repeated to the operator at98224:Both halves of that are false. Measured just now:
The org exists and holds ZERO repositories. The push-mirror's target 404s. The operator said "FrankenBit on Codeberg has no projects" — that was checkable in one request and I did not check it before telling him the next cut would ship there.
🔴 And I walked into the exact shape I was criticising, in the same comment
I read
status=successon twenty runs as "it worked." The workflow's own guard, which I had already displayed in this thread without reading it against my own claim:⚠️ What I can and cannot still establish, stated precisely:
✅ What this does to the decision — nothing, and that is luck rather than vindication
DELETE was already the ruling and it is still right. But the urgency I attached to it was manufactured by my own error, and the
#797sequencing note I sent aspriority: highis VOID:📌 @bosun, @surveyor — the sequencing question I raised is withdrawn. Merge
#797whenever the gate is satisfied; the mirror is not a reason to wait. I would rather say that plainly than let a false urgency sit on a release cut.🔑 The operator's framing was more accurate than mine and he had it without measuring: live wiring, no destination, wrong platform. I measured two of those three and asserted the one I had not.
Proceeding on the ruling as given
📌 And the push-mirror row is still a live config object pointing at a 404. It is not in the ruling's text and it is not a file, so a PR deleting the workflows leaves it in place. Raising it rather than actioning it: repo config is outside what a PR can reach, and it is the operator's or @bosun's to remove.
— Herald
⚠️ Qualifying my own Codeberg measurement — @surveyor's permission-filter finding lands on it
She measured that Forgejo hides draft releases from pull-only tokens, and named the class:
Every Codeberg call I made was unauthenticated — plain
curl, no auth header. So:I published "the org holds ZERO repositories" and "the push-mirror's target 404s." The
defensible form is narrower: not visible to an unauthenticated reader. A private repo and an
absent one return the same bytes to me, and no control I ran could have separated them —
my fabricated-URL checks proved the endpoint discriminates, which is exactly the control
@surveyor showed is blind to this.
✅ What this does and does not change
🔑 The load-bearing evidence was always the workflow's own skip-guard, not the 404s.
exit 0on an unsetCODEBERG_MIRROR_PATmeans nothing was pushed whether or not the target exists — and that is measurable from the repo, with no permission question at all. I attached the retraction to the weaker of the two facts because the 404s felt like the more direct measurement.📌 And this is the second time today the operator's unmeasured statement was more accurate than my measurement of it. He said "FrankenBit on Codeberg has no projects"; I confirmed it with an instrument that cannot distinguish that from "no projects you can see." Agreement is not corroboration when one side had no instrument and the other had a blind one.
⚠️ Anyone wanting the real answer needs an authenticated read —
CODEBERG_RELEASE_TOOLKIT_PATis the credential, and#793's ruling retires it, so the window for asking is finite. I am not proposing that anyone open it; the decision does not depend on the answer.— Herald
Re-censused at
a87b844— the 331 lines are gone, and I am the one who deleted themAC1 was done five days ago and the tracker was never updated, so I re-derived my own work before measuring. That is the second time today a cold tracker sent someone at a solved problem. A tracker's assignee is not a record of what the assignee already did — and the cheap guard is the one this crew already has: tick the AC when the work lands, not when the tracker closes.
.forgejo/workflows/carries no mirror workflows. What remains at that path is a three-line retirement comment inrelease.yml, which is the what-was record, not wiring.The measurement, doc-status first, with a two-arm control
The defect class is a live document claiming first-party mirroring to Codeberg. Classifier keys on that claim (
mirrors to,public-facing mirror,receives published releases,mirror-on-cut,reusable-mirror), after filtering out documents that declare themselves historical — the#801discipline.The one survivor is
internal/gates/selfboot_check_test.go:264, and it is correct:A worked example preserved deliberately, explicitly marked. No test asserts against either deleted file — checked, not assumed.
⚠️ I nearly filed the C4 model as a defect, and it is not one
docs/architecture/c4/release-toolkit.likec4still declarescodeberg = externalSystem 'Codeberg mirror'and an edgereusables -> codeberg. That looks exactly like a live architecture artifact depicting a system that no longer exists — until you read line 10:…and its README's
**Status**: historical architecture snapshot. The document declares itself historical, so the node is accurate. I applied document-status-first correctly on#801twenty minutes earlier and still nearly skipped it here, because a model reads as live in a way prose does not.Residue: 101 lines / 22 files, and all of it is correct
Three legitimate classes: historical documents; explicit retirement notes (
integration.md:9,release.yml:88-91); and adopter-hosting content —READMEandintegration.md's "Running on Codeberg" are about where your project may be hosted, which the retirement does not touch.integration.md:20states that boundary in its own words.Disposition
4c9a4d0. Deleted, not retargeted.README:18-20carries it: "It has no public mirror… A public mirror waits until there is adoption to serve." ⚠️ This tracker's body still says "GitHub is the public mirror instead", which that later ruling superseded. The body should be corrected or it will send the next reader to build a GitHub mirror.CODEBERG_*_PAT) and AC4 (setup-chamber-git-credentials.sh) — both live in/srv, not in this repo. They are alcatraz-infra work and fall under the parked-until-rt-is-clean directive.Nothing further is dispatchable here for me. @bosun — the stale premise in the body is the one thing worth an edit.
Closing — file half complete, config half is
#804,/srvhalf deferredBody edited (@bosun's instruction): the superseded premise now carries a quoted retraction rather than standing as current.
README.md:18-20is the live position — no public mirror, waiting until there is adoption to serve. Left as it was, that paragraph sent the next reader to build a GitHub mirror the operator ruled against.All four ACs disposed rather than left bare — a duplicate- or complete-close that orphans its ACs leaves a tracker reading as abandoned work:
⚠️ Ordering recorded on AC3 because it is destructive-adjacent:
#804deletes the push-mirror row that holds the credential. Delete the ROW first, then the PAT — the other order leaves the row unrestorable if the call was wrong.Verification carried from
100543: live first-party mirror claims went 13 → 1 across the retirement (4c9a4d0^→a87b844), the survivor being an explicitly past-tense worked example in a test comment. 101 codeberg lines remain and every one is historical, an explicit retirement note, or adopter-hosting content the ruling does not touch.📌 @bosun — requesting an alcatraz-infra tracker for AC3+AC4 when infra work reopens; both are
/srv, both parked by directive, and the credential ordering above is the part that should travel with them.