Skip to content

fork: publish fork releases and sync them with upstream - #1

Merged
3mdistal merged 5 commits into
fork/mainfrom
fork/infra
Oct 24, 2026
Merged

3mdistal merged 5 commits into
fork/mainfrom
fork/infra

Conversation

@3mdistal

@3mdistal 3mdistal commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Problem

The fork has nowhere to come from. Without this, a fork build would update itself from upstream's releases (and install stock T3 over the fork), the CLI and the one-line installers would download upstream's builds, and upstream's release workflow can't run on a fork at all: it needs Blacksmith runners, deploy secrets, and upstream's Apple App ID. Nothing keeps the fork on upstream's stable releases either.

Approach

Every push to fork/main publishes vX.Y.Z-alice.N, where X.Y.Z is the upstream stable tag the patch stack sits on. Upstream's build already treats any version without -nightly or -preview as a production app on the latest update channel, and electron-updater compares semver, so installed fork builds follow the fork's latest release with no updater patch. A test pins those assumptions so a sync fails loudly if upstream changes them.

Releases reuse upstream's per-platform packaging (release-desktop.yml) on GitHub-hosted runners, gated on Fork CI passing on the exact commit being published. Only the fork's own workflows can run: every other upstream workflow keeps its jobs, but its triggers are commented out. A sync script rebases the stack onto each new upstream stable tag, waits for CI, and promotes it; when it can't, it starts one conflict thread for Alice instead of guessing.

What changed

  • Update sources: CLI_RELEASE_REPOSITORY, DEFAULT_DESKTOP_RELEASE_REPOSITORY, and the two install scripts point at 3mdistal/t3code. Upstream tests that pinned upstream URLs now expect the fork's.
  • t3 triage is off. It files public issues on pingdotgg/t3code; Alice reports bugs privately.
  • t3 thread start --option id=value, so the conflict thread can start at xhigh effort.
  • macOS signing without upstream's App ID: a signed build without a provisioning profile skips the passkey entitlements and uses apps/desktop/resources/entitlements.mac.plist; an unsigned build is signed ad hoc. release-desktop.yml no longer requires APPLE_TEAM_ID or MACOS_PROVISIONING_PROFILE.
  • .github/workflows/fork-release.yml: version, Fork CI, JS bundle, macOS arm64 / Linux x64 / Windows x64 builds (Windows builds on Linux like upstream's and runs its checks on windows-2025; Linux is needed because Windows embeds its CLI archive), then publish with latest.yml, latest-mac.yml, latest-linux.yml, blockmaps, CLI archives and SHA256SUMS.
  • Upstream's workflows can't run here. scripts/fork/ci-workflows.ts comments out the on: block of every workflow except fork-ci, fork-release, fork-windows-tests and release-desktop, leaving workflow_call, which nothing calls. The files stay because upstream's tests and knip read them: deleting them would have meant deleting three upstream tests and two scripts and patching release-smoke.ts. A test fails CI if any upstream workflow could run.
  • fork-ci.yml and fork-windows-tests.yml are generated from upstream's ci.yml and windows-tests.yml by the same script (GitHub-hosted runners, longer timeouts, fork triggers). A test fails if the copies drift.
  • scripts/fork/versions.ts picks the next version; scripts/fork/sync.ts runs the sync (run, push, promote, --dry-run). Both use only Node built-ins, so they run before any install.
  • Backport of upstream's fix(deps): pin all patched package versions pingdotgg/t3code#23798. Upstream's v0.1.2 fails its own Release Smoke check (ERR_PNPM_UNUSED_PATCH, upstream run); upstream fixed it 35 minutes after the tag. The commit is upstream's unchanged (same patch-id), and the sync drops it once a release contains it.
  • FORK.md is the brief for the patch stack, releases, rollback, the sync, and the conflict rules.

Safety and operations

  • Data: no migrations, no new persisted events, no new settings. Stock T3 can open the fork's data unchanged, which keeps "install stock T3" a working rollback.
  • Upstream workflows: with their triggers commented out, none can fire on a push, pull request, tag or schedule. Before this change, opening this PR ran upstream's PR workflows once: CI and Mobile Fingerprint Check were cancelled, PR Size and PR Vouch added labels (removed), and those workflows stay disabled in Actions settings.
  • Syncs: a conflict in one of upstream's own workflows resolves itself (take upstream's side, comment the triggers out again). sync.ts push and promote refuse a commit where an upstream workflow could run. The sync now rebases onto the target tag itself, so stack commits upstream has since released, like the backport, drop out instead of conflicting.
  • Upstream refs: main, the other 1,800 branches and the 729 v* tags that came with the fork keep upstream's live triggers, so they are never pushed. Upstream's newer stable tags never go to the fork; the release workflow fetches them from upstream.
  • Default branch: after this merges, the fork's default branch becomes fork/main, so schedules and bot events read the inert copies. Switching before the merge would point them at fork/main's current tree, which is upstream's v0.1.2 with release.yml's half-hourly schedule live.
  • First release: merging this publishes v0.1.2-alice.1. Nothing installs it: the PC runs Nightly, which follows upstream's nightly feed.
  • Signing: Windows builds are unsigned (SmartScreen warns once). macOS builds are signed ad hoc until the Apple secrets are added; an ad hoc Mac app can't update itself in place.

Verification

  • scripts/fork (23 tests, including scratch-repo rebases: upstream editing a trigger block and adding a workflow, a backport upstream released and then changed again, a non-workflow conflict, a workflow the rewrite can't handle), cliRelease (5), updateChannels.fork (1), desktopUpdate.logic (9), mobile environment-maintenance (7), and server thread.fork, thread and triage pass on Windows.
  • Upstream's tests that read workflows pass against the rewritten files: notify-discord-release.test.ts, vercel-deployment-output.test.cjs, relay-state-output.test.cjs.
  • vp run check:changed: format, lint, knip, graphql and typecheck pass.
  • Two upstream test files fail on Windows without these changes too: update.test.ts's PATH-launcher case, and build-desktop-artifact.test.ts's archive tests (tar on Windows). Fork CI runs them on Linux.
  • git ls-tree on the head commit: the only live triggers are Fork CI's (pull requests into fork/main, sync/** pushes, call, dispatch), Fork Release's (fork/main pushes, dispatch), Fork Windows Tests' (dispatch) and release-desktop.yml's (call). Every other workflow's only trigger is workflow_call.
  • actionlint reports nothing on the four workflows that run. On the inert ones it reports inputs.* references with no declared inputs, which can't matter since they never run; GitHub raised no workflow-file errors on the push.
  • Not yet run: the release workflow (it runs on merge), the update rehearsal (0.1.3-alice.1 to -alice.2 to 0.1.4-alice.1), and the conflict rehearsal.

Review focus

  • fork-release.yml's gate: publish needs version, Fork CI, and every desktop build; a re-run on a released commit does nothing.
  • sync.ts promote: it moves fork/main with --force-with-lease against the commit the sync started from, and refuses unless Fork CI passed on the sync branch.
  • The macOS fallback: ad hoc signing plus the unrestricted entitlements plist, against upstream's passkey path.
  • isInertWorkflow in ci-workflows.ts: it is the check that stands between upstream's triggers and the fork.

Done by Claude Opus 5.5 in Claude Code, running in T3 Code.

🤖 Generated with Claude Code

3mdistal and others added 3 commits October 24, 2026 01:28
The CLI's updates, server self-updates, SSH installs, the one-line installers
and the desktop release links all read 3mdistal/t3code, so no fork build
installs stock T3 over itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
t3 triage files public issues on pingdotgg/t3code, and Alice reports this
fork's bugs privately. --option id=value sets model options on a new thread,
so the sync's conflict thread can start at xhigh effort.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Passkey entitlements need a provisioning profile for upstream's App ID, so a
signed build without one signs with the unrestricted entitlements instead,
and an unsigned build is signed ad hoc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3mdistal and others added 2 commits October 24, 2026 02:48
Fork release publishes vX.Y.Z-alice.N with update manifests from every push
to fork/main, after Fork CI passes on that commit. Fork CI and Fork Windows
Tests are generated from upstream's workflows for GitHub-hosted runners.
Every other upstream workflow keeps its jobs but has its triggers commented
out, so only the fork's workflows can run here. scripts/fork/sync.ts rebases
the stack onto each new upstream stable tag, resolves conflicts in upstream's
workflows itself, refuses to push a stack where one could run, and starts
one conflict thread when it can't finish. FORK.md is the brief.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream's v0.1.2 fails its own Release Smoke check: a lockfile-only install
of the workspace manifests stops on ERR_PNPM_UNUSED_PATCH. Upstream fixed it
35 minutes after the tag in 22840ab. This is that commit unchanged, so the
next sync's rebase drops it as already applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@3mdistal
3mdistal merged commit dbbea02 into fork/main Oct 24, 2026
17 checks passed
@3mdistal
3mdistal deleted the fork/infra branch October 24, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant