Releases as pull requests, the same in all five app repos (hub, catalog, live, riftbound, karaoke). The Gitea side (PRs only into main, squash merges, required checks, `v*` tags only by release-bot, its `RELEASE_TOKEN` org secret) is dhkatz/infra `modules/app-repo`.
- **`.gitea/workflows/pull-request.yml`**: the PR title must be a conventional commit (`feat(api): …`, `fix: …`, `feat!: …`); it becomes the commit on main. `chore(release)` only from release-bot's `release` branch. Required to merge (`Pull request / title`).
- **`.gitea/default_merge_message/SQUASH_TEMPLATE.md`**: the squash commit is `<PR title> (#<n>)` plus the description.
- **`.gitea/workflows/release.yml`**, on every push to main, as release-bot (`RELEASE_TOKEN`):
- if the commit is a merged `chore(release): vX.Y.Z`: tag it vX.Y.Z (lightweight), push the tag (CI builds the image, Komodo deploys prod), publish the release with git-cliff's notes;
- otherwise, if anything releasable landed since the last tag: rewrite the `release` branch (CHANGELOG.md) and open or update `chore(release): vX.Y.Z`.
- **`cliff.toml`** (git-cliff 2.14.2, pinned by sha512): feat → minor, fix/perf/revert → patch, breaking → minor while 0.x; chore/ci/docs/test/style/build/refactor alone don't release. Only conventional commits count, so the history before this is left out.
- **`CHANGELOG.md`**: header only; release PRs fill it.
- Agent notes (CLAUDE.md / AGENTS.md): the PR flow and title format; never tag by hand.
After merging, `Release` runs on main and should report nothing to release (this PR is `ci:`); the first `feat:`/`fix:` PR merged after it opens the first release PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: #1
Co-authored-by: dhkatz <[email protected]>
After the tests pass, main pushes :latest-main and :main-<commit>, and vX.Y.Z tags push :X.Y.Z, :X.Y and
:latest to git.t5de.dev (registry login: the t5de org secret REGISTRY_TOKEN). Komodo deploys them once the
ref's CI has passed; it no longer builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Komodo now watches the t5de org (Gitea webhook → its t5de-push action, defined in t5de-infra): it builds and
deploys a commit to staging once this CI passes on main, and to prod for vX.Y.Z tags. So the deploy job and
.gitea/scripts/deploy.py go, and this repo no longer needs to know how it's deployed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI now builds the API and website images after the tests, pushes them to git.t5de.dev, and deploys them through
Komodo (.gitea/scripts/deploy.py): pushes to main as :main to hub.staging.t5de.dev, vX.Y.Z tags as :X.Y.Z to
hub.t5de.dev, with the version pinned in t5de/t5de-infra.
The API image installs with --linker hoisted: Bun's isolated layout keeps the workspace links (@t5de/db) in each
package's own node_modules, which the image didn't copy, so the API couldn't start.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The hub becomes a Bun monorepo: apps/api (Elysia, Better Auth for accounts, roles, bans, proxy device
sign-in and JWT keys; tagline linking, presence and the console as before), packages/db (Drizzle; Postgres in
production, PGlite in development and tests), and the Next.js website from apps/web. Self-hostable with
docker compose; the website deploys to Vercel-style hosts, the API to any that runs Bun or containers. The
first version, in Go, is removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
People sign up on the website and link IMVU accounts by putting a code in the account's tagline, which the
hub reads from IMVU's public API (the most recent proof wins; link caps per person). IMVU's age verification
or Access Pass make an account adult. T5DE's proxy signs in with a device code and keeps a WebSocket open,
reporting the IMVU account in use and the community products it wears; /online shows those who opted in.
Moderators get a console: who's online, people, link caps, bans, roles, and the audit log. Tests run the
website and API against an embedded Postgres and a fake IMVU API.
Co-Authored-By: Claude Opus 5.5 <[email protected]>