A detached mirror can be reconnected in one click, and a frozen upstream says so (#1090) #18

Merged
plat merged 11 commits from reconnect-mirror into main 2026-10-12 04:49:36 +00:00
Contributor

Found live on forge.open-platform.sh (trevato/forge#1090): the Forgejo origin token expired, the origin was deleted and connected again, and trevato/forge-upstream — the mirror the forge's own fork follows — was left with no origin. It froze at 10-06, the fork read "ahead 813, behind 0" while this repository gained 25 commits and eight releases, a repair push was refused ("reconnect the origin before pushing"), and nothing in the forge offered the reconnect.

  • A detached mirror shows a frozen block on its page and branches, with Reconnect (your connected origins on the same host): POST /:owner/:name/settings/origin/reconnect, POST /api/v1/repos/:o/:n/origin/reconnect.
  • Connecting an origin on that host re-attaches the mirrors you own and says which; connecting an account whose credential died renews it in place, so its mirrors never detach again.
  • A fork of a frozen mirror says so in its following card, never "up to date"; the feed asks the owner to unfreeze.
  • Migration 72: repo.origin_detached_at set by a trigger on the FK SET NULL. repoJson.origin carries host, detached, frozenSince, detachedAt.
  • Leaves main green: the gate list names the two spawn tests it was missing; the board and studio tests allow the one boot style every page carries; the claim-link test follows the single-label child hosts; the audit gets its way in back.
  • docs/ORIGINS.md "When a token expires", docs/FOLLOWING.md, docs/FEED.md.

Targeted suites green; new test/origins-reconnect.test.ts 11/11; the full suite and an adversarial review run before merge.

Found live on forge.open-platform.sh (trevato/forge#1090): the Forgejo origin token expired, the origin was deleted and connected again, and `trevato/forge-upstream` — the mirror the forge's own fork follows — was left with no origin. It froze at 10-06, the fork read "ahead 813, behind 0" while this repository gained 25 commits and eight releases, a repair push was refused ("reconnect the origin before pushing"), and nothing in the forge offered the reconnect. - A detached mirror shows a frozen block on its page and branches, with **Reconnect** (your connected origins on the same host): `POST /:owner/:name/settings/origin/reconnect`, `POST /api/v1/repos/:o/:n/origin/reconnect`. - Connecting an origin on that host re-attaches the mirrors you own and says which; connecting an account whose credential died renews it in place, so its mirrors never detach again. - A fork of a frozen mirror says so in its following card, never "up to date"; the feed asks the owner to unfreeze. - Migration 72: `repo.origin_detached_at` set by a trigger on the FK SET NULL. `repoJson.origin` carries `host`, `detached`, `frozenSince`, `detachedAt`. - Leaves main green: the gate list names the two spawn tests it was missing; the board and studio tests allow the one boot style every page carries; the claim-link test follows the single-label child hosts; the audit gets its way in back. - docs/ORIGINS.md "When a token expires", docs/FOLLOWING.md, docs/FEED.md. Targeted suites green; new test/origins-reconnect.test.ts 11/11; the full suite and an adversarial review run before merge.
Disconnecting an origin nulls repo.origin_id through the foreign key and
leaves the provenance: a detached mirror, frozen. Migration 72 adds
repo.origin_detached_at, set by a trigger on that SET NULL and cleared on
relink, and Repo.detached reads it off the same row so a list knows which
rows are frozen without a query per row. Core helpers find detached
mirrors, compare hosts, renew a dead credential in place, and count
mirrors per origin; push-through asks the shared helper.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
reattachMirror re-attaches a frozen mirror for an admin of it, through an
origin they own on the host it came from, once that account can see the
same repository; it relinks, rewrites the remote, appends
origin.mirror_reattached (for admins) and syncs at once. Connecting an
account re-attaches the person's own frozen mirrors on that host and
reports each one, refusals included; connecting an account whose
credential died renews it in place, so its mirrors never detach.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repo JSON keeps an origin for a detached mirror: detached, no origin id,
when it was disconnected and when it froze. POST
/api/v1/repos/:owner/:name/origin/reconnect re-attaches one; the connect
answer lists the mirrors it brought back; the following JSON carries
parentFrozen beside the counts.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The repository home and branches page of a detached mirror say what froze
and since when, with a Reconnect form for its admin or the way to connect
an account there. A fork following a frozen parent says so instead of
ahead/behind and never "up to date". Settings, Origins counts the mirrors
a dead credential froze, links Re-authorize, lists frozen mirrors, and
the connect flash names what came back.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One decision item per origin needing re-authorization with mirrors behind
it, and one per detached mirror, read from what is true now so each goes
the moment it is fixed.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A real forge, a fake Forgejo and a second forge as the git host: renew in
place, disconnect, every page and the API saying frozen, the fork never
"up to date", pushes still refused, refusals for another host, an account
that cannot see it, a non-admin, an anonymous caller, someone else's
origin and a native repo, then reconnecting by connecting again, through
the API and through the page, with refs moving each time. In the gate.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ORIGINS.md: when a token expires, what a detached mirror is, what a person
sees, the one click, and that connecting again re-attaches by itself.
FOLLOWING.md: the frozen-parent sentence and why it is never "up to date".
FEED.md: the two decision items.

Refs #1090

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gate list names the two spawn tests, and the board test allows the one boot style every page carries
Some checks failed
check / gate (2) (pull_request) Has been cancelled
check / gate (3) (pull_request) Has been cancelled
check / check (pull_request) Has been cancelled
check / gate (1) (pull_request) Has been cancelled
preview / preview (pull_request) Has been cancelled
ce79bed96b
Refs #1090. Upstream main was red on two tests that this pull inherited:
test/spawn-anon and test/spawn-host were in neither GATE nor EXCLUDED, and
the board test forbade any style element after the white-flash fix gave
every page a nonce'd boot style. The list now carries both tests, and the
board test says what it meant: one boot style, never the board's own rules
inline.
Main green again: tests follow the deliberate changes, and the audit gets its way in back
Some checks failed
check / gate (2) (pull_request) Has been cancelled
check / gate (3) (pull_request) Has been cancelled
check / check (pull_request) Has been cancelled
preview / preview (pull_request) Has been cancelled
check / gate (1) (pull_request) Has been cancelled
0302417c7c
Refs #1090. Three more tests on upstream main were red for reasons that
were not bugs in what they tested: the studio test forbade any style
element after every page gained its nonce'd boot style; the claim-link test
expected a nested child host after children moved to one label under the
parent (c5b54dad); and the audit test looked for the home's link to the
audit after the feed home (#599) dropped the Audit button — and nothing
else linked the audit at all. The first two tests now say what they meant;
the audit's button returns beside Map and Board on the home's apps card,
because the trust page must have a way in.
Refs #1090. Found by adversarial review: hostOf read only the hostname, so
git.example:8443 passed for git.example and a mirror could be reattached
to another server; a mirror from before remote ids were kept was bound to
whatever repository now carried its name. siteOf compares scheme, host and
port everywhere a reconnect decides (the auto reattach, the page's offer,
the attach itself); a mirror with no remembered id is refused as
remote_unknown with the way out said. The pins list carries the same
origin shape as the repository JSON.
Two more tests follow what shipped: the open spawn door, and a feed card's phone rule
Some checks failed
check / gate (1) (pull_request) Successful in 12m59s
check / gate (2) (pull_request) Successful in 11m6s
check / gate (3) (pull_request) Successful in 13m6s
check / check (pull_request) Successful in 0s
preview / preview (pull_request) Successful in 5s
check / gate (2) (push) Has been cancelled
check / gate (3) (push) Has been cancelled
check / check (push) Has been cancelled
sync-env / sync-env (push) Has been cancelled
check / gate (1) (push) Has been cancelled
1a39631fdf
Refs #1090. Upstream main was red on both: the new-forge test still said a
non-admin has no door, after 0.7.57 opened it to anyone signed in (the
roster and the claim links stay the admin's, and the test now says so);
and the studio's phone rules named .fcard-head, a feed card head with no
rule at 360px — it wraps now.
plat merged commit 1a39631fdf into main 2026-10-12 04:49:36 +00:00
plat deleted branch reconnect-mirror 2026-10-12 04:49:36 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
trevato-apps/forge!18
No description provided.