From 73e1ab9b00c37c748b691270cd2be402233f50e4 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 11:06:35 +0200 Subject: [PATCH 01/10] docs(fork): rebase routine, stack check and fork CI Fork stays a small topic-commit stack on upstream main: docs/fork.md describes the model, scripts/fork-rebase.sh absorbs upstream updates with per-file conflict reports and proof-gated pushes, scripts/fork-check.sh enforces the model, and .github/workflows/fork.yml runs it on every PR. No upstream-owned files are modified. Refs #6 --- .github/workflows/fork.yml | 31 +++++++ docs/fork.md | 144 ++++++++++++++++++++++++++++++++ scripts/fork-check.sh | 86 +++++++++++++++++++ scripts/fork-rebase.sh | 110 ++++++++++++++++++++++++ scripts/fork-upstream-edits.txt | 6 ++ 5 files changed, 377 insertions(+) create mode 100644 .github/workflows/fork.yml create mode 100644 docs/fork.md create mode 100755 scripts/fork-check.sh create mode 100755 scripts/fork-rebase.sh create mode 100644 scripts/fork-upstream-edits.txt diff --git a/.github/workflows/fork.yml b/.github/workflows/fork.yml new file mode 100644 index 0000000000..4f8209bc9c --- /dev/null +++ b/.github/workflows/fork.yml @@ -0,0 +1,31 @@ +name: Fork + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +# Runs alongside upstream's own CI: ci.yml covers the project's checks and +# tests, this workflow covers the fork-stack model. Both must be green. +jobs: + fork-model: + name: Fork Stack Model + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Fetch upstream base + run: | + git remote add upstream https://github.com/pingdotgg/t3code.git 2>/dev/null || true + git fetch upstream main --depth 100 + + - name: Check fork stack + run: bash scripts/fork-check.sh --upstream upstream diff --git a/docs/fork.md b/docs/fork.md new file mode 100644 index 0000000000..7446d92971 --- /dev/null +++ b/docs/fork.md @@ -0,0 +1,144 @@ +# Fork maintenance + +This repo (`toolboxmd/t3code`) is a thin, rebasable layer on upstream +`pingdotgg/t3code`. Upstream `main` is the base; fork work is a small stack of +topic commits kept on top of it. This document describes the model, the +maintenance routine, and the upstream extension points that would shrink the +patch set further. + +## Model + +- **Base.** Upstream `main` (`https://github.com/pingdotgg/t3code.git`, + remote name `upstream`). Fork `main` tracks it and adds only the stack below. +- **Stack.** Fork work lives as topic commits on top of the current upstream + base. No merge commits: upstream updates are absorbed by rebasing the stack, + never by merging `upstream/main` into the fork. +- **New files first.** Features go in new files and new packages. Edits to + upstream-owned files stay minimal and every edited file is listed under + "Upstream edits" below and in the machine-checked allowlist + `scripts/fork-upstream-edits.txt` (one path per line). +- **Remotes.** `origin` is `toolboxmd/t3code`; `upstream` is + `pingdotgg/t3code`. Never push to `upstream`, never send fork commits there + (offering extension points upstream is a separate human decision, out of + scope for routine maintenance). + +## Fork-only content + +New files owned by the fork (no upstream counterpart, always allowed): + +- `VISION.md`, `MISSION.md`, `OBJECTIVE.md` — Project Direction, merged in #5. +- `docs/fork.md` — this document. +- `scripts/fork-rebase.sh` — the rebase routine. +- `scripts/fork-check.sh` — the stack-model check run by CI. +- `scripts/fork-upstream-edits.txt` — canonical allowlist of upstream files + the fork may modify (currently empty). +- `.github/workflows/fork.yml` — fork CI. Upstream's own `ci.yml` is + untouched; the two workflows together give every PR upstream checks plus + fork checks. + +## Upstream edits + +Upstream-owned files modified by the fork. Keep this list exact; CI enforces +that any modification to an upstream file not listed here fails. + +- (none) + +## Routine + +`scripts/fork-rebase.sh` fetches `upstream`, rebases the current branch's +stack onto `upstream/main`, runs the fork check, optionally runs the full +proof, and optionally pushes. It reports conflicts per upstream file and never +force-pushes a published branch without proof passing in the same run. + +```bash +# Report the stack and what an absorption would replay, without changing anything. +scripts/fork-rebase.sh --dry-run + +# Absorb upstream updates (fetch + rebase + fork check). +scripts/fork-rebase.sh + +# Absorb and run the full fork proof (required before pushing a rebased stack). +scripts/fork-rebase.sh --proof + +# Absorb, prove, and publish (refuses unless --proof passed in the same run; +# uses --force-with-lease, never --force; refuses on main). +scripts/fork-rebase.sh --proof --push +``` + +On conflict the script stops at the failing commit, prints the conflicted +upstream files (`git diff --name-only --diff-filter=U`), and exits non-zero +with the rebase left in progress for manual resolution (`git status` to see +the files, resolve, `git rebase --continue`, then re-run the script). Record +the conflicted files and the resolution in the absorption log below and in the +PR. + +Rules that the script enforces and humans must follow when working by hand: + +- Never `git push --force`. A published rebased branch moves only via + `git push --force-with-lease` after the same proof passed. +- Never rebase or push `main` directly; land upstream absorptions through a + PR branch, then merge the PR. +- Bind proof to the final candidate: any rebase or conflict resolution + invalidates earlier proof, so the proof must run last. + +## CI + +`.github/workflows/fork.yml` runs on every pull request and on pushes to +`main`, alongside upstream's own `ci.yml`. It adds the `upstream` remote, +fetches it, and runs `scripts/fork-check.sh`, which verifies: + +- the stack on top of the upstream base contains no merge commits, +- the stack is small (at most 20 commits), +- every modification to an upstream-owned file is allowlisted in + `scripts/fork-upstream-edits.txt` (new fork-only files are always fine), +- this document exists. + +Upstream's `ci.yml` continues to run the project's own checks and tests +unchanged. + +## Absorption log + +One entry per absorbed upstream update: exact base and candidate commits, +elapsed time, conflicts by file (or "none"), and proof. + +- **2026-09-25** (this issue, #6): absorbed upstream `main` `e3e7cc3fc` + (`fix(usage): price Claude fast-mode requests at the fast rate (#13599)`) + onto fork branch `chore/6-fork-maint`. Previous base `b2b43bef7` + (`fix(server): preserve racy edits in review diff previews (#12613)`) plus + fork commit `3cfadb7bc` (#5, Project Direction). 54 upstream commits + replayed under the stack; **conflicts: none**. Routine: + `git fetch upstream main` + `git rebase upstream/main` via + `scripts/fork-rebase.sh`, wall time under 2 minutes for fetch plus rebase. + Full proof (`pnpm install --frozen-lockfile && pnpm exec vp run -r + typecheck && pnpm exec vp lint && pnpm exec vp run -r test && pnpm exec vp + fmt --check`) run on the rebased candidate; results recorded in the PR. + +## Candidate upstream extension points + +Generic seams the fork's planned work (Model Router threads, OpenBot mode) +will need. Each is written so that, if upstream ever adopted it, the stated +fork patch would disappear. Offering any of them upstream is a human decision +and explicitly out of scope for this maintenance work; this list only records +what each would remove. + +- **`parentThreadId` on `thread.create`.** `ThreadCreateCommand` + (`packages/contracts/src/orchestration.ts`) and `ThreadCreatedPayload` carry + no parent link, so Model Router job threads would need a fork patch that + smuggles the parent linkage (via title prefix, metadata, or a parallel + fork-owned map). A real optional `parentThreadId` field would remove that + patch entirely. +- **Thread visibility independent of archive state.** Threads are listed or + hidden by existing lifecycle flags; there is no "hidden from the sidebar + but reachable from the Agents panel" concept, so Model Router job threads + would need a fork patch in the sidebar query plus the Agents panel + (`apps/web/src/components/AgentsPanel.tsx`). A first-class visibility flag + would remove both edits. +- **Right-panel tab registry.** `RightPanelSurface` + (`apps/web/src/rightPanelStore.ts`) is a closed union and + `apps/web/src/components/RightPanelTabs.tsx` renders a fixed tab set, so an + OpenBot Computer tab would require editing both upstream files. A registry + (fork registers a tab kind without touching the union or renderer) would + remove those edits. +- **Thread persona/owner marker.** `ThreadCreatedPayload` carries no agent + identity, so a Bot-as-thread-with-persona would need a fork patch to attach + and render it. An optional upstream persona field would remove that patch. diff --git a/scripts/fork-check.sh b/scripts/fork-check.sh new file mode 100755 index 0000000000..9eeab392e5 --- /dev/null +++ b/scripts/fork-check.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# fork-check.sh: verify the fork stack model on the current branch. +# Usage: scripts/fork-check.sh [--base ] [--upstream ] [--max-commits ] +# Exits 0 when the stack is a small, merge-free stack whose upstream-file +# modifications are all allowlisted in scripts/fork-upstream-edits.txt. +set -euo pipefail + +BASE="" +UPSTREAM="upstream" +MAX_COMMITS=20 + +while [[ $# -gt 0 ]]; do + case "$1" in + --base) BASE="$2"; shift 2 ;; + --upstream) UPSTREAM="$2"; shift 2 ;; + --max-commits) MAX_COMMITS="$2"; shift 2 ;; + -h|--help) + sed -n '2,6p' "$0" + exit 0 + ;; + *) + echo "fork-check: unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +ROOT="$(git rev-parse --show-toplevel)" +cd "$ROOT" + +if [[ ! -f docs/fork.md ]]; then + echo "fork-check: FAIL: docs/fork.md is missing." >&2 + exit 1 +fi + +if [[ -z "$BASE" ]]; then + if git rev-parse --verify --quiet "$UPSTREAM/main" >/dev/null; then + BASE="$(git merge-base HEAD "$UPSTREAM/main")" + else + BASE="$(git merge-base HEAD origin/main)" + fi +fi + +echo "fork-check: base: $(git log --oneline -1 "$BASE")" + +MERGES="$(git log --merges --format='%h %s' "$BASE"..HEAD)" +if [[ -n "$MERGES" ]]; then + echo "fork-check: FAIL: merge commits in the fork stack (rebase, do not merge):" >&2 + echo "$MERGES" >&2 + exit 1 +fi + +COUNT="$(git rev-list --count "$BASE"..HEAD)" +echo "fork-check: stack: $COUNT commit(s) on top of base (limit $MAX_COMMITS)." +git log --oneline "$BASE"..HEAD || true +if [[ "$COUNT" -gt "$MAX_COMMITS" ]]; then + echo "fork-check: FAIL: stack exceeds $MAX_COMMITS commits; split the work." >&2 + exit 1 +fi + +# Files changed across the stack, classified against the base: a changed path +# that does not exist in the base is a new fork-only file (always fine). +# Anything else (modified or deleted upstream file) must be allowlisted. +ALLOW="$ROOT/scripts/fork-upstream-edits.txt" +ALLOWLIST="$(grep -v '^\s*#' "$ALLOW" | grep -v '^\s*$' || true)" +FAIL=0 +while IFS= read -r path; do + [[ -z "$path" ]] && continue + if git cat-file -e "$BASE:$path" 2>/dev/null; then + if printf '%s\n' "$ALLOWLIST" | grep -qxF "$path"; then + echo "fork-check: modified (allowlisted): $path" + else + echo "fork-check: FAIL: upstream file modified but not allowlisted: $path" >&2 + FAIL=1 + fi + else + echo "fork-check: new fork file: $path" + fi +done < <(git diff --name-only "$BASE"..HEAD) + +if [[ "$FAIL" -ne 0 ]]; then + echo "fork-check: FAIL: list the file in scripts/fork-upstream-edits.txt and docs/fork.md, or move the change to a new file." >&2 + exit 1 +fi + +echo "fork-check: OK." diff --git a/scripts/fork-rebase.sh b/scripts/fork-rebase.sh new file mode 100755 index 0000000000..dca7ab735c --- /dev/null +++ b/scripts/fork-rebase.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# fork-rebase.sh: absorb upstream updates into the fork stack. +# Usage: scripts/fork-rebase.sh [--upstream ] [--dry-run] [--proof] [--push] +# --dry-run report the stack and pending upstream commits without changing anything. +# --proof run the full fork proof after a successful rebase. +# --push publish the branch (requires --proof in the same run, uses +# --force-with-lease, never --force, and refuses on main). +# On conflict the rebase is left in progress and the conflicted upstream files +# are reported; resolve them, run `git rebase --continue`, then re-run this script. +set -euo pipefail + +UPSTREAM="upstream" +DRY_RUN=0 +PROOF=0 +PUSH=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --upstream) UPSTREAM="$2"; shift 2 ;; + --dry-run) DRY_RUN=1; shift ;; + --proof) PROOF=1; shift ;; + --push) PUSH=1; shift ;; + -h|--help) + sed -n '2,10p' "$0" + exit 0 + ;; + *) + echo "fork-rebase: unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +if [[ "$PUSH" -eq 1 && "$PROOF" -ne 1 ]]; then + echo "fork-rebase: refusing to push without proof passing in the same run; re-run with --proof --push." >&2 + exit 1 +fi + +BRANCH="$(git branch --show-current)" +if [[ "$PUSH" -eq 1 && "$BRANCH" == "main" ]]; then + echo "fork-rebase: refusing to push main directly; land absorptions through a PR branch." >&2 + exit 1 +fi + +ROOT="$(git rev-parse --show-toplevel)" +cd "$ROOT" +START="$SECONDS" + +echo "fork-rebase: fetching $UPSTREAM..." +git fetch "$UPSTREAM" main + +TARGET="$(git rev-parse "$UPSTREAM/main")" +BASE="$(git merge-base HEAD "$TARGET")" +echo "fork-rebase: branch: $BRANCH" +echo "fork-rebase: upstream: $(git log --oneline -1 "$TARGET")" +echo "fork-rebase: base: $(git log --oneline -1 "$BASE")" +echo "fork-rebase: stack ($(( $(git rev-list --count "$BASE"..HEAD) )) commit(s)):" +git log --oneline "$BASE"..HEAD || true +PENDING="$(git rev-list --count "$BASE".."$TARGET")" +echo "fork-rebase: pending upstream commits: $PENDING" +if [[ "$PENDING" -gt 0 ]]; then + git log --oneline "$BASE".."$TARGET" | head -n 20 +fi + +if [[ "$DRY_RUN" -eq 1 ]]; then + echo "fork-rebase: dry run; nothing changed." + exit 0 +fi + +if [[ "$PENDING" -eq 0 ]]; then + echo "fork-rebase: already up to date; nothing to replay." +else + echo "fork-rebase: rebasing stack onto $UPSTREAM/main..." + if ! git rebase "$UPSTREAM/main"; then + echo "fork-rebase: CONFLICT. The rebase is left in progress." >&2 + echo "fork-rebase: conflicted upstream files:" >&2 + git diff --name-only --diff-filter=U | while IFS= read -r f; do + echo "fork-rebase: - $f" >&2 + done + echo "fork-rebase: stopped at: $(git log --oneline -1 2>/dev/null || echo '(unknown)')" >&2 + echo "fork-rebase: resolve each file (hint: git log $UPSTREAM/main -- ), run \`git rebase --continue\`, then re-run this script." >&2 + exit 1 + fi + echo "fork-rebase: rebase clean (no conflicts)." +fi + +echo "fork-rebase: running fork check..." +bash "$ROOT/scripts/fork-check.sh" --upstream "$UPSTREAM" + +if [[ "$PROOF" -eq 1 ]]; then + echo "fork-rebase: running full fork proof..." + pnpm install --frozen-lockfile + pnpm exec vp run -r typecheck + pnpm exec vp lint + pnpm exec vp run -r test + pnpm exec vp fmt --check + echo "fork-rebase: proof passed." +fi + +if [[ "$PUSH" -eq 1 ]]; then + if git rev-parse --verify --quiet "refs/remotes/origin/$BRANCH" >/dev/null; then + echo "fork-rebase: publishing rebased stack with --force-with-lease..." + git push --force-with-lease origin "$BRANCH" + else + echo "fork-rebase: publishing new branch..." + git push -u origin "$BRANCH" + fi +fi + +echo "fork-rebase: done in $((SECONDS - START))s." diff --git a/scripts/fork-upstream-edits.txt b/scripts/fork-upstream-edits.txt new file mode 100644 index 0000000000..ec288f0c57 --- /dev/null +++ b/scripts/fork-upstream-edits.txt @@ -0,0 +1,6 @@ +# Canonical allowlist of upstream-owned files the fork may modify. +# One repo-relative path per line. Lines starting with # and blank lines are ignored. +# New fork-only files never need listing here; only modifications (or deletions) +# of files that already exist on the upstream base must be listed. +# Mirrored in the "Upstream edits" section of docs/fork.md; CI enforces the match. +# Currently the fork touches no upstream files. From 0d31a519824eb32ee9e9324e7106e86ed97603fc Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 11:07:30 +0200 Subject: [PATCH 02/10] fix(fork): fork-rebase listing must survive head closing the pipe --- scripts/fork-rebase.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/fork-rebase.sh b/scripts/fork-rebase.sh index dca7ab735c..69ecf36357 100755 --- a/scripts/fork-rebase.sh +++ b/scripts/fork-rebase.sh @@ -59,7 +59,8 @@ git log --oneline "$BASE"..HEAD || true PENDING="$(git rev-list --count "$BASE".."$TARGET")" echo "fork-rebase: pending upstream commits: $PENDING" if [[ "$PENDING" -gt 0 ]]; then - git log --oneline "$BASE".."$TARGET" | head -n 20 + # `|| true`: head closing the pipe is not a failure under pipefail. + git log --oneline "$BASE".."$TARGET" | head -n 20 || true fi if [[ "$DRY_RUN" -eq 1 ]]; then From b465687c0f92f166ac1920e9f6b54650f5f4404c Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 11:17:59 +0200 Subject: [PATCH 03/10] test(fork): focused tests for the rebase routine safety rules fork-check.sh and fork-rebase.sh are driven against throwaway git fixtures: new-file pass, unallowlisted-edit fail, allowlist pass, merge-commit fail, missing-doc fail, no-proof push refusal and no-main push refusal. Refs #6 --- scripts/fork-maintenance.test.ts | 148 +++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 scripts/fork-maintenance.test.ts diff --git a/scripts/fork-maintenance.test.ts b/scripts/fork-maintenance.test.ts new file mode 100644 index 0000000000..41ea31c64b --- /dev/null +++ b/scripts/fork-maintenance.test.ts @@ -0,0 +1,148 @@ +// Fork maintenance routine: drives the real scripts against tiny git fixtures. +// @effect-diagnostics nodeBuiltinImport:off - Drives the real bash/sh routines against throwaway git fixtures. +import * as NodeChildProcess from "node:child_process"; +import * as NodeFS from "node:fs"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import { describe, expect, it } from "vite-plus/test"; + +const scriptsDir = import.meta.dirname; +const forkCheck = NodePath.join(scriptsDir, "fork-check.sh"); +const forkRebase = NodePath.join(scriptsDir, "fork-rebase.sh"); + +function git(cwd: string, ...args: Array): string { + return NodeChildProcess.execFileSync("git", args, { cwd, encoding: "utf8" }); +} + +function makeFixture(): string { + const root = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-fork-check-")); + git(root, "init", "-q", "-b", "work"); + git(root, "config", "user.email", "fork-check@test"); + git(root, "config", "user.name", "fork-check"); + NodeFS.mkdirSync(NodePath.join(root, "docs"), { recursive: true }); + NodeFS.mkdirSync(NodePath.join(root, "scripts"), { recursive: true }); + NodeFS.writeFileSync(NodePath.join(root, "docs", "fork.md"), "# Fork maintenance\n"); + NodeFS.writeFileSync( + NodePath.join(root, "scripts", "fork-upstream-edits.txt"), + "# allowlist\nscripts/fork-upstream-edits.txt\n", + ); + NodeFS.writeFileSync(NodePath.join(root, "upstream-owned.txt"), "base\n"); + git(root, "add", "-A"); + git(root, "commit", "-qm", "base"); + return root; +} + +function runCheck(cwd: string, ...args: Array): { status: number; output: string } { + try { + const output = NodeChildProcess.execFileSync("bash", [forkCheck, ...args], { + cwd, + encoding: "utf8", + }); + return { status: 0, output }; + } catch (error) { + const result = error as { status?: number; stdout?: string; stderr?: string }; + return { + status: result.status ?? 1, + output: `${result.stdout ?? ""}${result.stderr ?? ""}`, + }; + } +} + +function baseOf(root: string): string { + return git(root, "rev-parse", "HEAD").trim(); +} + +describe("fork-check", () => { + it("passes for fork-only new files on top of the base", () => { + const root = makeFixture(); + const base = baseOf(root); + NodeFS.writeFileSync(NodePath.join(root, "docs", "new-fork-doc.md"), "fork only\n"); + git(root, "add", "-A"); + git(root, "commit", "-qm", "fork: new file"); + const result = runCheck(root, "--base", base); + expect(result.status).toBe(0); + expect(result.output).toContain("fork-check: OK."); + }); + + it("fails on unallowlisted upstream edits and passes once allowlisted", () => { + const root = makeFixture(); + const base = baseOf(root); + NodeFS.writeFileSync(NodePath.join(root, "upstream-owned.txt"), "forked\n"); + git(root, "add", "-A"); + git(root, "commit", "-qm", "fork: touch upstream file"); + const denied = runCheck(root, "--base", base); + expect(denied.status).toBe(1); + expect(denied.output).toContain("not allowlisted: upstream-owned.txt"); + + NodeFS.appendFileSync( + NodePath.join(root, "scripts", "fork-upstream-edits.txt"), + "upstream-owned.txt\n", + ); + git(root, "add", "-A"); + git(root, "commit", "-qm", "fork: allowlist the edit"); + const allowed = runCheck(root, "--base", base); + expect(allowed.status).toBe(0); + expect(allowed.output).toContain("modified (allowlisted): upstream-owned.txt"); + }); + + it("fails on merge commits in the stack", () => { + const root = makeFixture(); + const base = baseOf(root); + git(root, "checkout", "-qb", "side"); + NodeFS.writeFileSync(NodePath.join(root, "side.txt"), "side\n"); + git(root, "add", "-A"); + git(root, "commit", "-qm", "side"); + git(root, "checkout", "-q", "work"); + NodeFS.writeFileSync(NodePath.join(root, "work.txt"), "work\n"); + git(root, "add", "-A"); + git(root, "commit", "-qm", "work"); + git(root, "merge", "--no-ff", "-m", "merge side", "side"); + const result = runCheck(root, "--base", base); + expect(result.status).toBe(1); + expect(result.output).toContain("merge commits in the fork stack"); + }); + + it("fails when docs/fork.md is missing", () => { + const root = makeFixture(); + const base = baseOf(root); + NodeFS.rmSync(NodePath.join(root, "docs", "fork.md")); + git(root, "add", "-A"); + git(root, "commit", "-qm", "fork: drop the doc"); + const result = runCheck(root, "--base", base); + expect(result.status).toBe(1); + expect(result.output).toContain("docs/fork.md is missing"); + }); +}); + +describe("fork-rebase safety rules", () => { + it("refuses to push without proof in the same run", () => { + const root = makeFixture(); + try { + NodeChildProcess.execFileSync("bash", [forkRebase, "--push"], { + cwd: root, + encoding: "utf8", + }); + expect.unreachable("fork-rebase --push without --proof must fail"); + } catch (error) { + const result = error as { status?: number; stderr?: string }; + expect(result.status).toBe(1); + expect(result.stderr).toContain("refusing to push without proof"); + } + }); + + it("refuses to push main directly", () => { + const root = makeFixture(); + git(root, "checkout", "-qb", "main"); + try { + NodeChildProcess.execFileSync("bash", [forkRebase, "--proof", "--push"], { + cwd: root, + encoding: "utf8", + }); + expect.unreachable("fork-rebase --push on main must fail"); + } catch (error) { + const result = error as { status?: number; stderr?: string }; + expect(result.status).toBe(1); + expect(result.stderr).toContain("refusing to push main directly"); + } + }); +}); From a6e2d445a70edce407e9f90fb5fa2b44fc2d016f Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 11:18:01 +0200 Subject: [PATCH 04/10] docs(fork): list maintenance tests and fix formatter whitespace Refs #6 --- docs/fork.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/fork.md b/docs/fork.md index 7446d92971..950e1561c4 100644 --- a/docs/fork.md +++ b/docs/fork.md @@ -30,6 +30,8 @@ New files owned by the fork (no upstream counterpart, always allowed): - `docs/fork.md` — this document. - `scripts/fork-rebase.sh` — the rebase routine. - `scripts/fork-check.sh` — the stack-model check run by CI. +- `scripts/fork-maintenance.test.ts` — focused tests driving both scripts + against throwaway git fixtures (run with upstream's own test suite). - `scripts/fork-upstream-edits.txt` — canonical allowlist of upstream files the fork may modify (currently empty). - `.github/workflows/fork.yml` — fork CI. Upstream's own `ci.yml` is @@ -110,8 +112,8 @@ elapsed time, conflicts by file (or "none"), and proof. `git fetch upstream main` + `git rebase upstream/main` via `scripts/fork-rebase.sh`, wall time under 2 minutes for fetch plus rebase. Full proof (`pnpm install --frozen-lockfile && pnpm exec vp run -r - typecheck && pnpm exec vp lint && pnpm exec vp run -r test && pnpm exec vp - fmt --check`) run on the rebased candidate; results recorded in the PR. +typecheck && pnpm exec vp lint && pnpm exec vp run -r test && pnpm exec vp +fmt --check`) run on the rebased candidate; results recorded in the PR. ## Candidate upstream extension points From d19324a50ec0f018bb0dc72febd71a3a400def85 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 12:19:02 +0200 Subject: [PATCH 05/10] fix(fork): run proof in a subshell cleared of ELECTRON_RUN_AS_NODE --- docs/fork.md | 5 ++++- scripts/fork-rebase.sh | 16 +++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/fork.md b/docs/fork.md index 950e1561c4..e8a532e315 100644 --- a/docs/fork.md +++ b/docs/fork.md @@ -50,7 +50,10 @@ that any modification to an upstream file not listed here fails. `scripts/fork-rebase.sh` fetches `upstream`, rebases the current branch's stack onto `upstream/main`, runs the fork check, optionally runs the full proof, and optionally pushes. It reports conflicts per upstream file and never -force-pushes a published branch without proof passing in the same run. +force-pushes a published branch without proof passing in the same run. The +proof runs in a subshell with `ELECTRON_RUN_AS_NODE` cleared so an inherited +Electron environment does not contaminate upstream tests that inspect spawned +command environments. ```bash # Report the stack and what an absorption would replay, without changing anything. diff --git a/scripts/fork-rebase.sh b/scripts/fork-rebase.sh index 69ecf36357..373c623e8a 100755 --- a/scripts/fork-rebase.sh +++ b/scripts/fork-rebase.sh @@ -90,11 +90,17 @@ bash "$ROOT/scripts/fork-check.sh" --upstream "$UPSTREAM" if [[ "$PROOF" -eq 1 ]]; then echo "fork-rebase: running full fork proof..." - pnpm install --frozen-lockfile - pnpm exec vp run -r typecheck - pnpm exec vp lint - pnpm exec vp run -r test - pnpm exec vp fmt --check + # An inherited ELECTRON_RUN_AS_NODE (for example from a running T3 Code + # desktop instance) contaminates tests that inspect spawned command + # environments. Run the proof in a subshell with it cleared. + ( + unset ELECTRON_RUN_AS_NODE + pnpm install --frozen-lockfile + pnpm exec vp run -r typecheck + pnpm exec vp lint + pnpm exec vp run -r test + pnpm exec vp fmt --check + ) echo "fork-rebase: proof passed." fi From 11092724002a55e2549bc3426b0b8945de04b8f5 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 14:03:14 +0200 Subject: [PATCH 06/10] docs(fork): Chromeria install, squash-plus-absorption model and GitHub-hosted CI Document building, installing and updating the Chromeria desktop app, land absorptions on main with --force-with-lease after proof, allowlist every fork edit of upstream files, remap the PR and main CI workflows from Blacksmith to GitHub-hosted runners, and run the routine's proof in a host-neutral environment. Refs #6. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 14 +- .github/workflows/fork.yml | 2 +- .../workflows/mobile-fingerprint-check.yml | 2 +- docs/fork.md | 170 +++++++++++++----- scripts/fork-rebase.sh | 13 +- scripts/fork-upstream-edits.txt | 25 ++- 6 files changed, 170 insertions(+), 56 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 611d4cf44f..f324ab4563 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ concurrency: jobs: check: name: Check - runs-on: blacksmith-8vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - name: Checkout @@ -75,7 +75,7 @@ jobs: # limit stays at the default 4 so peak load per runner is unchanged. test: name: Test - runs-on: blacksmith-8vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - name: Checkout @@ -118,7 +118,7 @@ jobs: # isolation that flag buys is preserved exactly. test_server: name: Test Server ${{ matrix.shard }} - runs-on: blacksmith-8vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 10 strategy: fail-fast: false @@ -185,7 +185,7 @@ jobs: # for checks that take under 3s, on the critical path of every PR. rust: name: Rust - runs-on: blacksmith-4vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - name: Checkout @@ -219,7 +219,7 @@ jobs: # the diff cannot be resolved, the lint runs. mobile_native_changes: name: Mobile Native Changes - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 5 permissions: contents: read @@ -297,7 +297,7 @@ jobs: # Skip only on an explicit "no": a gate job that failed or errored leaves the # output empty, and that must run the lint rather than silently skip it. if: ${{ !cancelled() && needs.mobile_native_changes.outputs.changed != 'false' }} - runs-on: blacksmith-6vcpu-macos-26 + runs-on: macos-15 timeout-minutes: 10 steps: - name: Checkout @@ -325,7 +325,7 @@ jobs: release_smoke: name: Release Smoke - runs-on: blacksmith-8vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - name: Checkout diff --git a/.github/workflows/fork.yml b/.github/workflows/fork.yml index 4f8209bc9c..bec724138d 100644 --- a/.github/workflows/fork.yml +++ b/.github/workflows/fork.yml @@ -14,7 +14,7 @@ permissions: jobs: fork-model: name: Fork Stack Model - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Checkout diff --git a/.github/workflows/mobile-fingerprint-check.yml b/.github/workflows/mobile-fingerprint-check.yml index b8b872f019..5849b71ddb 100644 --- a/.github/workflows/mobile-fingerprint-check.yml +++ b/.github/workflows/mobile-fingerprint-check.yml @@ -24,7 +24,7 @@ concurrency: jobs: fingerprint: name: Native fingerprint diff - runs-on: blacksmith-8vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 permissions: contents: read issues: write diff --git a/docs/fork.md b/docs/fork.md index e8a532e315..4d1bbd0dbf 100644 --- a/docs/fork.md +++ b/docs/fork.md @@ -9,10 +9,16 @@ patch set further. ## Model - **Base.** Upstream `main` (`https://github.com/pingdotgg/t3code.git`, - remote name `upstream`). Fork `main` tracks it and adds only the stack below. + remote name `upstream`). Fork `main` is that base plus the stack below. - **Stack.** Fork work lives as topic commits on top of the current upstream - base. No merge commits: upstream updates are absorbed by rebasing the stack, - never by merging `upstream/main` into the fork. + base. A topic PR is squash-merged, so each lands as one stack commit. No + merge commits: upstream updates are absorbed by rebasing the stack, never + by merging `upstream/main` into the fork. +- **Absorption moves `main`.** Rebasing the stack rewrites `main`, which no + PR merge can do: squash or rebase merges would copy upstream commits under + new hashes and break the stack. An absorption is proven on its own branch + and then lands with `--force-with-lease` (see Routine). That push is a + human-approved step. - **New files first.** Features go in new files and new packages. Edits to upstream-owned files stay minimal and every edited file is listed under "Upstream edits" below and in the machine-checked allowlist @@ -26,34 +32,111 @@ patch set further. New files owned by the fork (no upstream counterpart, always allowed): -- `VISION.md`, `MISSION.md`, `OBJECTIVE.md` — Project Direction, merged in #5. -- `docs/fork.md` — this document. -- `scripts/fork-rebase.sh` — the rebase routine. -- `scripts/fork-check.sh` — the stack-model check run by CI. -- `scripts/fork-maintenance.test.ts` — focused tests driving both scripts - against throwaway git fixtures (run with upstream's own test suite). -- `scripts/fork-upstream-edits.txt` — canonical allowlist of upstream files - the fork may modify (currently empty). -- `.github/workflows/fork.yml` — fork CI. Upstream's own `ci.yml` is - untouched; the two workflows together give every PR upstream checks plus - fork checks. +- `VISION.md`, `MISSION.md`, `OBJECTIVE.md`, `GLOSSARY.md`: Project Direction + and project language. +- `assets/chromeria/`: Chromeria icons. +- `docs/fork.md`: this document. +- `scripts/fork-rebase.sh`: the rebase routine. +- `scripts/fork-check.sh`: the stack-model check run by CI. +- `scripts/fork-maintenance.test.ts`: tests driving both scripts against + throwaway git fixtures (runs with upstream's own test suite). +- `scripts/fork-upstream-edits.txt`: allowlist of upstream files the fork + may modify. +- `.github/workflows/fork.yml`: fork CI (the stack check). ## Upstream edits -Upstream-owned files modified by the fork. Keep this list exact; CI enforces -that any modification to an upstream file not listed here fails. +Upstream-owned files modified by the fork. `scripts/fork-check.sh` fails on +any modified upstream file missing from `scripts/fork-upstream-edits.txt`. + +- Chromeria branding (#12, #13): `apps/desktop/package.json` (product + name), `apps/desktop/src/app/DesktopEnvironment.ts` (names, userData + directory), `scripts/build-desktop-artifact.ts` (app id, artifact name, no + update feed, icons), `scripts/lib/brand-assets.ts` (icon paths), and the + expectations in `apps/desktop/src/app/DesktopAppIdentity.test.ts`, + `apps/desktop/src/app/DesktopPreReadyPlatform.test.ts`, + `scripts/build-desktop-artifact.test.ts` and + `scripts/lib/brand-assets.test.ts`. +- Child threads (#8, #10): `apps/server/src/mcp/McpHttpServer.ts` + registers the threads MCP toolkit; `apps/web/src/components/AgentsPanel.tsx` + links agents to their child threads; `apps/web/src/components/Sidebar.tsx` + and `apps/web/src/components/LegacySidebar.tsx` hide child threads from the + sidebar. +- `scripts/build-desktop-artifact.ts` (#10): the packaged-bundle + self-containment probe clears an inherited `ELECTRON_RUN_AS_NODE`. +- `apps/server/src/entrypoint.test.ts` (#10): resolves the fixture directory + so the test passes under macOS's symlinked `TMPDIR`. +- `.github/workflows/ci.yml`, `.github/workflows/mobile-fingerprint-check.yml`: + `blacksmith-*-ubuntu-2404` runners become `ubuntu-24.04` and + `blacksmith-*-macos-*` becomes `macos-15` (see CI). + +## Chromeria desktop app + +Chromeria is the fork's desktop build: bundle id `md.toolbox.chromeria`, +Electron userData `~/Library/Application Support/chromeria`, no update feed. +It installs next to upstream "T3 Code (Alpha)" and reads the same T3 home +(`T3CODE_HOME`, else `~/.t3`, state in `/userdata`), so its threads +are the upstream app's threads. + +**Never run Chromeria and T3 Code (Alpha) on the same T3 home at once.** Both +would start a server on one database. Quit one fully (Cmd-Q, not just close +the window) before opening the other. To try Chromeria while the upstream +app keeps running, give Chromeria its own home, which starts with no threads: -- (none) +```bash +open -na /Applications/Chromeria.app --env T3CODE_HOME="$HOME/.t3-chromeria" +``` + +Build (Apple Silicon, from a checkout of fork `main`): + +```bash +pnpm install --frozen-lockfile +pnpm dist:desktop:dmg:arm64 # writes release/Chromeria--arm64.dmg +``` + +Install or update (quit Chromeria first): + +```bash +hdiutil attach release/Chromeria-*-arm64.dmg -nobrowse -mountpoint /tmp/chromeria-dmg +rm -rf /Applications/Chromeria.app +ditto /tmp/chromeria-dmg/Chromeria.app /Applications/Chromeria.app +hdiutil detach /tmp/chromeria-dmg +codesign --force --deep --sign - /Applications/Chromeria.app +open /Applications/Chromeria.app +``` + +Gotchas: + +- The local build is unsigned. Without the ad-hoc `codesign` step macOS + reports the app as damaged or refuses to open it. +- Each build has a new ad-hoc signature, so macOS may ask again for + permissions granted to the previous build (screen recording, folders). +- There is no auto-update. Updating is: pull fork `main`, rebuild, + reinstall as above. +- Both apps register the `t3code://` URL scheme, so a browser sign-in + callback may open either app. +- Both apps prefer backend port 3773; whichever starts second takes the next + free port. ## Routine `scripts/fork-rebase.sh` fetches `upstream`, rebases the current branch's stack onto `upstream/main`, runs the fork check, optionally runs the full proof, and optionally pushes. It reports conflicts per upstream file and never -force-pushes a published branch without proof passing in the same run. The -proof runs in a subshell with `ELECTRON_RUN_AS_NODE` cleared so an inherited -Electron environment does not contaminate upstream tests that inspect spawned -command environments. +force-pushes a published branch without proof passing in the same run. + +The proof runs in a host-neutral environment, because upstream's tests assume +CI's Linux host. On a Mac, run from a T3 thread, about 50 upstream tests +otherwise fail without any fork change: T3 Code desktop leaks +`ELECTRON_RUN_AS_NODE=1` into agent shells, macOS `TMPDIR` sits behind the +`/var` to `/private/var` symlink, and Homebrew's `brew` on `PATH` adds +provider probes. To run the proof by hand: + +```bash +env -u ELECTRON_RUN_AS_NODE TMPDIR="$(cd "$TMPDIR" && pwd -P)/" \ + PATH="$(printf '%s' "$PATH" | tr ':' '\n' | grep -v '^/opt/homebrew' | paste -sd: -)" \ + sh -c 'pnpm install --frozen-lockfile && pnpm exec vp run -r typecheck && pnpm exec vp lint && pnpm exec vp run -r test && pnpm exec vp fmt --check' +``` ```bash # Report the stack and what an absorption would replay, without changing anything. @@ -70,6 +153,20 @@ scripts/fork-rebase.sh --proof scripts/fork-rebase.sh --proof --push ``` +An absorption, end to end: + +```bash +git fetch origin +git switch -c fork/absorb- origin/main +scripts/fork-rebase.sh --proof --push +# Open a PR to main for CI and review. Do not merge it through GitHub. +# After approval, land the proven branch on main: +git push --force-with-lease=main: origin fork/absorb-:main +``` + +Open topic PRs then rebase onto the new `main` (`git rebase --onto +origin/main `). + On conflict the script stops at the failing commit, prints the conflicted upstream files (`git diff --name-only --diff-filter=U`), and exits non-zero with the rebase left in progress for manual resolution (`git status` to see @@ -77,20 +174,26 @@ the files, resolve, `git rebase --continue`, then re-run the script). Record the conflicted files and the resolution in the absorption log below and in the PR. -Rules that the script enforces and humans must follow when working by hand: +Rules: - Never `git push --force`. A published rebased branch moves only via `git push --force-with-lease` after the same proof passed. -- Never rebase or push `main` directly; land upstream absorptions through a - PR branch, then merge the PR. +- `main` moves only by squash-merging a topic PR, or by the approved + `--force-with-lease` landing of a proven absorption branch. - Bind proof to the final candidate: any rebase or conflict resolution invalidates earlier proof, so the proof must run last. ## CI +Upstream's jobs run on Blacksmith runners the fork does not have, so they +would queue forever. The fork remaps the PR and `main` workflows (`ci.yml`, +`mobile-fingerprint-check.yml`) to GitHub-hosted runners, which are free for +public repositories. Release, deploy, preview and Windows workflows keep +their Blacksmith labels: they need upstream secrets and must not run here. + `.github/workflows/fork.yml` runs on every pull request and on pushes to -`main`, alongside upstream's own `ci.yml`. It adds the `upstream` remote, -fetches it, and runs `scripts/fork-check.sh`, which verifies: +`main`, alongside `ci.yml`. It adds the `upstream` remote, fetches it, and +runs `scripts/fork-check.sh`, which verifies: - the stack on top of the upstream base contains no merge commits, - the stack is small (at most 20 commits), @@ -98,26 +201,11 @@ fetches it, and runs `scripts/fork-check.sh`, which verifies: `scripts/fork-upstream-edits.txt` (new fork-only files are always fine), - this document exists. -Upstream's `ci.yml` continues to run the project's own checks and tests -unchanged. - ## Absorption log One entry per absorbed upstream update: exact base and candidate commits, elapsed time, conflicts by file (or "none"), and proof. -- **2026-09-25** (this issue, #6): absorbed upstream `main` `e3e7cc3fc` - (`fix(usage): price Claude fast-mode requests at the fast rate (#13599)`) - onto fork branch `chore/6-fork-maint`. Previous base `b2b43bef7` - (`fix(server): preserve racy edits in review diff previews (#12613)`) plus - fork commit `3cfadb7bc` (#5, Project Direction). 54 upstream commits - replayed under the stack; **conflicts: none**. Routine: - `git fetch upstream main` + `git rebase upstream/main` via - `scripts/fork-rebase.sh`, wall time under 2 minutes for fetch plus rebase. - Full proof (`pnpm install --frozen-lockfile && pnpm exec vp run -r -typecheck && pnpm exec vp lint && pnpm exec vp run -r test && pnpm exec vp -fmt --check`) run on the rebased candidate; results recorded in the PR. - ## Candidate upstream extension points Generic seams the fork's planned work (Model Router threads, OpenBot mode) diff --git a/scripts/fork-rebase.sh b/scripts/fork-rebase.sh index 373c623e8a..dac55e0d7c 100755 --- a/scripts/fork-rebase.sh +++ b/scripts/fork-rebase.sh @@ -38,7 +38,7 @@ fi BRANCH="$(git branch --show-current)" if [[ "$PUSH" -eq 1 && "$BRANCH" == "main" ]]; then - echo "fork-rebase: refusing to push main directly; land absorptions through a PR branch." >&2 + echo "fork-rebase: refusing to push main directly; push an absorption branch, then land it as docs/fork.md describes." >&2 exit 1 fi @@ -90,11 +90,16 @@ bash "$ROOT/scripts/fork-check.sh" --upstream "$UPSTREAM" if [[ "$PROOF" -eq 1 ]]; then echo "fork-rebase: running full fork proof..." - # An inherited ELECTRON_RUN_AS_NODE (for example from a running T3 Code - # desktop instance) contaminates tests that inspect spawned command - # environments. Run the proof in a subshell with it cleared. + # Run the proof in a host-neutral subshell. Upstream tests assume CI's Linux + # host: an inherited ELECTRON_RUN_AS_NODE (T3 Code desktop leaks it into + # agent shells) changes spawned command environments, macOS's /var -> + # /private/var TMPDIR symlink breaks path equality, and a Homebrew `brew` + # on PATH adds extra provider probes. ( unset ELECTRON_RUN_AS_NODE + TMPDIR="$(cd "${TMPDIR:-/tmp}" && pwd -P)/" + PATH="$(printf '%s' "$PATH" | tr ':' '\n' | grep -v '^/opt/homebrew' | paste -sd: -)" + export TMPDIR PATH pnpm install --frozen-lockfile pnpm exec vp run -r typecheck pnpm exec vp lint diff --git a/scripts/fork-upstream-edits.txt b/scripts/fork-upstream-edits.txt index ec288f0c57..936cc2dd2f 100644 --- a/scripts/fork-upstream-edits.txt +++ b/scripts/fork-upstream-edits.txt @@ -2,5 +2,26 @@ # One repo-relative path per line. Lines starting with # and blank lines are ignored. # New fork-only files never need listing here; only modifications (or deletions) # of files that already exist on the upstream base must be listed. -# Mirrored in the "Upstream edits" section of docs/fork.md; CI enforces the match. -# Currently the fork touches no upstream files. +# scripts/fork-check.sh enforces this list; keep "Upstream edits" in docs/fork.md +# describing the same files. + +# Chromeria branding (#12, #13) +apps/desktop/package.json +apps/desktop/src/app/DesktopAppIdentity.test.ts +apps/desktop/src/app/DesktopEnvironment.ts +apps/desktop/src/app/DesktopPreReadyPlatform.test.ts +scripts/build-desktop-artifact.ts +scripts/build-desktop-artifact.test.ts +scripts/lib/brand-assets.ts +scripts/lib/brand-assets.test.ts + +# Child threads in the Agents panel and threads MCP toolkit (#8, #10) +apps/server/src/entrypoint.test.ts +apps/server/src/mcp/McpHttpServer.ts +apps/web/src/components/AgentsPanel.tsx +apps/web/src/components/LegacySidebar.tsx +apps/web/src/components/Sidebar.tsx + +# CI on GitHub-hosted runners (#6) +.github/workflows/ci.yml +.github/workflows/mobile-fingerprint-check.yml From ba552a78a8b7a461822772360bde106957613a69 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 14:09:18 +0200 Subject: [PATCH 07/10] ci(fork): check the PR head, not GitHub's merge ref Refs #6. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/fork.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/fork.yml b/.github/workflows/fork.yml index bec724138d..db2c92400f 100644 --- a/.github/workflows/fork.yml +++ b/.github/workflows/fork.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + # Check the PR head itself; the default PR merge ref is a merge commit. + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Fetch upstream base run: | From f156c64aa524f8ab3edf850ef2d956ec97ced86e Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 14:42:23 +0200 Subject: [PATCH 08/10] docs(fork): allowlist the #14 web branding edits Refs #6. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/fork.md | 9 ++++++++- scripts/fork-upstream-edits.txt | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/fork.md b/docs/fork.md index 4d1bbd0dbf..8e3c803ab4 100644 --- a/docs/fork.md +++ b/docs/fork.md @@ -34,7 +34,7 @@ New files owned by the fork (no upstream counterpart, always allowed): - `VISION.md`, `MISSION.md`, `OBJECTIVE.md`, `GLOSSARY.md`: Project Direction and project language. -- `assets/chromeria/`: Chromeria icons. +- `assets/chromeria/`, `apps/web/public/chromeria-mark.png`: Chromeria icons. - `docs/fork.md`: this document. - `scripts/fork-rebase.sh`: the rebase routine. - `scripts/fork-check.sh`: the stack-model check run by CI. @@ -57,6 +57,13 @@ any modified upstream file missing from `scripts/fork-upstream-edits.txt`. `apps/desktop/src/app/DesktopPreReadyPlatform.test.ts`, `scripts/build-desktop-artifact.test.ts` and `scripts/lib/brand-assets.test.ts`. +- Chromeria name in the web UI (#14): `apps/web/index.html`, + `apps/web/src/branding.ts`, `apps/web/src/lib/bootError.ts` and the + components `T3Wordmark.tsx`, `chat/MessagesTimeline.tsx`, + `onboarding/WelcomeWizard.tsx`, `settings/IntegrationsSettings.tsx`, + `settings/ThemePreviewCircles.tsx` and `sidebar/SidebarChrome.tsx` under + `apps/web/src/components/`, with expectations in + `apps/web/src/bootstrap.test.ts` and `apps/web/src/branding.test.ts`. - Child threads (#8, #10): `apps/server/src/mcp/McpHttpServer.ts` registers the threads MCP toolkit; `apps/web/src/components/AgentsPanel.tsx` links agents to their child threads; `apps/web/src/components/Sidebar.tsx` diff --git a/scripts/fork-upstream-edits.txt b/scripts/fork-upstream-edits.txt index 936cc2dd2f..e500ca3d51 100644 --- a/scripts/fork-upstream-edits.txt +++ b/scripts/fork-upstream-edits.txt @@ -15,6 +15,19 @@ scripts/build-desktop-artifact.test.ts scripts/lib/brand-assets.ts scripts/lib/brand-assets.test.ts +# Chromeria name in the web UI (#14) +apps/web/index.html +apps/web/src/bootstrap.test.ts +apps/web/src/branding.test.ts +apps/web/src/branding.ts +apps/web/src/components/T3Wordmark.tsx +apps/web/src/components/chat/MessagesTimeline.tsx +apps/web/src/components/onboarding/WelcomeWizard.tsx +apps/web/src/components/settings/IntegrationsSettings.tsx +apps/web/src/components/settings/ThemePreviewCircles.tsx +apps/web/src/components/sidebar/SidebarChrome.tsx +apps/web/src/lib/bootError.ts + # Child threads in the Agents panel and threads MCP toolkit (#8, #10) apps/server/src/entrypoint.test.ts apps/server/src/mcp/McpHttpServer.ts From d41fc9be02d5d22e6c808930ff81b7c70459a2b5 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 17:37:10 +0200 Subject: [PATCH 09/10] fix(server): remove unused threads toolkit export --- apps/server/src/mcp/McpHttpServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/mcp/McpHttpServer.ts b/apps/server/src/mcp/McpHttpServer.ts index ce24afcb62..ad2dd73713 100644 --- a/apps/server/src/mcp/McpHttpServer.ts +++ b/apps/server/src/mcp/McpHttpServer.ts @@ -610,7 +610,7 @@ export const PullRequestsToolkitRegistrationLive = McpServer.toolkit(PullRequest Layer.provide(PullRequestsToolkitHandlersLive), ); -export const ThreadsToolkitRegistrationLive = McpServer.toolkit(ThreadsToolkit).pipe( +const ThreadsToolkitRegistrationLive = McpServer.toolkit(ThreadsToolkit).pipe( Layer.provide(ThreadsToolkitHandlersLive), ); From 6a5985bcef85a439065deb96c3c48cbb9c6076a3 Mon Sep 17 00:00:00 2001 From: lukemaj Date: Fri, 25 Sep 2026 17:45:36 +0200 Subject: [PATCH 10/10] fix(web): provide branding in bundled dev fixture --- apps/web/src/bundledDev.test.ts | 4 ++++ docs/fork.md | 3 ++- scripts/fork-upstream-edits.txt | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/web/src/bundledDev.test.ts b/apps/web/src/bundledDev.test.ts index 599b45d225..e5324e003d 100644 --- a/apps/web/src/bundledDev.test.ts +++ b/apps/web/src/bundledDev.test.ts @@ -32,6 +32,10 @@ it("initializes React refresh before a shared UI chunk runs in bundled dev", asy for (const file of ["index.html", "src/bootstrap.ts", "src/lib/bootError.ts"]) { await NodeFSP.copyFile(new URL(`../${file}`, import.meta.url), NodePath.join(root, file)); } + await NodeFSP.writeFile( + NodePath.join(root, "src/branding.ts"), + 'export const APP_BASE_NAME = "Chromeria";\n', + ); await NodeFSP.writeFile( NodePath.join(root, "src/shared.tsx"), "export function Shared() { return
ready
; }", diff --git a/docs/fork.md b/docs/fork.md index 8e3c803ab4..39e199336c 100644 --- a/docs/fork.md +++ b/docs/fork.md @@ -58,7 +58,8 @@ any modified upstream file missing from `scripts/fork-upstream-edits.txt`. `scripts/build-desktop-artifact.test.ts` and `scripts/lib/brand-assets.test.ts`. - Chromeria name in the web UI (#14): `apps/web/index.html`, - `apps/web/src/branding.ts`, `apps/web/src/lib/bootError.ts` and the + `apps/web/src/branding.ts`, `apps/web/src/lib/bootError.ts`, and the + branding fixture in `apps/web/src/bundledDev.test.ts`, plus the components `T3Wordmark.tsx`, `chat/MessagesTimeline.tsx`, `onboarding/WelcomeWizard.tsx`, `settings/IntegrationsSettings.tsx`, `settings/ThemePreviewCircles.tsx` and `sidebar/SidebarChrome.tsx` under diff --git a/scripts/fork-upstream-edits.txt b/scripts/fork-upstream-edits.txt index e500ca3d51..9bedb2a279 100644 --- a/scripts/fork-upstream-edits.txt +++ b/scripts/fork-upstream-edits.txt @@ -20,6 +20,7 @@ apps/web/index.html apps/web/src/bootstrap.test.ts apps/web/src/branding.test.ts apps/web/src/branding.ts +apps/web/src/bundledDev.test.ts apps/web/src/components/T3Wordmark.tsx apps/web/src/components/chat/MessagesTimeline.tsx apps/web/src/components/onboarding/WelcomeWizard.tsx