From 31846e0650f153baa524c3753f7cff05096817d5 Mon Sep 17 00:00:00 2001 From: Raj D <25481060+radroid@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:06:01 -0400 Subject: [PATCH 1/2] ci(coil): split the test gate like upstream so web tests get their own runner The single verify job ran the web suite alongside mobile and desktop, and MessagesTimeline.test.tsx's beforeAll (an in-file 30s literal that --hookTimeout cannot override) timed out on 3 of 3 runs after the 2026-10-08 sync. Mirror upstream's ci.yml: typecheck+lint, the remaining packages, web, and six server shards each run on their own runner. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/coil-ci.yml | 124 ++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/.github/workflows/coil-ci.yml b/.github/workflows/coil-ci.yml index df9e4f0a53b0..56f6615f8303 100644 --- a/.github/workflows/coil-ci.yml +++ b/.github/workflows/coil-ci.yml @@ -27,11 +27,54 @@ concurrency: cancel-in-progress: true jobs: - verify: + check: + name: Typecheck and lint runs-on: ubuntu-latest - # The whole gate ran 13-17 minutes before the 2026-10-08 sync grew upstream's server suite - # from 354 to 524 test files (it took 28 minutes locally on an M-series Mac). - timeout-minutes: 40 + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 + with: + node-version-file: package.json + cache: true + run-install: true + + # Run both even if typecheck fails, so a single run reports every failure. + - name: Typecheck + run: vp run typecheck + + - name: Lint + if: ${{ !cancelled() }} + run: vp run lint + + # The test jobs mirror upstream's ci.yml split: web and server each get their own runners, and + # everything else shares one. In a single job (before 2026-10-08) the web suite ran alongside + # mobile and desktop, and on these runners `MessagesTimeline.test.tsx` blew the 30s budget its + # own `beforeAll` pins for a heavy import (3 of 3 runs). An in-file literal beats + # `--hookTimeout`, so the only fork-side fix is to stop starving the web suite of CPU. + # + # `--testTimeout` is raised because `apps/web/vite.config.ts` pins testTimeout to 15s, which is + # tuned for upstream's blacksmith runners. On ubuntu-latest, upstream's `stashImageCompression` + # worst-case test (~3s on an M-series Mac; 6x the next slowest in its file) reliably exceeds 15s. + # Proven upstream-inherited, not a fork regression: branch `t3x/ci-control-upstream` — pristine + # upstream/main plus this workflow file and nothing else — fails identically (run 30386125253). + # + # 120000 is deliberate: it is the HIGHEST value any package configures + # (apps/server/vite.config.ts pins 120s for sqlite+git+orchestration integration tests). The + # flag applies to every package, so a lower value would silently CUT apps/server's budget. At + # 120s no package is lowered: web 15s -> 120s, root 60s -> 120s, server 120s -> 120s. Each job's + # `timeout-minutes` remains the backstop against a genuinely hung test. + # + # `--hookTimeout` is raised for the same reason and is NOT covered by `--testTimeout`: a + # `beforeAll`/`beforeEach` gets its own budget, unless the file passes one itself. + # Keep in sync with the `test` case in `scripts/coil/sync-upstream.sh`. + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v6 @@ -50,42 +93,53 @@ jobs: - name: Install browser secret helper build libraries run: sudo apt-get update && sudo apt-get install -y libsecret-1-dev pkg-config - # Run all three even if an earlier one fails, so a single run reports every failure. - - name: Typecheck - run: vp run typecheck + # `@t3tools/monorepo` is the root package; its `test` script is `vp run -r test`. + - name: Test + run: vp run --filter '!t3' --filter '!@t3tools/monorepo' --filter '!@t3tools/web' test --testTimeout=120000 --hookTimeout=120000 - - name: Lint - if: ${{ !cancelled() }} - run: vp run lint + test_web: + name: Test Web + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 + with: + node-version-file: package.json + cache: true + run-install: true + + - name: Test + run: vp run --filter @t3tools/web test --testTimeout=120000 --hookTimeout=120000 + + # apps/server sets `fileParallelism: false`, so its 524 test files run one at a time. Upstream + # shards them six ways by recorded duration (apps/server/src/testUtils/weightedShardSequencer.ts). + test_server: + name: Test Server ${{ matrix.shard }} + runs-on: ubuntu-latest + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5, 6] + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 + with: + node-version-file: package.json + cache: true + run-install: true # Upstream's ServerBrowserPage.test.ts drives a real headless Chromium; upstream's ci.yml # installs it the same way before its server tests. - name: Install Chromium for server browser integration tests run: node apps/server/node_modules/playwright-core/cli.js install --with-deps --only-shell chromium - # `--testTimeout` is raised because `apps/web/vite.config.ts` pins testTimeout to 15s, which is - # tuned for upstream's blacksmith runners. This fork runs on 2-core ubuntu-latest, where - # upstream's `stashImageCompression` worst-case test (~3s on an M-series Mac; 6x the next - # slowest in its file) reliably exceeds 15s and fails the whole gate. - # - # Proven upstream-inherited, not a fork regression: branch `t3x/ci-control-upstream` — pristine - # upstream/main plus this workflow file and nothing else — fails identically (run 30386125253). - # - # 120000 is deliberate: it is the HIGHEST value any package configures - # (apps/server/vite.config.ts pins 120s for sqlite+git+orchestration integration tests). The - # flag applies to every package, so a lower value would silently CUT apps/server's budget in - # half — the same failure class this override exists to prevent. At 120s no package is - # lowered: web 15s -> 120s, root 60s -> 120s, server 120s -> 120s. - # The job's own `timeout-minutes` remains the backstop against a genuinely hung test. - # - # `--hookTimeout` is raised for the same reason and is NOT covered by `--testTimeout`: a - # `beforeAll`/`beforeEach` gets its own budget. At the 2026-08-02 sync, - # `apps/web/src/components/chat/MessagesTimeline.test.tsx` (byte-identical to upstream, not a - # fork seam) timed out in a hook under full-suite parallel load while passing in 2.3s on its - # own — the same load-sensitivity class as the test timeout above, on hardware faster than the - # fork's 2-core runners. 120000 is chosen on the same "highest configured value" rule: - # web 15s -> 120s, root 60s -> 120s, server already 120s. - # Keep in sync with the `test` case in `scripts/coil/sync-upstream.sh`. - name: Test - if: ${{ !cancelled() }} - run: vp run test --testTimeout=120000 --hookTimeout=120000 + run: vp run --filter t3 test --shard ${{ matrix.shard }}/${{ strategy.job-total }} --testTimeout=120000 --hookTimeout=120000 From 48c28b09d88edb8aeabf5145069b0e4f491b4983 Mon Sep 17 00:00:00 2001 From: Raj D <25481060+radroid@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:14:59 -0400 Subject: [PATCH 2/2] ci(coil): run web tests alone in the single-runner sync verifies The daily sync and the resolver agent verify on one runner with a single `vp run test`, which hits the same MessagesTimeline hook timeout coil-ci did. Run the suite as two invocations, everything but web and then web alone, and give both jobs room for the server suite (~25 min serially). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/coil-sync-resolve.yml | 20 +++++++---- .github/workflows/coil-upstream-sync.yml | 5 +-- docs/coil/sync-agent-runbook.md | 26 +++++++++----- scripts/coil/sync-upstream.sh | 44 +++++++++++++++--------- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/.github/workflows/coil-sync-resolve.yml b/.github/workflows/coil-sync-resolve.yml index 26727dcbcdbc..5989ec786565 100644 --- a/.github/workflows/coil-sync-resolve.yml +++ b/.github/workflows/coil-sync-resolve.yml @@ -61,7 +61,9 @@ jobs: contains(github.event.issue.labels.*.name, 't3x-sync') ) && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) ) runs-on: ubuntu-latest - timeout-minutes: 45 + # The verify below runs the whole suite on this one runner; the server suite alone takes about + # 25 minutes serially since the 2026-10-08 sync (coil-ci.yml shards it six ways). + timeout-minutes: 90 steps: - name: Checkout fork main (full history) uses: actions/checkout@v6 @@ -138,12 +140,16 @@ jobs: changed the semantics of an API the fork hooks into. If upstream has absorbed a fork change, delete the fork's now-redundant version as a normal follow-up commit on this branch and say so — a merge never drops fork code for you. - 4. Verify: run `vp run typecheck`, `vp run lint`, then - `vp run test --testTimeout=120000`. The --testTimeout flag is required: apps/web - pins 15s, which is too tight for this fork's 2-core runners, and 120s matches the - fork's CI gate without lowering any package's own budget. Note the flag must NOT - come after a `--` separator or it is silently ignored. Fix the fork's code until - all three pass. + 4. Verify: run `vp run typecheck`, `vp run lint`, then the tests in two runs, web + last and alone: + `vp run --filter '!@t3tools/monorepo' --filter '!@t3tools/web' test --testTimeout=120000 --hookTimeout=120000` + then `vp run --filter @t3tools/web test --testTimeout=120000 --hookTimeout=120000`. + The timeout flags are required: apps/web pins 15s, which is too tight for this + runner, and 120s matches the fork's CI gate without lowering any package's own + budget. Web runs alone because a web test's in-file 30s hook budget times out + when it shares this runner with mobile and desktop. Note the flags must NOT come + after a `--` separator or they are silently ignored. Fix the fork's code until + all three steps pass. 5. Push the branch: `git push -u origin HEAD`. 6. Open a PR into `main` with `gh pr create`, title "coil: resolve upstream sync (run ${{ github.run_id }})". In the body summarise: the upstream range merged, diff --git a/.github/workflows/coil-upstream-sync.yml b/.github/workflows/coil-upstream-sync.yml index 205ce639c504..31b3ecdf6ff8 100644 --- a/.github/workflows/coil-upstream-sync.yml +++ b/.github/workflows/coil-upstream-sync.yml @@ -34,8 +34,9 @@ env: jobs: sync: runs-on: ubuntu-latest - # Merge plus the same verify as coil-ci.yml, which needs up to 40 minutes on its own. - timeout-minutes: 50 + # Merge plus coil-ci.yml's checks on this one runner; the server suite alone takes about 25 + # minutes serially since the 2026-10-08 sync (coil-ci.yml shards it six ways). + timeout-minutes: 60 steps: - name: Checkout fork main (full history) uses: actions/checkout@v6 diff --git a/docs/coil/sync-agent-runbook.md b/docs/coil/sync-agent-runbook.md index fb8bf5b69139..d6d4ce178d8b 100644 --- a/docs/coil/sync-agent-runbook.md +++ b/docs/coil/sync-agent-runbook.md @@ -36,9 +36,10 @@ The agent comments the PR link back on the issue when done. > gh workflow run "coil sync resolve (agent)" -R radroid/t3code -f issue= -f model=claude-opus-5 > ``` -**Check the budget before you pick a path.** The job is capped at `timeout-minutes: 45` and +**Check the budget before you pick a path.** The job is capped at `timeout-minutes: 90` and `--max-turns 150`. The one successful resolve to date absorbed a 36-commit upstream range against -37 fork commits and used 36m15s — 81% of the budget. A materially larger range will exhaust it, and +37 fork commits and used 36m15s, but that was before the 2026-10-08 sync, after which the test verify +alone takes about 30 minutes on one runner. A materially larger range will exhaust the budget, and a timeout mid-merge leaves an unpushed branch and a spent budget. Raising the caps needs a workflow edit; `workflow_dispatch` reads the workflow file from `--ref`, so you can carry raised limits on a scratch branch without merging it (the job still checks out `main` and merges upstream into it): @@ -113,8 +114,8 @@ upstream-inherited and nothing the fork did caused it. Delete the branch afterwa Fix such failures in `.github/workflows/coil-ci.yml` (fork-owned) rather than by patching the upstream file — patching adds a row to `docs/coil/SEAMS.md` and permanent sync cost for a CI-environment -problem. The `--testTimeout` override on the Test step exists for exactly this reason; its comment -records the case. +problem. The `--testTimeout`/`--hookTimeout` overrides and the separate web job exist for exactly +this reason; the comment above the test jobs records the cases. ## What the agent does (and what a human doing it locally should do) @@ -140,13 +141,20 @@ This is the checklist the workflow prompt mirrors — follow it if you resolve l really exists upstream, delete the fork's redundant copy as its own commit on the branch, and note it in the PR. (The old rebase-era "dropped patch" escalation is gone; nothing can vanish on its own any more.) -6. Verify: `vp run typecheck && vp run lint && vp run test --testTimeout=120000`. Fix the fork's - code to match upstream's new internals until green. +6. Verify: `vp run typecheck && vp run lint`, then the tests in two runs with web last and alone, + as `scripts/coil/sync-upstream.sh` does: + + ``` + vp run --filter '!@t3tools/monorepo' --filter '!@t3tools/web' test --testTimeout=120000 --hookTimeout=120000 + vp run --filter @t3tools/web test --testTimeout=120000 --hookTimeout=120000 + ``` + + Fix the fork's code to match upstream's new internals until green. Two notes. **`AGENTS.md` says "do not run repo-wide checks" — an upstream sync is the sanctioned exception.** A sync can break any package, so the full suite is the point; that rule is about - routine feature work. And the `--testTimeout` flag is required (see the section above); it must - not follow a `--` separator or it is silently ignored. + routine feature work. And the timeout flags are required (see the section above); they must + not follow a `--` separator or they are silently ignored. 7. **When green:** push the branch and open a PR into `main` (`gh pr create`). A human reviews and **lands it with a merge commit — see @@ -246,7 +254,7 @@ used to be the expensive one. the exact pre-sync tip if you want to diff against it or reset to it; if its push failed, the issue says `none`. - To re-gate locally before landing: - `git switch --detach origin/coil/sync- && vp run typecheck && vp run lint && vp run test`. + `git switch --detach origin/coil/sync-`, then the verify in step 6. ## One-time setup diff --git a/scripts/coil/sync-upstream.sh b/scripts/coil/sync-upstream.sh index 4e1590c09d9b..d1c3188589da 100755 --- a/scripts/coil/sync-upstream.sh +++ b/scripts/coil/sync-upstream.sh @@ -189,25 +189,37 @@ if [[ "$SKIP_VERIFY" == 1 ]]; then exit 0 fi -# Per-script extra args, so the daily verify matches the fork's CI gate exactly. `test` needs -# --testTimeout because apps/web pins 15s (tuned for upstream's blacksmith runners) and this fork -# runs on 2-core ubuntu-latest, where an upstream CPU-bound test reliably blows it. 120s is the -# highest any package configures (apps/server), so it never LOWERS a package's own budget. -# --hookTimeout is the same fix for the same reason and is NOT implied by --testTimeout: a -# beforeAll/beforeEach carries its own budget, and an upstream web test timed out in one under -# full-suite load at the 2026-08-02 sync while passing standalone. +# Each verify script is one `$RUN