Skip to content

feat: rebrand fork to Phoenix with side-by-side install support - #2

Merged
roughcoder merged 7 commits into
mainfrom
feat/phoenix-rebrand
Aug 11, 2026
Merged

feat: rebrand fork to Phoenix with side-by-side install support#2
roughcoder merged 7 commits into
mainfrom
feat/phoenix-rebrand

Conversation

@roughcoder

Copy link
Copy Markdown

Renames this fork's user-visible identity from T3 Code to Phoenix, and gives it a distinct
OS-level identity so it can be installed and run alongside upstream T3 Code on the same machine.

Approach

Runtime identity diverges. Source identity does not.

Anything written to disk, bound to a port, registered with the OS, or shown to a user becomes a
Phoenix name. Anything that only exists inside the repository — the @t3tools/* package scope,
t3-prefixed file paths, internal symbols like t3Home — is left exactly as upstream has it, so
merges from upstream stay cheap. docs/internals/branding.md records the policy and a post-merge
leak check.

Runtime identity now diverging

Concern Upstream Phoenix
State dir / env var ~/.t3 / T3CODE_HOME ~/.phoenix / PHOENIX_HOME
Desktop userData t3code phoenix
Product name (and macOS Keychain item) T3 Code (Alpha) Phoenix (Alpha)
URL scheme t3code:// phoenix://
Bundle ID / AppUserModelID com.t3tools.t3code com.goodbird.phoenix
Linux desktop entry / WM class / URL handler t3code* phoenix*
systemd unit t3code.service phoenix.service
Ports 3773, 13773/5733 3873, 13873/5833
CLI binary t3 phoenix
MCP server id t3-code phoenix

Points worth reviewer attention

The legacy-userData adoption step is removed. Upstream migrates its pre-rename directory
(T3 Code (Alpha)) into the current one. Keeping that would have pointed two live applications at a
single directory the moment both were installed. A test asserts it never comes back.

PHOENIX_HOME has no T3CODE_HOME fallback, unlike every other variable. The base dir holds the
SQLite database and auth state, so inheriting a T3CODE_HOME set for T3 Code would recreate exactly
the collision this change exists to prevent. Every other variable reads PHOENIX_* first and falls
back to T3CODE_*, so existing configuration keeps working.

Two env-isolation follow-ons that the rename would otherwise have silently broken:

  • The WSL backend strips the base-dir variables from its child env; that guard named only
    T3CODE_HOME, so it now strips PHOENIX_HOME too. Without this the Linux backend would share the
    Windows database via /mnt/c.
  • PHOENIX_PORT is added to the desktop backend's cleared-env list. The server prefers it over
    T3CODE_PORT, so an ambient value from the dev runner would otherwise outrank the port the
    backend was configured with.

.gitignore keeps ignoring .t3 as well as .phoenix, so a checkout that has run either app
(or taken an upstream merge) never commits local dev state.

Attribution

Credit to T3 Code is added to the README ("Built on T3 Code") and to Settings → About. The
original MIT copyright in LICENSE is untouched. Marketing legal pages and podspec author metadata
still name T3 Tools Inc., which remains accurate. README install instructions were corrected — they
previously described upstream's App Store / winget / Homebrew / npx t3@latest distributions, none
of which install Phoenix.

Verification

  • Typecheck: 9 errors, identical to the pre-existing count on main (unrelated requestId /
    reports API drift).
  • Tests: server 229 files pass, desktop 444, mobile 623, client-runtime 591, scripts 201, shared 337,
    ssh 25, web 2153. The only web failures are the 8 pre-existing promptStashStore.test.ts cases,
    verified failing identically on a clean tree.
  • Lint and formatter clean; the one remaining warning also pre-exists on main.

Not included

Four files touched by the in-flight feat/session-spawning branch don't exist on main, so their
strings are not rebranded here: the sessions MCP toolkit (handlers.ts, tools.ts),
SessionSpawnReactor.ts, and docs/user/session-orchestration.md. They need a small follow-up once
that branch lands.

Deliberately left alone: the t3code OAuth referrer sent to x.ai (changing it risks their
allowlist), upstream's Play Store link on the marketing site, and mobile showcase demo fixtures.

🤖 Generated with Claude Code

Renames the fork's user-visible identity from T3 Code to Phoenix, and gives it
a distinct OS-level identity so it can be installed and run alongside upstream
T3 Code on the same machine.

The guiding rule is "runtime identity diverges, source identity does not":
anything written to disk, bound to a port, or registered with the OS becomes a
Phoenix name; anything that only exists inside the repository (the @t3tools
package scope, t3-prefixed paths, internal symbols) is left exactly as upstream
has it, so merges from upstream stay cheap.

Runtime identity now diverging:
  - state dir ~/.t3 -> ~/.phoenix, env var T3CODE_HOME -> PHOENIX_HOME
  - desktop userData t3code -> phoenix, product name Phoenix (Alpha)
  - URL scheme t3code:// -> phoenix://
  - bundle/AppUserModelID com.t3tools.t3code -> com.goodbird.phoenix
  - Linux desktop entry, WM class and URL-handler entry
  - systemd unit t3code.service -> phoenix.service
  - ports 3773 -> 3873, 13773/5733 -> 13873/5833
  - CLI binary t3 -> phoenix, MCP server id t3-code -> phoenix

Two deliberate subtleties:
  - The legacy-userData adoption step is removed entirely. Upstream migrates its
    pre-rename directory into the current one; keeping that would point two live
    applications at a single directory. A test asserts it never comes back.
  - PHOENIX_HOME has no T3CODE_HOME fallback, unlike every other variable. The
    base dir holds the SQLite database and auth state, so inheriting a
    T3CODE_HOME set for T3 Code would recreate the exact collision this change
    exists to prevent.

Other environment variables read PHOENIX_* first and fall back to T3CODE_*.

Attribution to T3 Code is added to the README and to Settings -> About, and the
original MIT copyright in LICENSE is untouched. Marketing legal pages and
podspec author metadata still name T3 Tools Inc., which remains accurate.

docs/internals/branding.md records the policy and the post-merge leak check.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Aug 11, 2026
Completes the Phoenix rebrand across the session-orchestration feature, which
landed on main after the initial rebrand branch was cut.

Agent-facing prose, which is injected into spawned sessions' context and would
otherwise have agents calling the product "T3 Code" while running inside Phoenix:
  - the spawned-session report instructions
  - the list_session_providers / spawn_session tool descriptions
  - the "[Phoenix]" prefix on spawned-session report and error messages

Also renames three MCP clientInfo identities missed in the first pass
(GrokProvider's provider probe, and the Cursor/Grok git text-generation
clients), which left CursorProvider announcing "phoenix" while its Grok
counterpart still announced "t3-code".

Adds .omc/ to .gitignore: the agent tooling writes session history and project
memory into the working tree, and an automated commit had swept 169 of those
files onto this branch.
@roughcoder
roughcoder force-pushed the feat/phoenix-rebrand branch from 95830f4 to cb8b6ab Compare August 11, 2026 20:40
The GitHub repository was renamed from goodbirdhq/t3code to goodbirdhq/phoenix,
so the README's desktop-builds link redirected instead of resolving directly.
@roughcoder

Copy link
Copy Markdown
Author

Updated: folded in the session-orchestration strings

main has moved on since this PR was cut — the session-spawning feature merged, bringing the four
files this PR originally had to defer. I've merged main in and completed the rebrand across them.

cb8b6abc — agent-facing prose plus three missed identities:

  • The spawned-session report instructions, the list_session_providers / spawn_session tool
    descriptions, and the [Phoenix] prefix on spawned-session report/error messages. These are
    injected into spawned sessions' context, so without this an agent running inside Phoenix would
    refer to the product as "T3 Code".
  • Three MCP clientInfo identities missed in the first pass (GrokProvider's probe, and the
    Cursor/Grok git text-generation clients), which had left CursorProvider announcing phoenix
    while its Grok counterpart still announced t3-code.
  • Adds .omc/ to .gitignore — see below.

62a5a196 — repoints the README release link at the renamed goodbirdhq/phoenix repository.

Note on the force-push

An automated commit ("chore: persist OMX project memory and session history") pushed 330 .omc
agent-tooling files onto this branch — 169 session-history files and a project-memory scan. I've
force-pushed the branch without them and gitignored the directory. The real commits are unchanged
apart from the additions above.

Those files contained no credentials or conversation content — only session IDs, timestamps, PIDs,
local paths and a tech-stack scan — but they had no business in the PR.

Verification

Server 229 files pass, desktop 444, mobile 623, client-runtime 591, scripts 201, shared 337, ssh 25,
contracts 241.

Pre-existing failures inherited from main, all in files this branch does not touch, each verified
against a clean tree:

  • ProjectionSnapshotQuery.test.ts (1) — thread-shape field-count drift from session-spawning
  • threadActivity.test.ts — the one typecheck error, reports optionality, same root cause
  • promptStashStore.test.ts (8) and imageCompression.test.ts (1) in web

Lint clean apart from two warnings that also pre-date this work.

…ifacts

Upstream's pipelines assume T3 Tools' infrastructure, so almost none of them can
run here. Three changes make CI useful for Phoenix.

Runners. Upstream targets Blacksmith; a fork without a Blacksmith installation
queues those jobs forever rather than failing them, which is why every check on
the rebrand PR sat pending indefinitely. ci.yml now targets GitHub-hosted
runners, which are free on this public repository.

Disabled workflows. The nine that deploy to, or authenticate against, T3's
infrastructure are switched off GitHub-side with `gh workflow disable` rather
than edited, so the files stay byte-identical to upstream and never conflict on
merge. docs/internals/ci.md records which and why, since that state is invisible
in a fresh clone. The mobile native static analysis job is the exception: it
lives inside an otherwise-enabled workflow, so it is parked with `if: false`.

Phoenix Build. A new, Phoenix-only workflow that builds an unsigned arm64 macOS
DMG on every merge to main, publishes it as a GitHub pre-release, and posts to
Slack. Unsigned builds are quarantined by Gatekeeper, which reports a missing
signature as "Phoenix is damaged and can't be opened" — indistinguishable from a
corrupted download — so both the release notes and the Slack message carry the
`xattr -dr com.apple.quarantine` fix.

The Slack step reads SLACK_RELEASE_WEBHOOK_URL and no-ops when it is unset, so
builds do not fail before the secret exists. It reports failures as well as
successes.
CI had never actually executed on this fork - every job targeted Blacksmith
runners that were never connected, so checks queued indefinitely instead of
failing. Pointing them at GitHub-hosted runners surfaced two problems that had
been invisible.

threadActivity: the session-spawning feature (577c251) added a required
`reports` field to OrchestrationThread, but this fixture's defaults were never
updated, so it could not satisfy the type. This is our own regression, merged
without CI ever running. Adds `reports: []` alongside `proposedPlans`.

imageCompression: this test stubbed every encode at 8MB, and it is the only case
where nothing fits the budget, so it runs the full 3 dimension scales x (probe +
4 quality steps) = 15 encodes, each base64-encoded to ~10.7MB. It passes in
isolation and only times out when the suite runs in parallel and that churn
competes for CPU - upstream's 8-vCPU runners absorb it, ours do not. Drops the
stub to 2MB, which still encodes to roughly double the 1_300_000-char budget so
the assertion is unchanged, and comments why the size is bounded.
ProjectionSnapshotQuery: the session-spawning feature added `spawnedByThreadId`
to the projected thread, but neither expected snapshot in this test was updated.
Both assertions needed it - deepEqual aborts on the first mismatch, so fixing
the snapshot.threads case simply unmasked the shellSnapshot one. Same class of
regression as the `reports` field, and same cause: PR #1 merged without CI ever
executing.

imageCompression: give the give-up-path case a 90s timeout. It is the only case
where nothing fits the budget, so it runs 3 dimension scales x (probe + 4 quality
steps) = 15 encodes, each a base64 round-trip through jsdom's FileReader. That
takes ~8s on its own and overruns the 15s default once the rest of the suite is
competing for CPU. An earlier attempt to fix this by shrinking the stubbed
encode from 8MB to 2MB did not work - the test passes in isolation even at 8MB,
so volume was never the driver, contention is. The smaller stub is kept because
there is no reason to do more work than the assertion needs, but the timeout is
what actually fixes it.

The product path is bounded at 15 encodes and returns correctly; this is a slow
test, not a hanging implementation.
@roughcoder

Copy link
Copy Markdown
Author

CI now runs — and this is the first green build this repository has had

Why nothing ever ran. Upstream targets Blacksmith runners (26 of 27 jobs). Without a Blacksmith
installation GitHub queues those jobs indefinitely rather than failing them, so every check on
this fork sat pending forever. PR #1 merged green-by-absence. ci.yml now targets GitHub-hosted
runners, free on this public repo.

Nine workflows disabled GitHub-side (gh workflow disable) rather than edited, so the files stay
byte-identical to upstream and never conflict on merge: Release, Deploy relay, Web Preview, both EAS
workflows, Fingerprint Check, Showcase Screenshots, PR Vouch, Thread Transfer. Recorded in
docs/internals/ci.md, since that state is invisible in a fresh clone. mobile_native_static_analysis
is parked with if: false as it sits inside an otherwise-enabled workflow.

New phoenix-build.yml — unsigned arm64 macOS DMG per merge to main, published as a GitHub
pre-release, with a Slack notification. Both the release notes and the Slack message lead with
xattr -dr com.apple.quarantine, because Gatekeeper reports a missing signature as "Phoenix is
damaged and can't be opened"
, which reads like a corrupted download. The Slack step reads
SLACK_RELEASE_WEBHOOK_URL and no-ops when unset, and reports failures as well as successes.

Three defects this surfaced, all from PR #1

None are rebrand-related. All three had been sitting on main unseen because its checks never ran.

Defect Cause
threadActivity.test.ts typecheck PR #1 added a required reports field; fixture defaults never updated
ProjectionSnapshotQuery (x2 assertions) PR #1 added spawnedByThreadId; both expected snapshots never updated
imageCompression timeout Upstream test, load-sensitive: 15 base64 round-trips starve under parallel workers

The image test needed a 90s timeout rather than a smaller payload — it passes in 7.8s in isolation
even at the original size, so contention was the driver, not volume. The product path is bounded at
15 encodes and returns correctly.

Still outstanding

  • No human review on 238 files.
  • SLACK_RELEASE_WEBHOOK_URL not yet set — the notify step will skip until it is.
  • Phoenix Build cannot run until this lands, since it triggers on main.
  • Mobile bundle ID change means a separate install, not an upgrade; relyingParty and the Expo
    slug still point at T3's infrastructure, so mobile auth/EAS need your own before that path works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant